Join GitHub today
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.
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:
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
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
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