Simple LUT(lookup table) MCU friendly library for generating waves. 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 adjust-volume.cpp

// Click-free wave - smooth transition from/to zero with volume adjustment function

#include <cstdio>
#include <cstring>
#include <wave-generator.h>

#define SAMPLE_RATE 44100
#define SAMPLE_WIDTH 16

#define BUFF_SIZE SAMPLE_RATE / 4
#define TONE_A4 440
#define TONE_A6 1760

int main(int argc, char** argv)
{
    WaveGenerator wg(SAMPLE_RATE, SAMPLE_WIDTH, 1.0f);

    static uint32_t buff[BUFF_SIZE];                // output buffer
    memset(&buff[0], (uint32_t)0, BUFF_SIZE);  // we set midpoint 128 (silence) since 8bit PCM is encoded as unsigned

    wg.sineTransform(&buff[0], BUFF_SIZE, TONE_A4, TONE_A6);

    wg.adjustVolume(&buff[0], wg.ms32size(50), 0, 255);
    wg.adjustVolume(&buff[BUFF_SIZE - wg.ms32size(50)], wg.ms32size(50), 255, 0);

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

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

Example adjust-volume-32bit.cpp

// Click-free wave - smooth transition from/to zero with volume adjustment function, 32 bit sample width
// (Firefox can't play 32 bit WAVs)

#include <cstdio>
#include <cstring>
#include <wave-generator.h>

#define SAMPLE_RATE 48000
#define SAMPLE_WIDTH 32

#define BUFF_SIZE SAMPLE_RATE / 2
#define TONE_A4 440
#define TONE_A6 1760

int main(int argc, char** argv)
{
    WaveGenerator wg(SAMPLE_RATE, SAMPLE_WIDTH, 1.0f);

    static uint32_t buff[BUFF_SIZE];           // output buffer
    memset(&buff[0], (int32_t)0, BUFF_SIZE);  // we set midpoint 128 (silence) since 8bit PCM is encoded as unsigned

    wg.sineTransform(&buff[0], BUFF_SIZE, TONE_A4, TONE_A6);

    wg.adjustVolume(&buff[0], wg.ms32size(50), 0, 255);
    wg.adjustVolume(&buff[BUFF_SIZE - wg.ms32size(50)], wg.ms32size(50), 255, 0);

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

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

Example frequency-transform.cpp

// Smooth frequency change

#include <cstdio>
#include <cstring>
#include <wave-generator.h>

#define SAMPLE_RATE 44100
#define SAMPLE_WIDTH 16

#define BUFF_SIZE SAMPLE_RATE / 2
#define TONE_A4 440
#define TONE_A6 1760

int main(int argc, char** argv)
{
    WaveGenerator wg(SAMPLE_RATE, SAMPLE_WIDTH, 1.0f);

    static uint32_t buff[BUFF_SIZE];                // output buffer
    memset(&buff[0], (uint32_t)0, BUFF_SIZE);  // we set midpoint 128 (silence) since 8bit PCM is encoded as unsigned

    wg.sineTransform(&buff[0], BUFF_SIZE, TONE_A4, TONE_A6);

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

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

Example simple-tone.cpp

// Simple tone generation, since lib save phase to the state internally there should be uninterrupted wave

#include <cstdio>
#include <cstring>
#include <wave-generator.h>

#define SAMPLE_RATE 44100
#define SAMPLE_WIDTH 16

#define BUFF_SIZE SAMPLE_RATE // two channels
#define TONE_A 440

int main(int argc, char** argv)
{
    WaveGenerator wg(SAMPLE_RATE, SAMPLE_WIDTH, 1.0f);

    static uint32_t buff[BUFF_SIZE];
    memset(&buff[0], (uint16_t)0, BUFF_SIZE * 2);

    wg.sine(&buff[0], wg.ms32size(250), TONE_A);
    wg.sine(&buff[wg.ms32size(250)], wg.ms32size(250), TONE_A);
    wg.sine(&buff[wg.ms32size(500)], wg.ms32size(250), TONE_A);

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

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

Example super-mario-coin.cpp

// Generated Super Mario coin sound effect

#include <cstdio>
#include <cstring>
#include <wave-generator.h>

#define SAMPLE_RATE 44100
#define SAMPLE_WIDTH 16

#define BUFF_SIZE SAMPLE_RATE // our buffer is int32, so 16bit*2
#define ZERO_TRANSITION 100

int main(int argc, char** argv)
{
    WaveGenerator wg(SAMPLE_RATE, SAMPLE_WIDTH, 1.0f);

    static uint32_t buff[BUFF_SIZE];                // output buffer
    memset(&buff[0], (uint32_t)0, BUFF_SIZE);  // we set midpoint 128 (silence) since 8bit PCM is encoded as unsigned

    wg.square(&buff[0], wg.ms32size(100), 988);
    wg.square(&buff[wg.ms32size(100)], wg.ms32size(800), 1319);

    wg.adjustVolume(&buff[0], ZERO_TRANSITION, 0, 255);
    wg.adjustVolume(&buff[BUFF_SIZE / 10], wg.ms32size(800), 255, 0);

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

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

Example super-mario-kick.cpp

// Generated Super Mario kick sound effect

#include <cstdio>
#include <cstring>
#include <wave-generator.h>

#define SAMPLE_RATE 44100
#define SAMPLE_WIDTH 16

#define BUFF_SIZE SAMPLE_RATE

int main(int argc, char** argv)
{
    WaveGenerator wg(SAMPLE_RATE, SAMPLE_WIDTH, 1.0f);

    static uint32_t buff[BUFF_SIZE];                // output buffer
    memset(&buff[0], (uint32_t)0, BUFF_SIZE);  // we set midpoint 128 (silence) since 8bit PCM is encoded as unsigned

    int start = wg.ms32size(100);
    int slength = wg.ms32size(20); // 20ms
    int sdelay = wg.ms32size(80); // 80ms

    wg.square(&buff[start], slength, 500);
    wg.square(&buff[start + slength] + sdelay, slength, 693);

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

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