Merge pull request #3 from patrick-radius/OscillatorF32
Added an oscillator in float formatpull/5/head
commit
21fe4b37dc
@ -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 <Audio.h> //Teensy Audio Librarya |
||||||
|
#include <Wire.h> |
||||||
|
#include <SPI.h> |
||||||
|
#include <SD.h> |
||||||
|
#include <SerialFlash.h> |
||||||
|
|
||||||
|
#include <OpenAudio_ArduinoLibrary.h> //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.
|
||||||
|
}
|
||||||
|
}; |
||||||
|
|
@ -0,0 +1,92 @@ |
|||||||
|
#include <arm_math.h> |
||||||
|
#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; |
||||||
|
} |
@ -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 <arm_math.h> |
||||||
|
#include <AudioStream_F32.h> |
||||||
|
|
||||||
|
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 |
Loading…
Reference in new issue