2019/01/11

Keyboard synth!

This Monday I made some progress on the keyboard wiring, and realized that if I finished it in time, I could throw together a quick demo for Tuesday's monthly maker meetup in my area. The idea of the meetup is that people can give informal 5-minute presentations to the group about projects they're working on, ask for help with designs or repairs, talk about interesting goings-on, or just come and see what other people are up to. I've given a couple of presentations there in the past, but this time I had the time and found the motivation to scramble together a bit of a demonstration.

I didn't get the whole keyboard wired in time, but I was able to finish wiring five of the eight input modules, and I spent Monday night trying to figure out how to get a software synthesizer working so that I could get some sound out of the thing. I couldn't get it to work quite right, due to some control flow errors (read: silly mistakes in the script), but I was able to get a readout of button presses so that after the meeting, people could come up and play with the buttons and actually see something happening. It was basically a hackish console log output: I inserted a print() statement into the main loop that displayed the raw output from the input module shift registers.

As an aside, I really need to get around to writing a post about how the electronics actually work...

Before the meeting, I was unable to get the synthesizer to work properly, but it did sort of work; I didn't have time to properly think out the process from raw data input to actual key-press detection, so instead it just sent out a MIDI NoteEvent for every channel the script expected to have as input, every time the script looped. This produced an amusing and slightly obnoxious effect where instead of a single attack with a sustain, like you'd expect from pressing a piano key and holding it down, the synthesizer assumed that each time the script looped (several times per second, at least), the key had been pressed again, and happily did its best to replicate the sound of a piano key being hammered several times per second.

This was only one of the bugs I encountered, but it was the easiest one for me to understand and correct; all I needed to do was write some code to detect actual button presses (in other words, state changes) instead of doing the programming equivalent of brute-force key-mashing. Essentially what I did was compare the current value of each channel to the previous one, and wherever they didn't match, I sent a corresponding NoteEvent to the synthesizer (NoteOnEvent for newly-active channels and NoteOffEvent for newly-inactive ones).

The other bug I had to deal with was a bit more confusing and complicated to solve, because I had to think through every individual step of what the code was actually doing before I could understand why it was doing what it was. Essentially, the symptom was that when I played only one note at a time, the raw data from the input modules would show the correct function, but the note that played wasn't correct: instead of having eight different pitches on each module, every button in a module produced the same pitch. What was more confusing, at least at first, was that when I played more than one note at a time, two pitches would sound and would have the correct interval between them, but the lower pitch was always the same one, regardless of which button I pressed (within a module).

It took me longer than it should have taken to figure out what was going on, but the error was painfully obvious once I figured out what was happening. The problem was in the conversion from raw input to individual channels: for one, I was reading from the wrong end of the byte. The script started at the big end instead of the small end, so if it had worked correctly, each module would have had its channels reversed. In addition, I had neglected to consider that leading zeros are truncated automatically when converting to a binary number. These errors combined to cause a rather strange effect where any channel that was active appeared to be the first channel of the module, as far as the output was concerned. Once I rewrote the conversion, I was able to fix that bug as well.

Having resolved these issues, I've now got 5/8 of a functional keyboard synthesizer that I can experiment with. There's a bit of latency that's mildly frustrating to deal with when playing, but I expect that will be the case on the final product anyways, so I'm not sure there's much point in trying to eliminate it on the software side. The main purpose of the synthesizer was to put together the demo, so any further effort is just chasing a tangent.

No comments:

Post a Comment