STM32F4-Discovery MIDI input and basic synthesis

After the flurry of activities  in recent weeks on a previous post (thanks guys!) I finally went back and worked on the synth project some more. Some time ago I had already created a simple MIDI input and output circuit on a stripboard that the Discovery board could sit on. This would allow me to play notes or adjust parameters from any standard MIDI controller or the PC. The hardware setup for this was quite simple using a 6N137 optocoupler, which gets its 5V supply voltage directly from the 5V rail of the Discovery board. Since the output of the optocoupler is just an open drain, I didn’t even have to worry about any level shifting and directly connected it to the USART2 RX input on pin PA3, which I configured with an internal pull-up. (I have yet to test the output side, which is coming from USART2 TX on PA2 and goes to an external MOSFET.)

The serial module setup again was very simple thanks to the Standard Peripheral Library: configure the pin(s), turn on the peripheral clock, create the init structure with the appropriate settings, initialize, done!

USART_InitTypeDef usartis;

usartis.USART_BaudRate = 31250;
usartis.USART_WordLength = USART_WordLength_8b;
usartis.USART_StopBits = USART_StopBits_1;
usartis.USART_Parity = USART_Parity_No;
usartis.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
usartis.USART_Mode = USART_Mode_Rx;

This time I used the receiver interrupt to handle incoming MIDI messages, so I had to use the Nested Vector Interrupt Controller. Since this is currently the only interrupt in the system I set it to the lowest priority, but more on this later.

NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);

Since most MIDI messages consist of three bytes, a control byte followed by two data bytes, all the interrupt handler does is populate a global MIDI message structure with the incoming bytes and sets a flag once a complete message is received. This flag then gets picked up in the main loop, and when set, the MIDI message structure is passed to a function to be decoded.

I originally tested this routine with my old code, changing the frequency of the oscillator and its amplitude in response to “Note On” and “Note Off” messages. One slight hurdle here was the conversion from MIDI note number (i.e. the key being pressed) to the frequency for that note, since this is a non-linear relationship. I decided against a simple  table lookup for this (though I may end up using it after all), and coded a quick algorithm that compared the incoming MIDI note number to note 69, the middle A4 (440Hz), then coarsely jumps octaves (each time multiplying the frequency by 2.0 or 0.5), and then finely multiplies the frequency by 2^(1/12) or 2^(-1/12) to get to the desired note.

float noteNumToFreq(u8 noteNumber) {
	s8 offset = noteNumber - 69;  //difference from A4 (440Hz, MIDI-note 69)
	float freq = 440.0f;

	if (offset > 0) {
		while (offset > 11) {
			offset -= 12;
			freq *= 2.0f;
		}
		while (offset > 0) {
			offset--;
			freq *= 1.059463f;  // 2^(1/12)
		}
	} else if (offset < 0) {
		while (offset < -11) {
			offset += 12;
			freq *= 0.5f;
		}
		while (offset < 0) {
			offset++;
			freq *= 0.9438743f; // 2^(-1/12)
		}
	}
	return freq;
}

While this is certainly a relatively quick method (which could even easily be adapted to fixed-point arithmetic), it still takes a few cycles to compute on average despite the hardware FPU. A table look-up is obviously a lot quicker, and since only a maximum of 128 entries are needed (of which the extremes can probably be eliminated) it would also not take up much room. However, right now this method works fine.

Next I wanted to improve the oscillator a bit. Originally I had a simple accumulator, which wrapped around to -1.0 once it exceeded +1.0, and for which I adjusted the step-by-step increment value to change the speed with which this accumulator would wrap (i.e. its frequency). The resulting waveform was then filtered with a very simple and crude Finite-Impulse-Response (FIR) filter to smooth the transitions slightly. The reason for the filtering was to avoid aliasing at high frequencies which can apparently even happen with directly synthesized signals (not just sampled signals). In the DSP field the desired oscillator is said to be “band-limited” and there a quite a few algorithms out there that aim to produce more or less band-limited oscillators.

I found a great paper by Jussi Pekonen and others that explained how to model the Moog sawtooth oscillator using a “discrete-time” (i.e. digital) system. The paper is well written and the math is still relatively simple and easy to understand, so I decided to implement their method. The algorithm works by phase-distorting a simple sine wave, but adjusting the amount of the distortion based on the desired fundamental frequency (less distortion with higher frequencies). This also reduces the higher harmonics, which could lead to aliasing, for higher frequencies of the fundamental. The single, frequency-dependent distortion parameter P can be calculated with a very simple linear equation, so is very quick to compute. The only slight downside of this algorithm is that it has a trigonometric function (sine or cosine)  at its core. However, another quick search turned up this page, which shows how you can simultaneously compute sine and cosine by approximating it with a parabola (quadratic function), only needing very few multiplies and additions. Of course this is not a perfect sine/cosine function, but for the purpose of the oscillator it should be more than adequate.

