Melody generator based on wave_generator project. Main target is generating sound for NRF52 via I2S amplifier but given a nature of library it should work as hardware independent in more generic projects. Please do not use it as dependency (it might change in breaking way) just copy or fork this library and adjust code as you need.

Example bad-coffin-dance.cpp

// no rising and falling edges of tones, for comparision with click-free version

#include <cstdio>
#include <melody.h>

#define SAMPLE_RATE 44100
#define SAMPLE_WIDTH 16

#define BUFF_SIZE SAMPLE_RATE * 28

#define PART_LENGTH 112
Note part[PART_LENGTH] = {
        {NOTE_A4, 250, 0},
        {NOTE_A4, 250, 0},
        {NOTE_A4, 250, 0},
        {NOTE_A4, 250, 0},
        {NOTE_A4, 250, 0},
        {NOTE_A4, 250, 0},
        {NOTE_A4, 250, 0},
        {NOTE_A4, 250, 0},
        {NOTE_AS4, 250, 0},
        {NOTE_AS4, 250, 0},
        {NOTE_AS4, 250, 0},
        {NOTE_AS4, 250, 0},
        {NOTE_AS4, 250, 0},
        {NOTE_AS4, 250, 0},
        {NOTE_AS4, 250, 0},
        {NOTE_AS4, 250, 0},
        {NOTE_AS4, 250, 0},
        {NOTE_AS4, 250, 0},
        {NOTE_AS4, 250, 0},
        {NOTE_AS4, 250, 0},
        {NOTE_D5,  250, 0},
        {NOTE_D5,  250, 0},
        {NOTE_D5,  250, 0},
        {NOTE_D5,  250, 0},
        {NOTE_C5,  250, 0},
        {NOTE_C5,  250, 0},
        {NOTE_C5,  250, 0},
        {NOTE_C5,  250, 0},
        {NOTE_F5,  250, 0},
        {NOTE_F5,  250, 0},
        {NOTE_F5,  250, 0},
        {NOTE_F5,  250, 0},
        {NOTE_G5,  250, 0},
        {NOTE_G5,  250, 0},
        {NOTE_G5,  250, 0},
        {NOTE_G5,  250, 0},
        {NOTE_G5,  250, 0},
        {NOTE_G5,  250, 0},
        {NOTE_G5,  250, 0},
        {NOTE_G5,  250, 0},
        {NOTE_G5,  250, 0},
        {NOTE_G5,  250, 0},
        {NOTE_G5,  250, 0},
        {NOTE_G5,  250, 0},
        {NOTE_C5,  250, 0},
        {NOTE_AS4, 250, 0},
        {NOTE_A4,  250, 0},
        {NOTE_F4,  250, 0},
        {NOTE_G4,  250, 0},
        {0,        250, 0},
        {NOTE_G4,  250, 0},
        {NOTE_D5,  250, 0},
        {NOTE_C5,  250, 0},
        {0,        250, 0},
        {NOTE_AS4, 250, 0},
        {0,        250, 0},
        {NOTE_A4,  250, 0},
        {0,        250, 0},
        {NOTE_A4,  250, 0},
        {NOTE_A4,  250, 0},
        {NOTE_C5,  250, 0},
        {0,        250, 0},
        {NOTE_AS4, 250, 0},
        {NOTE_A4,  250, 0},
        {NOTE_G4,  250, 0},
        {0,        250, 0},
        {NOTE_G4,  250, 0},
        {NOTE_AS5, 250, 0},
        {NOTE_A5,  250, 0},
        {NOTE_AS5, 250, 0},
        {NOTE_A5,  250, 0},
        {NOTE_AS5, 250, 0},
        {NOTE_G4,  250, 0},
        {0,        250, 0},
        {NOTE_G4,  250, 0},
        {NOTE_AS5, 250, 0},
        {NOTE_A5,  250, 0},
        {NOTE_AS5, 250, 0},
        {NOTE_A5,  250, 0},
        {NOTE_AS5, 250, 0},
        {NOTE_G4,  250, 0},
        {0,        250, 0},
        {NOTE_G4,  250, 0},
        {NOTE_D5,  250, 0},
        {NOTE_C5,  250, 0},
        {0,        250, 0},
        {NOTE_AS4, 250, 0},
        {0,        250, 0},
        {NOTE_A4,  250, 0},
        {0,        250, 0},
        {NOTE_A4,  250, 0},
        {NOTE_A4,  250, 0},
        {NOTE_C5,  250, 0},
        {0,        250, 0},
        {NOTE_AS4, 250, 0},
        {NOTE_A4,  250, 0},
        {NOTE_G4,  250, 0},
        {0,        250, 0},
        {NOTE_G4,  250, 0},
        {NOTE_AS5, 250, 0},
        {NOTE_A5,  250, 0},
        {NOTE_AS5, 250, 0},
        {NOTE_A5,  250, 0},
        {NOTE_AS5, 250, 0},
        {NOTE_G4,  250, 0},
        {0,        250, 0},
        {NOTE_G4,  250, 0},
        {NOTE_AS5, 250, 0},
        {NOTE_A5,  250, 0},
        {NOTE_AS5, 250, 0},
        {NOTE_A5,  250, 0},
        {NOTE_AS5, 250, 0}
};

