From 0e0926cbe791bfa2725edcaa5a8064fa2ee15eb8 Mon Sep 17 00:00:00 2001 From: Rich Heslip Date: Mon, 14 Jun 2021 22:03:27 -0400 Subject: [PATCH] Added AudioEffectDaisySP object Added AudioEffectDaisySP object which takes one input and produces one output --- README.md | 40 +++++-- Teensy/Audio/Audio.h | 1 + Teensy/Audio/effect_daisysp.h | 46 ++++++++ .../teensyaudioeffect/teensyaudioeffect.ino | 104 ++++++++++++++++++ .../teensyaudiosynth.ino} | 0 5 files changed, 181 insertions(+), 10 deletions(-) create mode 100644 Teensy/Audio/effect_daisysp.h create mode 100644 examples/teensyaudio/teensyaudioeffect/teensyaudioeffect.ino rename examples/teensyaudio/{teensyaudio.ino => teensyaudiosynth/teensyaudiosynth.ino} (100%) diff --git a/README.md b/README.md index b07f99b..f752f81 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ DaisySP DSP Library for the Teensy 4.x -Alpha release 0.1 March 28 2021 +Alpha release 0.2 June 14 2021 This is a port of Electrosmith's DaisySP signal processing library @@ -14,37 +14,57 @@ DaisySP consists mostly of code collected from other projects - Csound, Soundpip DaisySP uses floating point for all DSP operations and as such will run slowly on the Teensy 3.x - this has not been tested. On a Teensy 4.x most DaisySP functions (oscillators, envelope generators etc) consume roughly 1% of the CPU so you could create a polyphonic synth with 10 oscillators, 10 envelope generators and 10 filters and still have lots of CPU left. The physical modelling functions use quite a lot of CPU. The sine oscillator uses up more CPU since its implemented as a trig function. DaisySP has antialiased polyblep oscillators which are quite CPU efficient - much better than the simplistic and noisy waveform generators in the Teensy Audio library. -This implementation adds a DaisySP Teensy Audio synth object to the Teensy Audio library. An Audio Library synth object has no inputs and it outputs a single stream of audio samples. The library currently supports only one instance of a DaisySP object - more may be possible but I'm not good enough with C++ to figure it out. -The simplest setup is a DaisySP object to the Teensy Audio Shield object which is set up like this: +This implementation adds a DaisySP Teensy Audio synth object and a DaisySP Teensy Audio effect object to the Teensy Audio library. The DaisySP synth object has no inputs and one output ie its a generator of audio samples. The DaisySP effect object has one input and one output. The implementation currently supports only one instance of a DaisySP object - more may be possible but I'm not good enough with C++ to figure it out. +The simplest setup is a DaisySP synth object to the Teensy Audio Shield object which is set up like this: -AudioSynthDaisySP synth; // create the daisysp synth audio object +AudioSynthDaisySP Mysynth; // create the daisysp synth audio object AudioOutputI2S out; // audio shield object AudioControlSGTL5000 audioShield; // control channel -AudioConnection patchCord1(synth,0,out,0); // patch mono synth to right and left channels +AudioConnection patchCord1(Mysynth,0,out,0); // patch mono synth to right and left channels + +AudioConnection patchCord2(Mysynth,0,out,1); + + +A DaisySP effect object would be set up like this: + +AudioEffectDaisySP Myeffect; // create the daisysp synth audio object + +AudioInputI2S audioInput; // audio shield: mic or line-in + +AudioOutputI2S out; // audio shield object + +AudioControlSGTL5000 audioShield; // control channel + +AudioConnection patchCord1(audioInput,0,Myeffect,0); // mono input + +AudioConnection patchCord2(Myeffect,0,out,0); // patch mono synth to right and left channels + +AudioConnection patchCord3(Myeffect,0,out,1); -AudioConnection patchCord2(synth,0,out,1); Teensy Audio processes 128 16 bit integer samples at a time and uses a pool of sample buffers which are passed between audio objects. This is memory and CPU efficient but can make coding audio objects quite complicated because of the sample buffer management. Every audio object has a callback function which processes blocks of samples approximately every 2.3 ms @ 44.1khz sample rate. In contrast, DaisySP processes one sample at a time using floating point and each function allocates its memory statically. Simple, but uses a lot of memory for things like reverbs and delays and its pretty CPU intensive. -To use DaisySP with Teensy Audio we process 128 samples at a time, convert the floating point results to integer and pass them to the next Teensy audio object in the patch. The DaisySP audio object has a callback function called AudioSynthDaisySP::update which does this. You must have this function in your sketch and this is where you call DaisySP library functions. Look at the example sketches to see how this works. +To use DaisySP with Teensy Audio we process 128 samples at a time, convert the floating point results to integer and pass them to the next Teensy audio object in the patch. The DaisySP audio object has a callback function called AudioSynthDaisySP::update (or AudioEffectDaisySP::update) which does this. You must have this function in your sketch and this is where you call DaisySP library functions. Look at the example sketches to see how this works. Installing the library: Copy the contents of the DaisySP folder to your arduino/library folder -Copy the file Teensy/Audio/synth_daisysp.h (the DaisySP audio object) to your Teensy audio library - usually this will be your_Arduino_installation_directory/hardware/teensy/avr/libraries/audio. +Copy the file Teensy/Audio/synth_daisysp.h and Teensy/Audio/effect_daisysp.h (the DaisySP audio objects) to your Teensy audio library - usually this will be your_Arduino_installation_directory/hardware/teensy/avr/libraries/audio. -Teensy/Audio/Audio.h has a #include synth_daisysp.h so you can replace your Teensy audio library Audio.h with this file. Its probably better edit your existing Audio.h file - I can't guarantee I will be tracking future changes to the Teensy Audio library. +Teensy/Audio/Audio.h has the lines #include synth_daisysp.h and #include effect_daisysp.h added so you can replace your Teensy audio library Audio.h with this file. Its probably better edit your existing Audio.h file - I can't guarantee I will be tracking future changes to the Teensy Audio library. I decided to structure the library so you have to manually include the DaisySP *.cpp files you are using in your sketch vs compiling the whole library into the sketch. This is a bit of a pain but including the whole library uses almost 500k of program memory and close to 500k of RAM which leaves very little RAM for the rest of your code. There is currently no provision for using the optional PSRAM on the Teensy 4.1. -I have not tested the library extensively but so far everything works as expected. It should be fairly simple to add a DaisySP audio effect object (ie one that has inputs and outputs) and one that generates stereo audio out for the DaisySP functions generate stereo audio. I have not had a need for this yet so its not in the library. +I have not tested the library extensively. It should be fairly simple to add a DaisySP audio effect object (ie one that has inputs and outputs) and one that generates stereo audio out for the DaisySP functions generate stereo audio. I have not had a need for this yet so its not in the library. + +There are compile problems with the resonators etc. I think this an issue with derived classes but I have not been able to fix it as yet. Tested with Arduino 1.85 and Teensyduino 1.53. diff --git a/Teensy/Audio/Audio.h b/Teensy/Audio/Audio.h index 1d43fb5..00540d8 100644 --- a/Teensy/Audio/Audio.h +++ b/Teensy/Audio/Audio.h @@ -88,6 +88,7 @@ #include "effect_granular.h" #include "effect_combine.h" #include "effect_rectifier.h" +#include "effect_daisysp.h" #include "filter_biquad.h" #include "filter_fir.h" #include "filter_variable.h" diff --git a/Teensy/Audio/effect_daisysp.h b/Teensy/Audio/effect_daisysp.h new file mode 100644 index 0000000..b42af89 --- /dev/null +++ b/Teensy/Audio/effect_daisysp.h @@ -0,0 +1,46 @@ +/* Audio Library for Teensy 3.X + * Copyright (c) 2016, Paul Stoffregen, paul@pjrc.com + * + * Development of this audio library was funded by PJRC.COM, LLC by sales of + * Teensy and Audio Adaptor boards. Please support PJRC's efforts to develop + * open source software by purchasing Teensy or other PJRC products. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice, development funding notice, and this permission + * notice shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + // RH June 14 2021 - an effect that uses the DaisySP DSP library to process audio + // its an effect - it has one input and generates one output + // normally AudioEffectDaisySP:update() will be in your sketch - its the equivalent of Daisylib's Audiocallback function + +#ifndef effect_daisysp_h_ +#define effect_daisysp_h_ +#include "Arduino.h" +#include "AudioStream.h" + +class AudioEffectDaisySP: public AudioStream +{ +public: + AudioEffectDaisySP(void) : AudioStream(1, inputQueueArray) {} + virtual void update(void); +private: + audio_block_t *inputQueueArray[1]; +}; + +#endif + + diff --git a/examples/teensyaudio/teensyaudioeffect/teensyaudioeffect.ino b/examples/teensyaudio/teensyaudioeffect/teensyaudioeffect.ino new file mode 100644 index 0000000..612d613 --- /dev/null +++ b/examples/teensyaudio/teensyaudioeffect/teensyaudioeffect.ino @@ -0,0 +1,104 @@ +// test of DaisySP effect object for the Teensy audio library +// just does audio passthru + +#include +#include + +Metro five_sec=Metro(5000); // Set up a 5 second Metro +Metro trigger=Metro(250); // envelope trigger + +#include "daisysp.h" + +using namespace daisysp; + +// including the source files is a pain but that way you compile in only the modules you need +// DaisySP statically allocates memory and some modules e.g. reverb use a lot of ram + + +// constants for integer to float and float to integer conversion +//#define MULT_16 2147483647 +//#define DIV_16 4.6566129e-10 +#define MULT_16 2147483647 +#define DIV_16 3.05109e-5 + +float samplerate=AUDIO_SAMPLE_RATE_EXACT; + +// create daisySP processing objects + + +AudioInputI2S audioInput; // audio shield: mic or line-in +AudioOutputI2S out; +//AudioOutputUSB outUSB; +AudioControlSGTL5000 audioShield; + +AudioEffectDaisySP effect; // create the daisysp effect audio object +AudioConnection patchCord1(audioInput,0,effect,0); //mono input + +AudioConnection patchCord20(effect,0,out,0); // send to left and right out +AudioConnection patchCord21(effect,0,out,1); +//AudioConnection patchCord22(synth,0,outUSB,0); +//AudioConnection patchCord23(synth,0,outUSB,1); + + + +// this is the function called by the AudioSynthDaisySP object when it needs a block of samples +void AudioEffectDaisySP::update(void) +{ + float in, out,sig; + audio_block_t *block; + + // start of processing functions. + block = receiveWritable(); // we will modify the samples in place + if (!block) return; + + for (int s=0; s < AUDIO_BLOCK_SAMPLES; s++) { + + in=(float)block->data[s]*DIV_16; // convert -32767 to 32767 to -1.0 to +1.0 + +//**** insert daisySP generators here + out=in; + + +// convert generated float value -1.0 to +1.0 to int16 used by Teensy Audio + int32_t val = out*MULT_16; + block->data[s] = val >> 16; + } + transmit(block); + release(block); +} + +void setup() { + // Init Serial + Serial.begin(38400); +// wait for Arduino Serial Monitor to be ready +// while (!Serial); + + Serial.println("starting setup"); + + + // Enable the AudioShield + AudioMemory(10); // only need 2 blocks for 1 daisySP object + audioShield.enable(); + audioShield.volume(0.3); + + Serial.println("finished setup"); +} + float freq=1000.0, amp; + +void loop() { + + +// audio stats + if (five_sec.check() == 1) + { + Serial.print("Proc = "); + Serial.print(AudioProcessorUsage()); + Serial.print(" ("); + Serial.print(AudioProcessorUsageMax()); + Serial.print("), Mem = "); + Serial.print(AudioMemoryUsage()); + Serial.print(" ("); + Serial.print(AudioMemoryUsageMax()); + Serial.println(")"); + } +} diff --git a/examples/teensyaudio/teensyaudio.ino b/examples/teensyaudio/teensyaudiosynth/teensyaudiosynth.ino similarity index 100% rename from examples/teensyaudio/teensyaudio.ino rename to examples/teensyaudio/teensyaudiosynth/teensyaudiosynth.ino