In the last post we generated a very simple note using a sine wave. Whilst some techno artists would disagree, it’s not very interesting having a tune consist of just a single note. So lets generate some different notes for our little tune.

So we know that to play an A, we can generate a sine wave at 440hz (repeating 440 times per second). Now, to generate an A# we need to play our sine wave at 466.16hz - 26.16hz faster than the A.

To play a B (one semitone higher than a A#) we need to play at 493.88hz. This is 27.71hz faster than A#.

The gaps between the notes are not linear!

# The formula

The formula for calculating the frequency of each note is:

$$f = 440 \cdot 2^{\frac{(note - 69)}{12}}$$

We can use the following rust code:

pub fn midi_note_to_freq(note: u32) -> f32 {
440 * 2.0f32.powf((note as f32 - 69.0) / 12.0)
}


The note parameter is the midi note number. According to the MIDI standard, middle A is 69, A# is 70, B 71 and so on.

# The tune

Then let’s update our main function to generate the tune.

As before:

fn main() {
let mut phase = 0.0;


We’ll play the following notes (B, G, A#, E):

    let notes = [71, 67, 70, 64];


Then we need to allocate enough capacity to generate half a second per note in our list:

    let mut sine = Vec::with_capacity(SAMPLE_RATE * notes.len() / 2);


Now we need to go through each of our notes, get the frequency we want to play and use this to update our phase delte.

    for note in notes {
let freq = midi_note_to_freq(note);
let phase_delta = freq / SAMPLE_RATE as f32;


Then we generate our sine as before:

        for _ in 0..SAMPLE_RATE / 2 {
sine.push((phase * std::f32::consts::TAU).sin());
phase = (phase + phase_delta) % 1.0;
}
}


The rest is the same as before, just saving that tune to a wav file:

    let mut out_file = File::create("tuneful_sine.wav").unwrap();
1,
SAMPLE_RATE as u32,
32,
);

wav::write(