diff --git a/README.md b/README.md index 1c9c558..4ad2f5c 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # DaisySP_Teensy -DaisySP DSP Library for the Teensy 4.x +DaisySP DSP Library for the Teensy 4.x Alpha release 0.1 March 28 2021 @@ -12,7 +12,7 @@ to the PJRC Teensy 4.x. This library allows you to use the features of the Teens DaisySP consists mostly of code collected from other projects - Csound, Soundpipe, and Mutable Instruments eurorack modules. DaisySP is quite similar to Soundpipe but much better written and documented. I ported Soundpipe first and then realized DaisySP is much better. -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 each DaisySP function call consumes 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 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. +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: diff --git a/examples/modal/modal.ino b/examples/modal/modal.ino new file mode 100644 index 0000000..2e29f7a --- /dev/null +++ b/examples/modal/modal.ino @@ -0,0 +1,226 @@ + +// test of DaisySP synth object for the Teensy audio library +// modal synth - not very polyphonic because it uses about 30% CPU for one resonator +// its a good starting point for polyphonic instruments in any case +// some of this code was cribbed from the Faust for Teensy Additivesynth example +// RH March 28 2021 + +#include +#include + +//#define DEBUG // comment out to remove debug code + +#ifdef DEBUG +Metro five_sec=Metro(5000); // Set up a 5 second Metro for performance stats +#endif + +// constants for integer to float and float to integer conversion +#define MULT_16 2147483647 +#define DIV_16 4.6566129e-10 + +#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 +#include "physicalmodeling/resonator.cpp" +#include "physicalmodeling/modalvoice.cpp" +#include "effects/reverbsc.cpp" // uses a LOT of ram + +float samplerate=AUDIO_SAMPLE_RATE_EXACT; + +// create daisySP processing objects + +#define VOICES 1 // uses too much CPU for more than 2 and more than 1 doesn't work anyway +daisysp::ModalVoice voice[VOICES]; +ReverbSc verb; + +// this is the function called by the AudioSynthDaisySP object when it needs a block of samples +void AudioSynthDaisySP::update(void) +{ + float out,sig,wetvl, wetvr; + audio_block_t *block; + + block = allocate(); // grab an audio block + if (!block) { + return; + } + + for (int i=0; i < AUDIO_BLOCK_SAMPLES; i++) { + +//**** insert daisySP generators here + + sig=0; // process and sum the string voices + for (int i=0; i< VOICES;++i) { + sig+=voice[i].Process(); + } + sig=sig/VOICES; // scale the sum +// sig=sig*5; // crank the level a bit + verb.Process(sig, sig, &wetvl, &wetvr); + + out=sig + wetvl*0.2; // add in some reverb + +// convert generated float value -1.0 to +1.0 to int16 used by Teensy Audio + int32_t val = out*MULT_16; + block->data[i] = val >> 16; + } + transmit(block); + release(block); +} + +// teensy audio objects and patch creation + +AudioOutputI2S out; +//AudioOutputUSB outUSB; +AudioControlSGTL5000 audioShield; + +AudioSynthDaisySP synth; // create the daisysp synth audio object + +AudioConnection patchCord20(synth,0,out,0); +AudioConnection patchCord21(synth,0,out,1); +//AudioConnection patchCord22(synth,0,outUSB,0); +//AudioConnection patchCord23(synth,0,outUSB,1); + +// frequencies for all 127 MIDI Note numbers. +// C C# D D# E F F# G G# A A# B + +const float NoteNumToFreq[] = { + 8.18, 8.66, 9.18, 9.72, 10.30, 10.91, 11.56, 12.25, 12.98, 13.75, 14.57, 15.43, + 16.35, 17.32, 18.35, 19.45, 20.60, 21.83, 23.12, 24.50, 25.96, 27.50, 29.14, 30.87, + 32.70, 34.65, 36.71, 38.89, 41.20, 43.65, 46.25, 49.00, 51.91, 55.00, 58.27, 61.74, + 65.41, 69.30, 73.42, 77.78, 82.41, 87.31, 92.50, 98.00, 103.82, 110.00, 116.54, 123.47, + 130.81, 138.59, 146.83, 155.56, 164.81, 174.61, 184.99, 195.99, 207.65, 220.00, 233.08, 246.94, + 261.63, 277.18, 293.66, 311.13, 329.63, 349.23, 369.99, 391.99, 415.31, 440.00, 466.16, 493.88, + 523.25, 554.37, 587.33, 622.25, 659.26, 698.46, 739.99, 783.99, 830.61, 880.00, 932.32, 987.77, + 1046.50, 1108.73, 1174.66, 1244.51, 1318.51, 1396.91, 1479.98, 1567.98, 1661.22, 1760.00, 1864.66, 1975.53, + 2093.00, 2217.46, 2349.32, 2489.02, 2637.02, 2793.83, 2959.96, 3135.96, 3322.44, 3520.00, 3729.31, 3951.07, + 4186.01, 4434.92, 4698.64, 4978.03, 5274.04, 5587.65, 5919.91, 6271.93, 6644.88, 7040.00, 7458.62, 7902.13, + 8372.02, 8869.84, 9397.27, 9956.06, 10548.08, 11175.30, 11839.82, 12543.85 }; + +// for polyphony - an array of all current notes. +// Value -1 means the note is off (not sounding). + +int StoredNotes[VOICES]; + +void setup() { + Serial.begin(38400); + +#ifdef DEBUG + while (!Serial) { + // wait for Arduino Serial Monitor to be ready + } + Serial.println("starting setup"); +#endif + + for (int i=0; i< VOICES;++i) { + StoredNotes[i]=-1; // initialize the note allocation array + voice[i].Init(samplerate); // initialize the voice object + } + +// initialize the reverb object and set its initial parameters + verb.Init(samplerate); + verb.SetFeedback(0.87); + verb.SetLpFreq(10000.0f); + + // Enable the AudioShield + AudioMemory(10); // only uses 2 blocks + Serial.println("enabling audio shield"); + audioShield.enable(); + audioShield.volume(0.4); + + + // Handles for the USB MIDI callbacks + usbMIDI.setHandleNoteOn(myNoteOn); + usbMIDI.setHandleNoteOff(myNoteOff); + usbMIDI.setHandleControlChange(myControlChange); + usbMIDI.setHandleAfterTouchPoly(myAfterTouch); +#ifdef DEBUG + Serial.println("finished setup"); +#endif +} + + +// Only looking for incoming MIDI events in the loop() +// myNoteOn(), myNoteOff() and myControlChange() will be processed on incoming MIDI messages. + +void loop() { + usbMIDI.read(); + +#ifdef DEBUG + // DEBUG - Microcontroller Load Check + 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(")"); + } +#endif +} + +// Callback for incoming NoteOn messages +// Handling the voice allocation here. + +void myNoteOn(byte channel, byte note, byte velocity) { + for (int i=0; i <= VOICES; ++i){ + if (StoredNotes[i] == -1) { // if voice is idle + StoredNotes[i] = int(note); // allocate this voice + voice[i].SetFreq(NoteNumToFreq[note]); + voice[i].Trig(); + } + } +} + +// Callback for incoming NoteOff messages +// Releasing voices to be re-allocated here. + +void myNoteOff(byte channel, byte note, byte velocity) { + for (int i=0; i < VOICES; ++i){ + int k = int(note); + if (StoredNotes[i] == k) { // if this voice matches the note we are silencing + StoredNotes[i] = -1; // deallocate the voice + } + } +} + + +// Callback for incoming CC messages +// I'm using an external MIDI controller (Arturia Beatstep) to set voice parameters +// you can also do this with pots and AnalogRead() + +void myControlChange(byte channel, byte control, byte value) { + float val = float(value) / 127; // convert to 0-1 + + for (int i=0; i < VOICES; ++i){ + switch (control) { + case 101: + voice[i].SetBrightness(val); + break; + case 102: + voice[i].SetDamping(val); + break; + case 103: + voice[i].SetStructure(val); + break; + default: + break; + } + } +} + +// Callback for incoming Aftertouch messages + +void myAfterTouch(byte channel, byte note, byte value) { + float val = float(value) / 127; // convert to 0-1 + +} + + + + +