Szybkie przypomnienie, jakie są konsekwencje hierarchii klas. Jeśli klasa B dziedziczy po klasie A to obiekty klasy B będą posiadały pełny interfejs klasy A. Metody klasy B mogą co prawda przeciążać (przesłaniać) interfejs klasy A, lecz, jeśli tego nie zrobi, wywołujemy metody klasy A, czyli…
No właśnie – gdzie tu problem? Problem w tym, że w dokumentacji znajdziemy dwie informacje:
– z jakiej klasy dziedziczy dana klasa
– jaki jest jej interface
co oznacza, że nie widzimy pełnego interfejsu wraz z metodami dziedziczonymi. Może się zatem zdarzyć, że pracując np. z obiektem klasy ADSR wywołamy metodę klasy Envelope (z której ADRS dziedziczy) nie powodując w ten sposób żadnego ostrzeżenia ze strony kompilatora. No bo niby czemu miałby nas ostrzegać i przed czym?! No jak dla mnie przed źle zaimplementowaną hierarchią klas, w której metody dziedziczone źle działają z obiektami klas dziedziczących.
Dokładnie tak się to ma z funkcją duration klasy ADSR. Nie jest ona przeciążona w klasie ADSR, więc wywoływana jest jej implementacja z klasy Envelope. Szkoda tylko, że z obiektami klasy ADSR ona nie działa!
SinOsc s1 => Envelope e => Gain g => dac; SinOsc s2 => ADSR a => g; .5 => g.gain; // zaczynamy od ADSR, które nie działa najlepiej 1::second => a.duration; <<< "ADSR - poczatek:", now/second >>>; // uwaga - nie ma obwiedni - dźwięk wchodzi na ostro! a.keyOn(); a.duration() => now; <<< "ADSR - polowa:", now/second >>>; // odtąd nie ma dźwięku! a.keyOff(); a.duration() => now; <<< "ADSR - koniec:", now/second >>>; // zrobmy sekunde przerwy dla zlapania oddechu second => now; // a teraz Envelope, żeby zobaczyć, co to znaczy działa 1::second => e.duration; <<< "Envelope - poczatek:", now/second >>>; e.keyOn(); e.duration() => now; <<< "Envelope - polowa:", now/second >>>; e.keyOff(); e.duration() => now; <<< "Envelope - koniec:", now/second >>>;
Kompilator nie ostrzega, kod się kompiluje, dźwięk się tnie, szukaj człowieku błędu ;)