Thursday 14 August 2014

Bending it like Beckham

Pitch that is. And not a football one. The MIDI library I'm using makes it easy to respond to MIDI messages of all types with the use of callbacks. It's simply a matter of defining a new function and registering that to handle the message:

MIDI.setHandlePitchBend(handlePitchBend);


// A pitch-bend event has been received:
void handlePitchBend( byte channel, int bend )
{
  // Pass the pitchbend onto the oscillators
  OSC1.setPitchBend(bend);

}


The synth is so simple at this point it's hardly a priority but I couldn't resist trying it out. The question was then - how to bend the pitch? In the past I've worked with a MIDI-CV converter. When dealing with control voltages the bending is easy, since the exponential relationship between CV and pitch is handled by the hardware it's simply a matter of a linear interpolation between to get a voltage offset corresponding to the amount of bend.

In this application however I'm working directly in frequencies, or actually Timer periods but they behave like frequencies. There is a discontinuity in the values for each note since there are optimal pre-scalers for each note range as described here. That makes it a little tricky to interpolate between notes without some complicated code and special cases.

In any case when dealing with frequencies it's not a linear relationship. Each octave doubles in frequency. The MIDI standard suggests pitchbend is implemented as +/- two semitones therefore we need to bend by some ratio of 1/6th of an octave (since an octave contains 12 semitones). The code to do this looks like:

    // Implement pitch bend. The MIDI standard suggests that the pitch bend is +/- two 
    // semitones (or two MIDI notes). 
    // To figure out the bend factor we need to find out what multiplication factor
    // we need. This will be. Each octave doubles in frequency and there are tweleve
    // semi-tones per octave. Therefore we need a factor of
    // 2**(r*2/12) or 2**(r/6) where r is the ratio 
    ratio = fabs((float)m_pitch_bend/ MIDI_PITCHBEND_MAX);
    factor = pow(2,(ratio/6));
    if( m_pitch_bend < 0 ){
      freq = musical_freqs[m_note] * factor;
    } else {
      freq = musical_freqs[m_note] / factor;
    }
    OCR1A = freq;


Where m_pitch_bend is the value received from the MIDI library and MIDI_PITCHBEND_MAX is the largest absolute value we'll ever receive (also defined by the library). Once we know how far to bend than calculating a multiplicative factor is quite straight-forward and avoids the issue with discontinuities in the note data. This strategy may well come in useful when it comes to implementing other pitch manipulations in software such as vibrato.

But does this exponential scaling actually make a difference relative to simply scaling linearly? I did some experiments to see if that's the case. Here's scaling from Middle C to D with 50 linear steps:


And here's doing it with 50 exponential steps:


Tell the difference? I can't, but then I don't have a very advanced musical ear. In any case the exponential scaling is easier to implement and seems to work well.





No comments:

Post a Comment