From 03c5e4c846898cc64f070b12c48549e76c6bf11b Mon Sep 17 00:00:00 2001 From: patrick-radius Date: Wed, 4 Jan 2017 10:05:22 +0100 Subject: [PATCH] Added an oscillator in float format --- OpenAudio_ArduinoLibrary.h | 1 + .../OscillatorWithPitchmod_Float.ino | 99 +++++++++++++++ keywords.txt | 1 + synth_waveform_F32.cpp | 92 ++++++++++++++ synth_waveform_F32.h | 118 ++++++++++++++++++ 5 files changed, 311 insertions(+) create mode 100644 examples/OscillatorWithPitchmod_Float/OscillatorWithPitchmod_Float.ino create mode 100644 synth_waveform_F32.cpp create mode 100644 synth_waveform_F32.h diff --git a/OpenAudio_ArduinoLibrary.h b/OpenAudio_ArduinoLibrary.h index 2b4a77f..8061c17 100644 --- a/OpenAudio_ArduinoLibrary.h +++ b/OpenAudio_ArduinoLibrary.h @@ -7,3 +7,4 @@ #include #include #include +#include \ No newline at end of file diff --git a/examples/OscillatorWithPitchmod_Float/OscillatorWithPitchmod_Float.ino b/examples/OscillatorWithPitchmod_Float/OscillatorWithPitchmod_Float.ino new file mode 100644 index 0000000..b3626f3 --- /dev/null +++ b/examples/OscillatorWithPitchmod_Float/OscillatorWithPitchmod_Float.ino @@ -0,0 +1,99 @@ +/* + Oscillator with Pitch modulation + + Created: Patrick Radius, Jan 2017 + Purpose: Demonstrates the ability of the oscillator and pitch modulation. + Demonstrates audio processing using floating point data type. + + Uses Teensy Audio Adapter. + + MIT License. use at your own risk. +*/ + +//These are the includes from the Teensy Audio Library +#include //Teensy Audio Librarya +#include +#include +#include +#include + +#include //for AudioConvert_I16toF32, AudioConvert_F32toI16, and AudioEffectGain_F32 + +#define DO_USB 1 //set to 1 to enable USB audio. Be sure to go under the "Tools" menu and do "USB Type" -> "Audio" + +//create audio library objects for handling the audio +AudioControlSGTL5000_Extended sgtl5000; //controller for the Teensy Audio Board +AudioOutputI2S i2s_out; //Digital audio *to* the Teensy Audio Board DAC. Expects Int16. Stereo +AudioConvert_F32toI16 float2Int; //Converts Float to Int16. See class in AudioStream_F32.h + +AudioSynthWaveform_F32 osc1; // Audio-rate oscillator. +AudioSynthWaveform_F32 lfo1; // Low-frequency oscillator to modulate the main oscillator with. + +AudioConnection_F32 patchCord1(osc1, 0, float2Int, 0); // connect the oscillator to the float 2 int converter +AudioConnection_F32 patchCord2(lfo1, 0, osc1, 0); // connect the output of the lfo to the mod input of the oscillator + +AudioConnection patchCord3(float2Int, 0, i2s_out, 0); //Left out. +AudioConnection patchCord4(float2Int, 0, i2s_out, 1); //Right out. + +//For output, you always need an i2s or else you have no clock to drive the audio subsystem. +//So, even if you're not going to use the headphone/line-out, you need these lines +AudioConnection patchCord5(float2Int, 0, i2s_out, 0); //connect the Left float processor to the Left output. +AudioConnection patchCord6(float2Int, 0, i2s_out, 1); //connect the Right float processor to the Right output. + +//Now, if you want USB output, it gets enabled here +#if DO_USB + AudioOutputUSB usb_out; + AudioConnection patchCord7(float2Int, 0, usb_out, 0); //connect the float processor to the Left output + AudioConnection patchCord8(float2Int, 0, usb_out, 1); //connect the float processor to the Right output +#endif + + +// define the overall setup() function, the function that is called once when the device is booting +void setup() { + Serial.begin(115200); //open the USB serial link to enable debugging messages + delay(500); //give the computer's USB serial system a moment to catch up. + Serial.println("Teensy: AudioSynthWaveform_F32 example..."); //identify myself over the USB serial + + // Audio connections require memory, and the record queue + // uses this memory to buffer incoming audio. + AudioMemory(12); //allocate Int16 audio data blocks + AudioMemory_F32(10); //allocate Float32 audio data blocks + + // Enable the audio shield, no input, and enable output + sgtl5000.enable(); //start the audio board + sgtl5000.volume(0.5); //volume can be 0.0 to 1.0. 0.5 seems to be the usual default. + + osc1.begin(1.0, 440.0, 0); // run main oscillator at nominal amplitude, note A4, sine wave + lfo1.begin(1.0, 0.25, 1); // run lfo at nominal amplitude, 0.25hz (so 4 second period), saw wave + osc1.pitchModAmount(3.0); // Set the amount of pitch modulation to be around 3 octaves + +} //end setup() + + +unsigned long curTime_millis = 0; +unsigned long lastMemUpdate_millis=0; + +// define the loop() function, the function that is repeated over and over for the life of the device +void loop() { + + printCPUandMemoryUsage(&Serial); +} //end loop(); + + +void printCPUandMemoryUsage(Stream *s) { + //print status information to the Serial port + curTime_millis = millis(); //what time is it right now + if ((curTime_millis - lastMemUpdate_millis) < 0) lastMemUpdate_millis=0; //handle case where millis wraps around! + + if ((curTime_millis - lastMemUpdate_millis) > 2000) { // print a summary of the current & maximum usage + s->print("Usage/Max: "); + s->print("oscillator CPU = "); s->print(osc1.processorUsage()); s->print("/"); s->print(osc1.processorUsageMax());s->print(", "); + s->print("LFO CPU = "); s->print(lfo1.processorUsage()); s->print("/"); s->print(lfo1.processorUsageMax());s->print(", "); + s->print("all CPU = " ); s->print(AudioProcessorUsage()); s->print("/"); s->print(AudioProcessorUsageMax());s->print(", "); + s->print("Int16 Mem = ");s->print(AudioMemoryUsage()); s->print("/"); s->print(AudioMemoryUsageMax());s->print(", "); + s->print("Float Mem = ");s->print(AudioMemoryUsage_F32());s->print("/"); s->print(AudioMemoryUsageMax_F32()); s->print(", "); + s->println(); + lastMemUpdate_millis = curTime_millis; //we will use this value the next time around. + } +}; + diff --git a/keywords.txt b/keywords.txt index 44ab5e3..0b4d794 100644 --- a/keywords.txt +++ b/keywords.txt @@ -16,6 +16,7 @@ AudioMemoryUsageMax_F32 KEYWORD1 AudioMemoryUsageMaxReset_F32 KEYWORD1 AudioMixer4_F32 KEYWORD1 AudioMultiply_F32 KEYWORD1 +AudioSynthWaveform_F32 KEYWORD1 AudioControlSGTL5000_Extended KEYWORD1 micBiasEnable KEYWORD2 \ No newline at end of file diff --git a/synth_waveform_F32.cpp b/synth_waveform_F32.cpp new file mode 100644 index 0000000..9ae0d02 --- /dev/null +++ b/synth_waveform_F32.cpp @@ -0,0 +1,92 @@ +#include +#include "synth_waveform_F32.h" + +void AudioSynthWaveform_F32::update(void) { + audio_block_f32_t *block, *lfo; + + if (_magnitude == 0.0f) return; + + block = allocate_f32(); + if (!block) return; + + lfo = receiveReadOnly_f32(0); + switch (_OscillatorMode) { + case OSCILLATOR_MODE_SINE: + for (int i = 0; i < AUDIO_BLOCK_SAMPLES; i++) { + applyMod(i, lfo); + + block->data[i] = arm_sin_f32(_Phase); + _Phase += _PhaseIncrement; + while (_Phase >= twoPI) { + _Phase -= twoPI; + } + } + break; + case OSCILLATOR_MODE_SAW: + for (int i = 0; i < AUDIO_BLOCK_SAMPLES; i++) { + applyMod(i, lfo); + + block->data[i] = 1.0f - (2.0f * _Phase / twoPI); + _Phase += _PhaseIncrement; + while (_Phase >= twoPI) { + _Phase -= twoPI; + } + } + break; + case OSCILLATOR_MODE_SQUARE: + for (int i = 0; i < AUDIO_BLOCK_SAMPLES; i++) { + applyMod(i, lfo); + + if (_Phase <= _PI) { + block->data[i] = 1.0f; + } else { + block->data[i] = -1.0f; + } + + _Phase += _PhaseIncrement; + while (_Phase >= twoPI) { + _Phase -= twoPI; + } + } + break; + case OSCILLATOR_MODE_TRIANGLE: + for (int i = 0; i < AUDIO_BLOCK_SAMPLES; i++) { + applyMod(i, lfo); + + float32_t value = -1.0f + (2.0f * _Phase / twoPI); + block->data[i] = 2.0f * (fabs(value) - 0.5f); + _Phase += _PhaseIncrement; + while (_Phase >= twoPI) { + _Phase -= twoPI; + } + } + break; + } + + if (_magnitude != 1.0f) { + arm_scale_f32(block->data, _magnitude, block->data, AUDIO_BLOCK_SAMPLES); + } + + if (lfo) { + release(lfo); + } + + AudioStream_F32::transmit(block); + AudioStream_F32::release(block); +} + +inline float32_t AudioSynthWaveform_F32::applyMod(uint32_t sample, audio_block_f32_t *lfo) { + if (_PortamentoSamples > 0 && _CurrentPortamentoSample++ < _PortamentoSamples) { + _Frequency+=_PortamentoIncrement; + } + + float32_t osc_frequency = _Frequency; + + if (lfo && _PitchModAmt > 0.0f) { + osc_frequency = _Frequency * powf(2.0f, 0.0f / 1200.0f + lfo->data[sample] * _PitchModAmt); + } + + _PhaseIncrement = osc_frequency * twoPI / AUDIO_SAMPLE_RATE_EXACT; + + return osc_frequency; +} \ No newline at end of file diff --git a/synth_waveform_F32.h b/synth_waveform_F32.h new file mode 100644 index 0000000..8591057 --- /dev/null +++ b/synth_waveform_F32.h @@ -0,0 +1,118 @@ +/* + * AudioSynthWaveform_F32 + * + * Created: Patrick Radius, January 2017 + * Purpose: Generate waveforms at a given frequency and amplitude. Allows for pitch-modulation and portamento. + * + * This processes a single stream fo audio data (ie, it is mono) + * + * MIT License. use at your own risk. +*/ +#ifndef SYNTHWAVEFORMF32_H +#define SYNTHWAVEFORMF32_H + +#include +#include + +class AudioSynthWaveform_F32 : public AudioStream_F32 +{ + public: + enum OscillatorMode { + OSCILLATOR_MODE_SINE = 0, + OSCILLATOR_MODE_SAW, + OSCILLATOR_MODE_SQUARE, + OSCILLATOR_MODE_TRIANGLE + }; + + AudioSynthWaveform_F32(void) : AudioStream_F32(1, inputQueueArray_f32), + _PI(2*acos(0.0f)), + twoPI(2 * _PI), + _OscillatorMode(OSCILLATOR_MODE_SINE), + _Frequency(440.0f), + _Phase(0.0f), + _PhaseIncrement(0.0f), + _PitchModAmt(0.0f), + _PortamentoIncrement(0.0f), + _PortamentoSamples(0), + _CurrentPortamentoSample(0), + _NotesPlaying(0) {}; + + void frequency(float32_t freq) { + float32_t nyquist = AUDIO_SAMPLE_RATE_EXACT/2; + + if (freq < 0.0) freq = 0.0; + else if (freq > nyquist) freq = nyquist; + + if (_PortamentoSamples > 0 && _NotesPlaying > 0) { + _PortamentoIncrement = (freq - _Frequency) / (float32_t)_PortamentoSamples; + _CurrentPortamentoSample = 0; + } else { + _Frequency = freq; + } + + _PhaseIncrement = _Frequency * twoPI / AUDIO_SAMPLE_RATE_EXACT; + } + + void amplitude(float32_t n) { + if (n < 0) n = 0; + _magnitude = n; + } + + void begin(short t_type) { + _Phase = 0; + oscillatorMode(t_type); + } + + void begin(float32_t t_amp, float32_t t_freq, short t_type) { + amplitude(t_amp); + frequency(t_freq); + begin(t_type); + } + + void pitchModAmount(float32_t amount) { + _PitchModAmt = amount; + } + + void oscillatorMode(int mode) { + _OscillatorMode = (OscillatorMode)mode; + } + + void portamentoTime(float32_t slidetime) { + _PortamentoTime = slidetime; + _PortamentoSamples = floorf(slidetime * AUDIO_SAMPLE_RATE_ROUNDED); + } + + + void onNoteOn() { + _NotesPlaying++; + } + + void onNoteOff() { + if (_NotesPlaying > 0) { + _NotesPlaying--; + } + } + + void update(void); + private: + inline float32_t applyMod(uint32_t sample, audio_block_f32_t *lfo); + const float32_t _PI; + float32_t twoPI; + + OscillatorMode _OscillatorMode; + float32_t _Frequency; + float32_t _Phase; + float32_t _PhaseIncrement; + float32_t _magnitude; + float32_t _PitchModAmt; + float32_t _PortamentoTime; + float32_t _PortamentoIncrement; + + uint64_t _PortamentoSamples; + uint64_t _CurrentPortamentoSample; + uint8_t _NotesPlaying; + + audio_block_f32_t *inputQueueArray_f32[1]; +}; + +#endif \ No newline at end of file