-
-
Notifications
You must be signed in to change notification settings - Fork 2.6k
Sound programming design
At the moment in the code we have an ofMixer that accepts multiple sources, an ofSoundPlayer that just outputs and testApp that just outputs. For illustration I've added an ofToneGenerator that outputs a tone and ofSoundDelay that applies a delay effect. The mixer has as sources an ofSoundPlayer, an ofSoundDelay and a testApp. The ofSoundDelay takes input from the ofToneGenerator.
In pseudocode:
ofMixer mixer;
ofSoundPlayer musicPlayer;
mixer.addOutput(musicPlayer);
ofToneGenerator toneGenerator;
ofSoundDelay delay;
delay.setInput(toneGenerator);
mixer.addOutput(delay);
mixer.addOutput(testApp);
(solid arrowheads show inheritance, light arrowheads show pointers).
We have the following requirements:
- We want to be able to change the output of the ofSoundPlayer so that it passes through the ofSoundDelay.
- We want to achieve (1) by calling a function on the ofSoundPlayer.
In pseudocode, we want to be able to do this:
musicPlayer.sendGeneratedAudioTo(delay);
and have the musicPlayer send its audio to the delay rather than to the mixer.
Note in the diagram above, the ofSoundPlayer does not have a pointer to the ofMixer, therefore we can immediately say that (2) is impossible. As was pointed out here, to allow (2) we need a doubly linked structure: the ofMixer needs a pointer to the ofSoundPlayer and the ofSoundPlayer needs a pointer to the ofMixer.
What about (1)? Since the ofSoundDelay only has a single source pointer, if we tell it to take audio from the ofSoundPlayer we lose the input from the ofToneGenerator, which is probably not what we want to do. So the ofSoundDelay should also be able to keep a list of inputs and mix them down ... which looks like duplication of the code in ofMixer.
Here's my proposal for dealing with both of these problems:
We have added a base class ofSoundSink
which holds an array of pointers to sound sources. (A class should subclass ofSoundSink
when it intends to get sound data from another object using audioOut()
). To achieve (2) above we added a couple of features to ofBaseSoundOutput
so that it now holds an array of pointers back to ofSoundSink
instance. We have also renamed ofBaseSoundOutput
to ofBaseSoundSource
to better match the ofSoundSink
name. Now all of the classes that inherit from ofBaseSoundSource
get a sinks
array, and all of the classes that inherit from ofBaseSoundSink
get a sources
array.
With this structure, here's what our original scenario above looks like, after doing this pseudocode:
ofMixer mixer;
ofSoundPlayer musicPlayer;
mixer.addSource(musicPlayer);
ofToneGenerator toneGenerator;
ofSoundDelay delay;
delay.addSource(toneGenerator);
mixer.addSource(delay);
mixer.addSource(testApp);
The pairs of arrows are marked by = , they represent the doubly-linked structure mentioned above.
And here's what happens after we do (1), ie set the ofSoundPlayer to send its audio to the ofSoundDelay rather than straight to the mixer. Note that the ofSoundDelay now has two inputs, and that this was able to happen transparently since the methods to handle mixing down were already implemented on ofBaseSoundSink
. Note also that we are now able to achieve our requirement (2) from above because the ofSoundPlayer inherits from ofBaseSoundSource
and thus holds a pointer back to the ofMixer
.
Pseudocode:
musicPlayer.setSink(delay);