Monday, September 28, 2009

Object-oriented audio systems

The first assignment for 256a (Music, Computing and Design I) was a doozie.  It involved creating a signal generator application with a handful of features like waveform selection and pulse width modulation.  My submission is here: http://ccrma.stanford.edu/~adam/courses/256a/hw1/

It didn't have to be a doozie, but once you get used to generic programming there's no going back, so I kind of went overboard.  The code can speak for itself; this post is about some issues that left me scratching my head a little.

The Flow of Callbacks

In my system I created a singleton entity that acts as a "Server," mediating the connections between audio processing objects (clients) and the DAC.  Clients can be registered with the server, and registered clients will get callbacks when the server gets its callback from the driver (the "driver" is a wrapper around some nice api like RtAudio, PortAudio, Juce, etc).  Clients connected to the dac can be processed and the their resulting outputs summed.  But what if clients are not connected to the dac, but connected to each other?  This is an obvious feature of modular audio environments (both analog and digital), but there is a  question of how best to represent these "connections" in software.  The way I decided to try was to design any classes that require input to hold a pointer to an abstract client.  Source objects can be registered with these, and their callbacks will be called by the client taking the input.  In this way, the callbacks "flow" in a depth-first traversal of loosely coupled clients in a graph, with the Server/DAC as the origin.  The question that remains is: is this optimal?  I've considered another way of dealing with processing, such as having the server process all objects, relying on a connection graph to handle dependencies (inputs to other clients).  I haven't really thought that one out, so maybe it's senseless.  But I'm not convinced at the obvious way I came up with is optimal.  And so I scratch my head...

Time


I ran into a bug when I connected a client to the dac more than once (for multi-channel output).  The bug was that the client would render new material more than once per buffer.  Ge explained to me that the notion of time is useful in determining whether a client should produce new audio or just return a copy of old material.  This hint was hugely insightful, and the problem went away.  However, now I'm scratching my head over the usage of the word time.  When the a client is asked for audio some time after it has already done so, it needs to know if time has advanced for the server, as well.  If not, then no new audio needs to be generated.  So in this sense, time is a perfectly good description of what needs to be considered.  But the word 'time' will inevitably come up again soon when I implement some kind of event scheduling system, and I know this has to be tied in to the server in much the same way.  The question now is should I create some kind of "time keeper" that manages time?  I need to draft some specs for the next phase of this project before I can know anything else...

No comments:

Post a Comment