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