I implemented this algorithm, and it sounds quite good. Here is a screen shot of the resulting wave form (recorded with Audacity):

output waveform from discrete Moog saw wave algorithm (C2, 64Hz)
output waveform from discrete Moog saw wave algorithm (C2, 64Hz)

As you can see, the waveform looks more like the chopped middle 2 quarters of a sine wave, but apparently that’s what the Moog saw looked like as well. The little insert shows the smooth transition from the bottom to the top of the curve. This smoothing becomes stronger with higher frequencies, thereby reducing the harmonics in the signal. This, I understand, makes this oscillator more “band-limited”.

The last thing I implemented so far is an ADSR envelope generator for the amplification stage. For the attack, decay and release stages I again used parabolic shapes to derive the values. Here is a screen shot showing a note played with approx. 200 ms for these three stages:

ADSR amplitude modulation
waveform showing an ADSR-envelope modulation of the amplitude

Now that some basic MIDI control and sound synthesis is established, I can start expanding the synthesis and control capabilities. However, a few things will need to be fundamentally changed first:

  • The main sound synthesis and transmission to the codec is still handled in the main loop, which gets interrupted by incoming MIDI messages. This is fine as long there are only few MIDI messages to deal with, but with frequent MIDI input (e.g. from a modulation wheel), this starts pausing the sound. DMA both for MIDI and sound buffering should help here.
  • I had foolishly assumed that, since the processor has a hardware floating-point divide instruction, I could liberally use divisions in my code. I since found out that a divide takes 14 clock cycles (whereas a multiply takes only one). I should therefore try to eliminate divides as much as possible.
  • Gracefully handling key presses is hard! Even though the synthesis is monophonic (and will probably remain that way for a long time), I still have to deal with multiple keys being pressed at potentially the same time. I have a basic note manager working (e.g. preventing a switch to the release stage if there is still another key pressed), but I’m not too happy with it. This will also impact the envelope generator, but this should be easier to deal with.

 

10 thoughts on “STM32F4-Discovery MIDI input and basic synthesis

  1. Hi Andreas. Thankyou very much for this code. I have had an STM32f4 discovery board lying around for over a year now. I have been eyeing it but being a little put off by it’s steep learning curve compared to Arduino and other 8 bitters, it has remained unpowered. BUT now after picking it up again and thinking “well it can’t be THAT hard..” I found your great tutorial on generating a wave and putting it through the on-board Codec ( one of the main reasons for getting the board in the first place) I had a flash of insight into how it works!! Very impressed with the pace and layout of your tutorial and successfully compiled and ran it using COOCOX which IMHO is the best IDE for the stm32… so Ive not only got your code to work but I have also managed to write some code for the ADC so I can change the waves frequency with a pot connected to PC0! 🙂
    Is there any way you could post me your main.c file on the programming synthesis and MIDI page so I can have a look at that too???
    Thanks again & hope you do some more tutorials on this board again. (soon.)
    All the best,
    Steve.

    Like

  2. Thank you for your kind comment. Good job on the ADC – that’s another quick way of changing the synthesis parameters. I played around with the ADC a bit after I got the LCD display working (in my DMA post). It seemed like another example how the complexity of a seemingly simple peripheral device like an ADC can go up in a “beefier” microcontroller (e.g. regular vs. injected channels).

    I’ll send the code to the email you provided with your comment. As you’ll notice it has changed quite a bit from the original.

    Like

    1. Hi Andreas,

      First, thank you for your tutorials and tackling the complex subject of sound generation on the F4 Discovery board.
      At this price point, the discovery board hardware seems ideal for tinkering with sound, but learning curve is steep for the beginner in ARM coding. I found your article above very educational. I’d be so grateful if you could perhaps share the code for your midi/synthesis project on the F4 discovery. I’ve been looking long and hard, and came up with nothing, until I stumbled upon your tutorials. I use CooCox ide as well.
      Thanks in advance !

      Leo

      Like

      1. Hello Leo,

        Thanks for your comment. I’m planning on working on the code some more over the upcoming holidays. This will give me a chance to clean things up a bit and add some comments here and there. Once that’s done, I’ll probably put the whole project on github or something similar for others to download, comment, and maybe even contribute. I’ll post a link here of course, once that’s done.

        Andreas

        Like

  3. Your tutorials are amazing, Andreas. I’m a newbie with these boards and your explanations have helped me a lot in understanding how to use it. I’m looking forward to when you post your code, as I’m sure it will help a novice lime even more.
    Keep on the great work!

    Like

  4. Hey hey 🙂
    I’m also working on a little synth project and want to understand midi ….

    can you share the code with me?

    thanks 🙂

    Like

Comments are closed.

Design a site like this with WordPress.com
Get started