Mike Wilson's Blog


I was a MA/MST student at Stanford's Center for Computer Research in Music and Accoustics.

This is my weblog.

Click here for my main page.



2012-04-20

More work on Wilsynth.

I've been trying to get band-limited, variable duty-cycle waveforms
into the synthesizer.  Initially I generated tables of band-limited
square, triangle, and sawtooth waves with 2^12 steps from 1 to 1024
harmonics (over about 1000 harmonics the harmonics end up about 100 dB
down from the highest peak so I figure that's enough) using simple
additive synthesis based on their Fourier series.  I learned from
Julius' LAC talk that you can subtract a shifted sawtooth from another
sawtooth to get variable duty-cycle pulse waves, so that's well taken
care of (there is some tricky stuff with when to switch the width when
it's changed in real-time but I think what I have is workable).

Leaning triangle waves are a bit trickier.  You can integrate a
variable-width pulse wave with a digital integrator (sum of current
sample and previous sample) and then work out the scaling and offset,
which I did, and it works pretty well.  Until you change the pulse
width.  Since it relies on previous state you can end up with a lot of
problems, such as wild DC offsets, inversions, etc.  What I ended up
doing is resetting the phase and state to 0 every time the width is
changed, but only at the beginning of a cycle (my sawtooths ramp from
-1 to 1 with -1 being at the beginning of the cycle, although they're
band-limited so the discontinuity is really a steep downward slope).
It's a kludge, and results in some audible zippering when the width is
changed in real-time, especially on the higher notes.  But I tried
many things and need to take a break from working on this.  I feel
that there is a much better way to handle this than what I am doing,
but I haven't written out the right equations yet.

I also ran into some more multi-threading problems.  Loading patches
can be slow since it involves memory allocation, and I was trying to
use semaphores in the wrong way to handle this.  What I am doing now
is extremely simple: I have a single volatile integer which functions
as a counter.  The audio callback increments this counter every time
it runs (overflowing and wrapping around as necessary).  I also have
an array of boolean variables to signify if each voice channel is
ready to receive data.  When an asynchronous program select call comes
in, first the flag for that channel is set to false, then the value of
the volatile variable is recorded.  Then the program simply spinlocks
on a comparison between the stored value and the current value of the
variable, which is ONLY modified by the audio callback.  The audio
callback runs pretty often so it shouldn't have to spinlock long.  The
program change then runs and the boolean variable is set back to true.
Crude, inefficient, but safe... or is it?

It turns out that it's not quite safe.  If you get a lot of program
change requests for the same channel then they could both potentially
run at the same time resulting in all sorts of terrible undefined
behavior such as double-allocation, double-free, setting the flag to
true when the memory is not allocated, etc.  But since I have control
over when the program requests are sent I will simply discipline
myself for the time being to not send them too fast.

I need to learn more about synchronization methods such as lock-free
and wait-free queues.  Something like that might be more appropriate.
But with Modulations looming on the horizon I have to take some
shortcuts in interests of getting something stable for the
performance, if not in general.  None of the pieces I have planned
require quickly switching between patches, although that might be an
interesting experiment someday!


email mwilson@alumni.caltech.edu
Disclaimer: the views herein are my own and do not represent the views of Stanford University. All material copyright Michael J. Wilson.