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.
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!