Hi guys, I looking at reworking the mixer in a medium/large
C++ application. Any pointer/opinions/theories on how this
should be approached? (I know many of you have encountered
the problem.)
In particular, I'm thinking about having a central Mixer
object that the other parts of the application interface
with. This Mixer object would own, maintain, send, return,
and mix the buffers for everybody.
For those wanting/needing more details... read on.
APPLICATION
-----------
The application is Composite (
http://gabe.is-a-geek.org/composite/).
It is intended to have DAW-like attributes... but is more
along the lines of a sequencer. Mixing is currently
implemented directly in the process() callback and inside
the sampler.[1]
RATIONALE
---------
The Sampler currently has a pointer to a parent class Engine
so that it can have access to the output buffers (currently
tied to AudioOutput 'drivers'). I would like to make
Sampler a more self-contained class.
APPROACHES I'M CONSIDERING
--------------------------
A. Create an abstract Mixer class. This class will manage
the audio buffers, their connects, send/return, etc.
The sampler class would, for instance, request from the
Mixer a buffer allocation. On every process cycle, the
Sampler would get a fresh copy of the buffer pointer.
In this way, the Mixer could, if it wanted to, serve up
the exact same pointer that jack_get_buffer() returns.
B. Have Sampler own all its output buffers. Force other
applications to query or connect to them in order to do
mixing. An example of this approach would be the LV2 amp
example.[2] However, with this approach I'm concerned
that I won't be able to avoid buffer thrashing.
THINGS I'VE SEEN ELSEWHERE
--------------------------
Most applications I've looked at have a very de-centralized
approch. If you are the author of one of these -- forgive
me if I've failed to grok your code! :-)
* Ingen handles mixing as a feature of a "connection."
It also appears that gain has to be handled elsewhere
(like an amplifier insert.) However, in Composite
I'd like to avoid setting up an arb. connection
framework at this stage.
* Ardour appears to handle channel gain internal to
each channel/buffer object. The output mix-downs
are more or less handled directly in the process()
callback.[3]
* Non-daw appears to handle it similar to Ardour.
* All of them, at the core, implement the mixing as
some manner of basic function... like a specialized
memcpy().
* All of them implement SSE optimizations in mixing, or
at least have them on their TO-DO list.
Any thoughts or comments are appreciated!
Thanks,
Gabriel
[1] If digging in the code, it's roughly at:
src/Tritium/src/EnginePrivate.cpp:438
src/Tritium/src/Sampler.cpp:419
src/Tritium/src/Sampler.cpp:598
Current git revision cfebf2058...
[2]
http://lv2plug.in/plugins/Amp-example.lv2/
[3] See, e.g. AudioTrack::roll() in libs/ardour/audio_track.cc