int main(int argc, char **argv) {
    WaveGenerator waveGenerator(SAMPLE_RATE, SAMPLE_WIDTH, 0.5f);
    Melody melody(&waveGenerator, &part[0], PART_LENGTH);
    static uint32_t buff[BUFF_SIZE];

    uint32_t i = 0;
    while(melody.nextBuffer(&buff[i*4096], 4096)) {
        i++;
    }

    for (int z = 0; z < BUFF_SIZE; z++) {
        printf("%d\t%d\n", z, ((int16_t *) buff)[z * 2]);
    }

    if (argc > 1) {
       melody.wg->writeWav(&buff[0], BUFF_SIZE, argv[1]);
    }
}

Example click-free-coffin-dance.cpp

// click-free melody - smooth transition from/to zero with volume adjustment function

#include <cstdio>
#include <melody.h>

#define SAMPLE_RATE 44100
#define SAMPLE_WIDTH 16

#define BUFF_SIZE SAMPLE_RATE * 28

#define PART_LENGTH 112
Note part[PART_LENGTH] = {
        {NOTE_A4, 250, 3},
        {NOTE_A4, 250, 3},
        {NOTE_A4, 250, 3},
        {NOTE_A4, 250, 3},
        {NOTE_A4, 250, 3},
        {NOTE_A4, 250, 3},
        {NOTE_A4, 250, 3},
        {NOTE_A4, 250, 3},
        {NOTE_AS4, 250, 3},
        {NOTE_AS4, 250, 3},
        {NOTE_AS4, 250, 3},
        {NOTE_AS4, 250, 3},
        {NOTE_AS4, 250, 3},
        {NOTE_AS4, 250, 3},
        {NOTE_AS4, 250, 3},
        {NOTE_AS4, 250, 3},
        {NOTE_AS4, 250, 3},
        {NOTE_AS4, 250, 3},
        {NOTE_AS4, 250, 3},
        {NOTE_AS4, 250, 3},
        {NOTE_D5,  250, 3},
        {NOTE_D5,  250, 3},
        {NOTE_D5,  250, 3},
        {NOTE_D5,  250, 3},
        {NOTE_C5,  250, 3},
        {NOTE_C5,  250, 3},
        {NOTE_C5,  250, 3},
        {NOTE_C5,  250, 3},
        {NOTE_F5,  250, 3},
        {NOTE_F5,  250, 3},
        {NOTE_F5,  250, 3},
        {NOTE_F5,  250, 3},
        {NOTE_G5,  250, 3},
        {NOTE_G5,  250, 3},
        {NOTE_G5,  250, 3},
        {NOTE_G5,  250, 3},
        {NOTE_G5,  250, 3},
        {NOTE_G5,  250, 3},
        {NOTE_G5,  250, 3},
        {NOTE_G5,  250, 3},
        {NOTE_G5,  250, 3},
        {NOTE_G5,  250, 3},
        {NOTE_G5,  250, 3},
        {NOTE_G5,  250, 3},
        {NOTE_C5,  250, 3},
        {NOTE_AS4, 250, 3},
        {NOTE_A4,  250, 3},
        {NOTE_F4,  250, 3},
        {NOTE_G4,  250, 3},
        {0,        250, 3},
        {NOTE_G4,  250, 3},
        {NOTE_D5,  250, 3},
        {NOTE_C5,  250, 3},
        {0,        250, 3},
        {NOTE_AS4, 250, 3},
        {0,        250, 3},
        {NOTE_A4,  250, 3},
        {0,        250, 3},
        {NOTE_A4,  250, 3},
        {NOTE_A4,  250, 3},
        {NOTE_C5,  250, 3},
        {0,        250, 3},
        {NOTE_AS4, 250, 3},
        {NOTE_A4,  250, 3},
        {NOTE_G4,  250, 3},
        {0,        250, 3},
        {NOTE_G4,  250, 3},
        {NOTE_AS5, 250, 3},
        {NOTE_A5,  250, 3},
        {NOTE_AS5, 250, 3},
        {NOTE_A5,  250, 3},
        {NOTE_AS5, 250, 3},
        {NOTE_G4,  250, 3},
        {0,        250, 3},
        {NOTE_G4,  250, 3},
        {NOTE_AS5, 250, 3},
        {NOTE_A5,  250, 3},
        {NOTE_AS5, 250, 3},
        {NOTE_A5,  250, 3},
        {NOTE_AS5, 250, 3},
        {NOTE_G4,  250, 3},
        {0,        250, 3},
        {NOTE_G4,  250, 3},
        {NOTE_D5,  250, 3},
        {NOTE_C5,  250, 3},
        {0,        250, 3},
        {NOTE_AS4, 250, 3},
        {0,        250, 3},
        {NOTE_A4,  250, 3},
        {0,        250, 3},
        {NOTE_A4,  250, 3},
        {NOTE_A4,  250, 3},
        {NOTE_C5,  250, 3},
        {0,        250, 3},
        {NOTE_AS4, 250, 3},
        {NOTE_A4,  250, 3},
        {NOTE_G4,  250, 3},
        {0,        250, 3},
        {NOTE_G4,  250, 3},
        {NOTE_AS5, 250, 3},
        {NOTE_A5,  250, 3},
        {NOTE_AS5, 250, 3},
        {NOTE_A5,  250, 3},
        {NOTE_AS5, 250, 3},
        {NOTE_G4,  250, 3},
        {0,        250, 3},
        {NOTE_G4,  250, 3},
        {NOTE_AS5, 250, 3},
        {NOTE_A5,  250, 3},
        {NOTE_AS5, 250, 3},
        {NOTE_A5,  250, 3},
        {NOTE_AS5, 250}
};

