From 5cda4f0132f6dbb2f403878b7c6aae7ba37142ac Mon Sep 17 00:00:00 2001 From: Gordon JC Pearce Date: Fri, 22 Aug 2025 22:55:20 +0100 Subject: [PATCH] generates sine tones --- plugin/Makefile | 2 +- plugin/assigner.cpp | 9 ++++- plugin/assigner.hpp | 4 +- plugin/generator.cpp | 92 ++++++++++++++++++++++++++++++++++++++++++++ plugin/generator.hpp | 56 +++++++++++++++++++++++++++ plugin/tonewheel.cpp | 11 ++++-- plugin/tonewheel.hpp | 1 + 7 files changed, 168 insertions(+), 7 deletions(-) create mode 100644 plugin/generator.cpp create mode 100644 plugin/generator.hpp diff --git a/plugin/Makefile b/plugin/Makefile index 886f835..c2d4ca3 100644 --- a/plugin/Makefile +++ b/plugin/Makefile @@ -9,7 +9,7 @@ NAME = tonewheel -FILES_DSP = assigner.cpp tonewheel.cpp +FILES_DSP = generator.cpp assigner.cpp tonewheel.cpp include ../dpf/Makefile.plugins.mk TARGETS += vst2 vst3 jack lv2_dsp diff --git a/plugin/assigner.cpp b/plugin/assigner.cpp index 6ae1119..ec47d37 100644 --- a/plugin/assigner.cpp +++ b/plugin/assigner.cpp @@ -18,7 +18,7 @@ #include "assigner.hpp" -#define DEBUG +//#define DEBUG void Assigner::dumpTables() { #ifdef DEBUG @@ -32,7 +32,7 @@ void Assigner::dumpTables() { #endif } -Assigner::Assigner() { +Assigner::Assigner(Voice *v) { d_debug("assigner constructor"); // set up the assigner tables @@ -40,6 +40,9 @@ Assigner::Assigner() { voiceTbl[i] = i; noteTbl[i] = 0x80; } + + // populate the pointer to the generator voice table + voices = v; } void Assigner::handleMidi(MidiEvent *ev) { @@ -92,6 +95,7 @@ void Assigner::noteOff(uint8_t note) { memmove(voiceTbl + i, voiceTbl + i + 1, NUM_VOICES - i - 1); voiceTbl[NUM_VOICES - 1] = v; noteTbl[v] |= 0x80; + voices[v].stopNote(); } void Assigner::noteOn(uint8_t note) { @@ -137,5 +141,6 @@ void Assigner::noteOn(uint8_t note) { while(note>96) note -= 12; while(note<36) note += 12; + voices[v].startNote(note); d_debug("send voice on note %3d to voice %d", note, v); } diff --git a/plugin/assigner.hpp b/plugin/assigner.hpp index d3e7a21..c984284 100644 --- a/plugin/assigner.hpp +++ b/plugin/assigner.hpp @@ -20,10 +20,11 @@ #define _ASSIGNER_HPP #include "DistrhoPlugin.hpp" +#include "generator.hpp" class Assigner { public: - Assigner(); + Assigner(Voice *v); void handleMidi(MidiEvent *ev); private: @@ -33,6 +34,7 @@ class Assigner { uint8_t voiceTbl[NUM_VOICES]; // voices in order of use uint8_t noteTbl[NUM_VOICES]; // note played by voice + Voice *voices; }; #endif diff --git a/plugin/generator.cpp b/plugin/generator.cpp new file mode 100644 index 0000000..cf1fb6d --- /dev/null +++ b/plugin/generator.cpp @@ -0,0 +1,92 @@ +/* + tonewheel organ plugin + + Copyright 2025 Gordon JC Pearce + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +#include "generator.hpp" + +#include +#include + +#include + +#include "DistrhoPluginInfo.h" + +// globals set when the plugin is activated +// sample rate or buffer size may actually change! +extern double sampleRate; +extern uint32_t bufferSize; + +Generator::Generator() { +} + +Generator::~Generator() { +} + +void Generator::activate() { + // create a sine table + for (uint16_t i = 0; i < 256; i++) { + sine[i] = sin((float)i / 128.0 * 3.14159); + } + // create the phase increments for each semitone + for (uint8_t i = 0; i < 12; i++) { + phase[i] = 0; + uint32_t f; + f = (1 << 30) * (32.703 * powf(2, 0.083334 * i) / sampleRate); + omega[i] = f; + } +} + +void Generator::run(float *output, uint32_t frames) { + Voice *v; + uint32_t i; + uint8_t k, p; + uint32_t d = 0; + + memset(output, 0, frames * sizeof(float)); + + // for every frame of the output... + for (i = 0; i < frames; i++) { + // loop over all twelve semitones adding on the phase increment + // these are 32-bit unsigned values and will wrap + for (p = 0; p < 12; p++) { + phase[p] += omega[p]; + } + + // loop over all the voices, calculating what they need + for (k = 0; k < NUM_VOICES; k++) { + v = &voices[k]; + + // 8' stop + d = (phase[v->semi] >> (24 - v->oct)) & 0xff; + output[i] += .25 * (sine[d] * v->gate); + + // mutation stops are a fifth up + d = (phase[(v->semi + 7) % 12] >> (22 - v->oct)) & 0xff; + output[i] += .25 * (sine[d] * v->gate); + } + } +} + +void Voice::startNote(uint8_t key) { + // start a new note + semi = key % 12, oct = (key / 12); + gate = 1; +} + +void Voice::stopNote() { + gate = 0; +} diff --git a/plugin/generator.hpp b/plugin/generator.hpp new file mode 100644 index 0000000..4c0dadb --- /dev/null +++ b/plugin/generator.hpp @@ -0,0 +1,56 @@ +/* + tonewheel organ plugin + + Copyright 2025 Gordon JC Pearce + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +#ifndef GENERATOR_HPP +#define GENERATOR_HPP + +#include + +#include "DistrhoPluginInfo.h" + +class Generator; + +class Voice { + friend Generator; + + public: + void startNote(uint8_t key); + void stopNote(); + + private: + uint8_t semi, oct; + float gate; +}; + +class Generator { + public: + // Generator(uint32_t bufferSize, double xSampleRate); + Generator(); + + ~Generator(); + void run(float *output, uint32_t frames); + void activate(); + Voice voices[NUM_VOICES]; + + private: + uint32_t phase[12]; + uint32_t omega[12]; + float sine[256]; +}; + +#endif diff --git a/plugin/tonewheel.cpp b/plugin/tonewheel.cpp index e6b0d88..ec61c0c 100644 --- a/plugin/tonewheel.cpp +++ b/plugin/tonewheel.cpp @@ -24,11 +24,15 @@ uint32_t bufferSize; START_NAMESPACE_DISTRHO Tonewheel::Tonewheel() : Plugin(kParameterCount, 0, 0) { - assigner = new Assigner; + generator = new Generator; + assigner = new Assigner(generator->voices); + printf("Tonewheel constructor\n"); } Tonewheel::~Tonewheel() { delete assigner; + delete generator; + printf("Tonewheel destructor\n"); } void Tonewheel::initAudioPort(bool input, uint32_t index, AudioPort& port) { @@ -42,6 +46,7 @@ void Tonewheel::initAudioPort(bool input, uint32_t index, AudioPort& port) { void Tonewheel::activate() { sampleRate = getSampleRate(); bufferSize = getBufferSize(); + generator->activate(); printf("In activate, bufSz=%d smpRt=%f\n", bufferSize, sampleRate); } @@ -51,11 +56,11 @@ void Tonewheel::deactivate() { void Tonewheel::run(const float**, float** outputs, uint32_t frames, const MidiEvent* ev, uint32_t evCount) { - (void) frames; - (void) outputs; for (uint32_t i = 0; i < evCount; i++) { assigner->handleMidi((MidiEvent*)&ev[i]); } + + generator->run(outputs[0], frames); } Plugin* createPlugin() { return new Tonewheel(); } diff --git a/plugin/tonewheel.hpp b/plugin/tonewheel.hpp index 012f095..ddc55f0 100644 --- a/plugin/tonewheel.hpp +++ b/plugin/tonewheel.hpp @@ -65,6 +65,7 @@ class Tonewheel : public Plugin { private: Assigner *assigner; + Generator *generator; DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(Tonewheel); };