This weekend I spent quite a bit of time working on re-writing my delay table generator.
The 8 bit VCDO generates an audio signal by spitting out each sample of a waveform, one at a time. I do not use a timer on the chip to achieve this (maybe I should have). Instead, I manually track how many processor cycles take place every time I go through the main loop. Depending on what pitch I am trying to achieve, I delay a specified number of processor cycles (fetched from a lookup table) before moving on with the loop.
Since the VCDO has a range of 5 octaves, and the base waveform has 256 samples, as the pitch goes up, there are physically not enough processor cycles to be able to continue to do this. At this point, instead of using every single sample in the table, I start skipping one sample in the table per run through the main loop, then skipping three, four, five, six, etc.
Today's post is focused on the delay table.
Very early on in this process (uh, back in the mists of time), I wrote a delay table generator program. Given the number of octaves, the number of sample steps, etc, it would spit out a delay table (and a step table, remember that at the right points I have to start skipping steps so I need a lookup for those as well).
Because there are only 256 possible voltages for the full 5 volts of input range, I had to do some massaging around semitones, I wanted semitones to be in tune, but the stuff inbetween was fine if it was not. Well then there's this whole mess around do you have 4 steps between semitones or 5, because 60 semitones (5 octaves, 1 volt per octave) is not a divisor of 256. The delay table generator takes all of this into account. I wrote it in a furious fit of staying up super late in an 8-9 hour binge and by the time it was done, it was quite the mess. "But there, that's settled" I thought to myself and happily moved on.
Well now it's a decade later. My ancient version of Visual Studio no longer runs happily in Windows 10. The community version of Visual Studio refuses to load programs off of clay tablets unless I pay Microsoft some money for a full license. I am hazy on the licensing details because I started trying to modify the delay table last September, but the full story is I said "screw it" and I started porting it to Python. I made some progress but it was rather tedious because the code was rather.... unique in terms of structure and naming conventions, as one would expect if one knew that it was completed at 3;30am in quite a state of sleep deprivation.
The Python port started back in September.
I dusted it off again in April and started poking away at it. First step was to try to figure out what the heck was going on. In September I was taking a rather literal approach to the translation, and I was still relatively new to Python. So I wasn't even trying to understand what I wrote in C#, just trying to take each line one at a time and move it over.
Now I know a bit more about Python. I quickly translated the rest of the C# to python, and started trying to understand what was going on. It was slow going, and it quickly this turned into debugging, because I found quite a few bugs. Based on the print statements I put in, I don't know if these are existing bugs from the beginning of time or if they are new bugs that I put in when I did the translation to Python.
In April, I had a vague idea of what is going on. I identified many of the bugs. I wrote (but did not finish) this blog post.
Over two months later, over this past weekend, at the end of June, I looked at it again and despaired. Because I had totally forgotten about this blog post. I also didn't leave myself any documentation about where I was at with it, or what I was in the middle of doing in any way. All I could remember is I did a bunch of refactoring right before I stopped working on it.
After much peering and poking and prodding, I determined that I had indeed finished it, and fixed all known bugs. All I had left to do was take the results and iterate through them to put them in a format that I can easily input into my assembly language source file. I got that finished.
Then, I found this nearly finished post in my drafts folder! Sure would have been easier if I would have finished the post at the time and uploaded it... ah well. I did some light editing of the text above so it all hopefully makes a bit more sense, and here we are.
Next step is a source code audit for the whole synth engine to ensure that the cycle counts are accurate. This is 98 percent complete, in terms of lines, and 75 percent complete, in terms of difficulty. (That is to say, the pieces remaining are not straightforward and there are many branches. If I can trim a few cycles off this one bit, I may be able to trim a few cycles off the main loop, which will be nice. (The fewer cycles the main loop runs at, the smoother it is in the upper registers.)
Once I have accurate cycle counts for the whole shebang, I can plug those into the delay table generator so I have accurate delay information. Then finally, I can do an end-to-end test, where I plug in the delay table, and then test the waveforms coming out of the synthesizer engine to verify that they are accurate and I didn't mess anything up.