int main(int argc, char **argv) {
    WaveGenerator waveGenerator(SAMPLE_RATE, SAMPLE_WIDTH, 0.5f);
    Melody melody(&waveGenerator, &part[0], PART_LENGTH);
    static uint32_t buff[BUFF_SIZE];

    uint32_t i = 0;
    while(melody.nextBuffer(&buff[i*4096], 4096)) {
        i++;
    }

    for (int z = 0; z < BUFF_SIZE; z++) {
        printf("%d\t%d\n", z, ((int16_t *) buff)[z * 2]);
    }

    if (argc > 1) {
       melody.wg->writeWav(&buff[0], BUFF_SIZE, argv[1]);
    }
}

Example continuous.cpp

// continuous melody driven by code outside

#include <cstdio>
#include <melody.h>

#define SAMPLE_RATE 44100
#define SAMPLE_WIDTH 16

#define BUFF_SIZE SAMPLE_RATE * 28

#define PART_LENGTH 1
Note part[PART_LENGTH] = {{NOTE_A4, 0, EDGE_RISING}};

int main(int argc, char **argv) {
    WaveGenerator waveGenerator(SAMPLE_RATE, SAMPLE_WIDTH, 0.5f);
    Melody melody(&waveGenerator, &part[0], PART_LENGTH);

    static uint32_t buff[BUFF_SIZE];

    uint32_t i = 0;
    while(melody.nextBuffer(&buff[i*4096], 4096) != 0) {
        if (i == 2 || i == 8) {
            melody.adjustFrequency(NOTE_B4);
        }
        if (i == 5) {
            melody.adjustFrequency(NOTE_A4);
        }
        if (i == 10) {
            melody.adjustFrequency(NOTE_C6);
        }
        if (i == 20) {
            melody.stop();
        }
        i++;
    }

    for (int z = 0; z < (i + 1)*4096; z++) {
        printf("%d\t%d\n", z, ((int16_t *) buff)[z * 2]);
    }

    if (argc > 1) {
       melody.wg->writeWav(&buff[0], BUFF_SIZE, argv[1]);
    }
}

Example super-mario-kick.cpp

// small buffer to test "cutting" through notes

#include <cstdio>
#include <cstring>
#include <melody.h>

#define SAMPLE_RATE 33333
#define SAMPLE_WIDTH 16

#define BUFF_SIZE 3333 // 100 ms
#define WAV_BUFFER_SIZE SAMPLE_RATE * 1 // 1 second1

#define PART_LENGTH 3
static Note startupMelody[PART_LENGTH] = {
    {500, 20, 0},
    {0, 80, 0},
    {693, 20, 0}
};

int main(int argc, char **argv) {
    WaveGenerator waveGenerator(SAMPLE_RATE, SAMPLE_WIDTH, 0.5f);
    Melody melody(&waveGenerator, &startupMelody[0], PART_LENGTH);
    static uint32_t buff[BUFF_SIZE];
    static uint32_t wav[WAV_BUFFER_SIZE];

    uint32_t i = 0;
    uint32_t generated = 0;
    while(generated = melody.nextBuffer(&buff[0], BUFF_SIZE)) {
        memcpy(&wav[i * BUFF_SIZE], &buff[0], generated * 4);
        i++;
    }

    for (int z = 0; z < WAV_BUFFER_SIZE; z++) {
        printf("%d\t%d\n", z, ((int16_t *) wav)[z * 2]);
    }

    if (argc > 1) {
       melody.wg->writeWav(&wav[0], WAV_BUFFER_SIZE, argv[1]);
    }
}