Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save poetaster/d32827eeff4647f45fc470f72b3c326d to your computer and use it in GitHub Desktop.
Save poetaster/d32827eeff4647f45fc470f72b3c326d to your computer and use it in GitHub Desktop.
C++ MIDI Frequency V/oct #cc #audio #cpp #midi
/* https://onecompiler.com/cpp/3xhec63fj */
#include <cstdint>
#include <cmath>
/*
Examples for converting between note numbers, frequencies, and Volts/octave.
If you're unfamiliar with MIDI note numbers, they are integers that
correspond to semitones (half steps). For example, MIDI note 12 is
C1 (16.35 Hz) and MIDI note 24 (twelve half steps higher) is C2 (32.70 Hz).
See newt.phys.unsw.edu.au/jw/notes.html
*/
/*
This sets the MIDI note that corresponds to 0 volts. Typically, this is
either C0 (MIDI note 12) or C1 (MIDI note 24).
*/
static const uint32_t midi_note_at_zero_volts = 12;
static const float semitones_per_octave = 12.0f;
static const float volts_per_semitone = 1.0f / semitones_per_octave;
static const float a4_frequency = 440.0f;
static const uint32_t a4_midi_note = 69;
/*
Converts a MIDI note number to note frequency.
The relationship betweens notes and frequencies is exponential-
A4 (440 Hz) is twice the frequency of A3 (220 Hz). This uses
an exponential function to determine the frequency using
A4 (440 Hz, MIDI note 69) as the "center".
See en.wikipedia.org/wiki/MIDI_tuning_standard
*/
float midi_note_to_frequency(uint32_t midi_note) {
float semitones_away_from_a4 = (float)(midi_note) - (float)(a4_midi_note);
return powf(2.0f, semitones_away_from_a4 / semitones_per_octave) * a4_frequency;
}
/*
Converts a note frequency to the closest corresponding MIDI
note number.
This is the inverse of the previous function- keep in mind
that if the frequency falls inbetween two MIDI notes this
will discard the "remainder". This method could be extended
to return the remainder as the number of cents above or below
the MIDI note.
*/
uint32_t frequency_to_midi_note(float frequency) {
float note = 69.0f + logf(frequency / a4_frequency) / logf(2.0f) * semitones_per_octave;
return ceil(note);
}
/*
Converts a MIDI note number to Volts/octave.
Since the note number represents the number of semitones, there is a
simple linear relationship between note number and Volts/octave. The
note number is multiplied by the number of Volts per semitone
(1/12th of a Volt, or about 83.3 mV) to obtain the corresponding
voltage.
*/
float midi_note_to_volts(uint32_t midi_note) {
if( midi_note <= midi_note_at_zero_volts ) {
midi_note = 0;
} else {
midi_note -= midi_note_at_zero_volts;
}
return midi_note * volts_per_semitone;
}
/*
Converts Volts/octave to its corresponding MIDI note number.
Similar to the frequncy to MIDI note number conversion, this
only returns the nearest MIDI note, so any remainder will be
discarded.
*/
uint32_t volts_to_midi_note(float volts) {
return ceil(midi_note_at_zero_volts + ceil(volts * semitones_per_octave));
}
/*
Converts Volts/octave to frequency.
Just like when converting MIDI notes to frequency, there is an
exponential relationship between Volts/octave and note frequency.
Helpfully, the same formula used there can be used here.
*/
float volts_to_frequency(float volts) {
float semitones = volts * semitones_per_octave;
float adjusted_semitones = semitones + midi_note_at_zero_volts;
float semitones_away_from_a4 = adjusted_semitones - (float)(a4_midi_note);
return powf(2.0f, semitones_away_from_a4 / semitones_per_octave) * a4_frequency;
}
/*
Converts a note frequency to Volts/octave.
Just like when converting from frequency to MIDI note, this is
the inverse of the preceeding function. The formula for
reversing frequency to MIDI note can be largely reused here.
*/
float frequency_to_volts(float frequency) {
float semitones = (float)(a4_midi_note) + logf(frequency / a4_frequency) / logf(2.0f) * semitones_per_octave;
float adjusted_semitones = semitones - midi_note_at_zero_volts;
return adjusted_semitones * volts_per_semitone;
}
/*
This main function is just here to demonstrate these functions,
you wouldn't include this in your Arduino sketch.
*/
#include <cstdio>
int main() {
printf("MIDI note -> V/oct & frequency:\n");
for( uint32_t note = 12; note < 73; note++) {
float frequency = midi_note_to_frequency(note);
float volts = midi_note_to_volts(note);
printf("MIDI note: %u, volts: %0.2f, frequency: %0.2f\n", note, volts, frequency);
}
printf("V/oct -> MIDI note & frequency:\n");
for( float voct = 0.0f; voct <= 5.0f; voct += volts_per_semitone) {
uint32_t note = volts_to_midi_note(voct);
float frequency = volts_to_frequency(voct);
printf("Volts: %0.2f, MIDI note: %u, frequency: %0.2f\n", voct, note, frequency);
}
printf("frequency -> MIDI note & V/oct:\n");
for( uint32_t input_note = 12; input_note < 73; input_note++) {
float frequency = midi_note_to_frequency(input_note);
float voct = frequency_to_volts(frequency);
uint32_t note = frequency_to_midi_note(frequency);
printf("frequency: %0.2f, MIDI note: %u, Volts: %0.2f\n", frequency, note, voct);
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment