Paula is the name of the Amiga computer's sound chip, supporting 4 channels of 8 bit samples. I set myself the task of simulating its output in the dataflow and DSP programming environment Pure-data. So without further ado here is the screen-grab of the Pd patch that models each channel:
On the left are the inputs to the patch, the [inlet] objects. They appear as rectangles on the top of the box when you instantiate the patch, providing points where wires can be connected to. They are positioned from left to right in the order they appear on the patch instance.
The inputs are:
Any of the inputs can be changed at any time, for example you could change the loop length while a sound is being played, or set offset to 0 to jump back to the start of the waveform. Sending a bang to any of the inputs sends the current value to an output, except for the waveform input. I haven't worked out a way to get that to work with symbols rather than numbers.
Below the inlets and their connected variables are some objects that specify how many samples are processed in each DSP cycle. This sets the time granularity of the control signals affecting the sound. A bug in Pd means the minimum block size is 64, even though for my purposes I wanted a size of 1 sample, which would mean control signals get handled after every sample point.
To simulate a block size of 1 sample I used a few tricks. At the top is a loop that counts to blocksize. This is used as an index into an array of blocksize samples, which the main code fills in [tabwrite] as it is triggered blocksize times. Once the array is filled, a [tabreceive~] object copies the data to the signal output.
The main part of the code increases the offset by an appropriate amount according to the period and output sample rate, before a loop subtracts the loop length until the offset is before the loop end point (which is loop start + loop length). It took a fair bit of painful debugging to get this loop correct, the mistake I failed to spot initially was a missing wire from the [-] to the [float], which led to an infinite loop in Pd which had to be Ctrl-C'd out of, thus losing any unsaved changes.
Now on to the patch that routes commands to the 4 channels:
This is fairly straightforward - say a message like (2 period 300) arrives at the inlet at the top left. [route] looks at the first element of the list, and if it matches any of the arguments, sends the remainder of the list to the appropriate output, in this case the second-from-top big [route] object. This looks at the first element of its input, and routes the rest accordingly, to the appropriate input of the appropriate [paulachannel~] object. Any non-matching inputs are printed out to indicate the message hasn't been understood.
So finally, a patch that makes use of the [paula~] abstraction:
The [loadbang] objects trigger activity when the patch is loaded. On the top right a loop writes a sawtooth waveform to an array, ready for playback by [paula~]. The big message box sends a sequence of messages to the [paula~] object, initialising its settings. Note that you have to have a set in there when setting the waveform, I haven't worked out how to build messages out of symbols yet.
Then the outputs of Paula are mixed and output to the soundcard, and a button writes it to an array so you can see the waveform.
This is my first completed Pd project, it works as specified. It would be a lot nicer if Pd supported [block~ 1], hopefully a future version will. And there is a slight issue - if the loop length on any of the channels is <= 0 then Pd goes into an infinite loop with the subtracting and the not getting any smaller. I think adding a [loadbang] in paulachannel.pd should fix this, setting the loop size to 2 (the smallest sensible value).
I recorded a small snippet of the sound generated by paulamain.pd, you can download the WAV file here (right-click, save-as, load into your music player). And you can download a zip archive of the Pd patches here.