commit d8002fe6fa4a6993362f8c131d22fb3036c8df0e Author: Adrian Scripca Date: Wed Apr 20 15:04:06 2022 +0300 First PPM experiment diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..6bbf48c --- /dev/null +++ b/Makefile @@ -0,0 +1,4 @@ +all: main + ./main +main: main.cpp sound_server.h sound_server.cpp Makefile + g++ -std=c++11 -o main main.cpp sound_server.cpp `sdl-config --cflags --libs` diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..92344ab --- /dev/null +++ b/main.cpp @@ -0,0 +1,74 @@ +#include +#include +#include "sound_server.h" + +// Naive example of 1-bit audio mixing employing PPM technique +// Adrian Scripca + +struct oscillator { + void init(uint16_t sr) { samplerate = sr; } + + void set_frequency(uint16_t frequency) { + samples_per_cycle = samplerate / frequency; + // phase = 0; // hard sync + } + + bool tick() { + phase = (phase + 1) % samples_per_cycle; + return phase < (samples_per_cycle / 2 * duty_cycle / 100); + } + + uint16_t samples_per_cycle; + uint16_t duty_cycle = 4; // percentage + uint32_t phase = 0; + uint16_t samplerate; +}; + +struct synth : sound_producer_1bit { + explicit synth(uint16_t samplerate) { + for (auto& o : oscs) { + o.init(samplerate); + } + } + + bool tick() override { + return oscs[0].tick() | oscs[1].tick() | oscs[2].tick() | oscs[3].tick(); + } + + void major_chord(float root) { + oscs[0].set_frequency(root); + oscs[1].set_frequency(root * 5.0f / 4.0f); // major third + oscs[2].set_frequency(root * 3.0f / 2.0f); // perfect fifth + oscs[3].set_frequency(root * 2.0f); // octave + } + + void minor_chord(float root) { + oscs[0].set_frequency(root); + oscs[1].set_frequency(root * 6.0f / 5.0f); // minor third + oscs[2].set_frequency(root * 3.0f / 2.0f); // perfect fifth + oscs[3].set_frequency(root * 2.0f); // octave + } + + oscillator oscs[4]; +}; + +int main(int argc, char* argv[]) { + std::cout << "1bit PPM technique example" << std::endl; + // setup + const uint16_t SampleRate = 44100; + + synth instrument{SampleRate}; + sound_server_1bit sound_server{SampleRate, instrument}; + + // fool around. Gmaj, Cmaj, Emaj (la bamba) + instrument.major_chord(98); + SDL_Delay(1000); + + instrument.major_chord(130.81); + SDL_Delay(1000); + + instrument.major_chord(146.83); + SDL_Delay(1000); + + return 0; +} \ No newline at end of file diff --git a/sound_server.cpp b/sound_server.cpp new file mode 100644 index 0000000..f67962a --- /dev/null +++ b/sound_server.cpp @@ -0,0 +1,37 @@ +#include "sound_server.h" + +#include + +#include + +void audio_callback(void* param, uint8_t* stream, int len) { + sound_producer_1bit* producer = reinterpret_cast(param); + for (int i = 0; i < len; i++) { + *stream++ = producer->tick() ? 255 : 0; + } +} + +sound_server_1bit::sound_server_1bit(uint16_t samplerate, + sound_producer_1bit& producer) { + if (SDL_Init(SDL_INIT_AUDIO) < 0) { + std::cerr << "Init failed" << std::endl; + exit(1); + } + + SDL_AudioSpec as; + as.freq = samplerate; + as.format = AUDIO_U8; + as.samples = 256; + as.callback = audio_callback; + as.userdata = reinterpret_cast(&producer); + as.channels = 1; + + if (SDL_OpenAudio(&as, NULL) < 0) { + std::cerr << "Unable to open audio" << std::endl; + exit(1); + } + + SDL_PauseAudio(0); +} + +sound_server_1bit::~sound_server_1bit() { SDL_Quit(); } diff --git a/sound_server.h b/sound_server.h new file mode 100644 index 0000000..91d04af --- /dev/null +++ b/sound_server.h @@ -0,0 +1,12 @@ +#pragma once +#include + +struct sound_producer_1bit { + // false = off, true = on + virtual bool tick() = 0; +}; + +struct sound_server_1bit { + sound_server_1bit(uint16_t samplerate, sound_producer_1bit& producer); + ~sound_server_1bit(); +};