diff --git a/MicroMDAEPiano.ino b/MicroMDAEPiano.ino index 8fb6f66..644ef11 100644 --- a/MicroMDAEPiano.ino +++ b/MicroMDAEPiano.ino @@ -37,6 +37,7 @@ #include "UI.hpp" #include "midi_devices.hpp" #include "config.h" +#include "synth_waveform_extended.h" //************************************************************************************************* //* GLOBAL VARIABLES @@ -56,7 +57,7 @@ AudioAmplifier volume_l; AudioAmplifier inverter; AudioEffectModulatedDelay modchorus_r; AudioEffectModulatedDelay modchorus_l; -AudioSynthWaveform modulator; +AudioSynthWaveformExtended modulator; AudioConnection patchCord0(queue_r, peak_r); AudioConnection patchCord1(queue_l, peak_l); AudioConnection patchCord2(queue_r, freeverb_r); diff --git a/UI.hpp b/UI.hpp index bfe7e76..4bf5a59 100644 --- a/UI.hpp +++ b/UI.hpp @@ -49,6 +49,7 @@ #include #include "Encoder4.h" #include "config.h" +#include "synth_waveform_extended.h" LiquidCrystal_I2C lcd(LCD_I2C_ADDRESS, LCD_CHARS, LCD_LINES); Encoder4 enc[NUM_ENCODER] = {Encoder4(ENC_L_PIN_A, ENC_L_PIN_B), Encoder4(ENC_R_PIN_A, ENC_R_PIN_B)}; @@ -123,7 +124,7 @@ extern void eeprom_config_write(uint8_t value); extern AudioControlSGTL5000 sgtl5000_1; extern AudioEffectFreeverb freeverb_r; extern AudioEffectFreeverb freeverb_l; -extern AudioSynthWaveform modulator; +extern AudioSynthWaveformExtended modulator; extern AudioEffectModulatedDelay modchorus_r; extern AudioEffectModulatedDelay modchorus_l; extern AudioMixer4 mixer_r; @@ -244,6 +245,23 @@ char* get_chorus_delay_value_text(void) return (chorus_delay_value_text1); } +char chorus_waveform_value_text1[] = " "; +char* get_chorus_waveform_value_text(void) +{ + switch (configuration.chorus_waveform) + { + case 1: + sprintf(chorus_waveform_value_text1, "TRIANGLE"); + break; + case 2: + sprintf(chorus_waveform_value_text1, "SINE"); + break; + default: + sprintf(chorus_waveform_value_text1, "TRIANGLE"); + } + return (chorus_waveform_value_text1); +} + char midi_channel_value_text1[] = " "; char* get_midi_channel_value_text(void) { @@ -435,14 +453,14 @@ const char effects_text6[] PROGMEM = "Comp. Limit"; const char effects_text7[] PROGMEM = "Comp. Threshold"; const char effects_text8[] PROGMEM = "Comp. Attack"; const char effects_text9[] PROGMEM = "Comp. Decay"; -const char effects_text10[] PROGMEM = "Rev. Roomsize"; -const char effects_text11[] PROGMEM = "Rev. Damping"; -const char effects_text12[] PROGMEM = "Rev. Level"; +const char effects_text10[] PROGMEM = "Reverb Roomsize"; +const char effects_text11[] PROGMEM = "Reverb Damping"; +const char effects_text12[] PROGMEM = "Reverb Level"; const char effects_text13[] PROGMEM = "Chorus Freq."; const char effects_text14[] PROGMEM = "Chorus Delay"; const char effects_text15[] PROGMEM = "Chorus Intens."; -const char effects_text16[] PROGMEM = "Chorus FB"; -const char effects_text17[] PROGMEM = "Chorus Wave"; +const char effects_text16[] PROGMEM = "Chorus Feedback"; +const char effects_text17[] PROGMEM = "Chorus Waveform"; const char effects_text18[] PROGMEM = "Chorus Level"; const char effects_text19[] PROGMEM = "Bass LR Level"; const char effects_text20[] PROGMEM = "Bass M Level"; @@ -629,7 +647,7 @@ LiquidMenu chorus_intensity_menu(lcd); CHORUS_FEEDBACK MENU ******************************************/ #define NUM_CHORUS_FEEDBACK_MENUS 1 -const char chorus_feedback_text1[] PROGMEM = "Chorus FB"; +const char chorus_feedback_text1[] PROGMEM = "Chorus Feedback"; LiquidLine chorus_feedback_line1(1, 0, chorus_feedback_text1); LiquidLine chorus_feedback_line2(1, 1, configuration.chorus_feedback); LiquidScreen chorus_feedback_screen; @@ -639,9 +657,9 @@ LiquidMenu chorus_feedback_menu(lcd); CHORUS_WAVEFORM MENU ******************************************/ #define NUM_CHORUS_WAVEFORM_MENUS 1 -const char chorus_waveform_text1[] PROGMEM = "Chorus Wave"; +const char chorus_waveform_text1[] PROGMEM = "Chorus Waveform"; LiquidLine chorus_waveform_line1(1, 0, chorus_waveform_text1); -LiquidLine chorus_waveform_line2(1, 1, configuration.chorus_waveform); +LiquidLine chorus_waveform_line2(1, 1, get_chorus_waveform_value_text); LiquidScreen chorus_waveform_screen; LiquidMenu chorus_waveform_menu(lcd); @@ -2144,18 +2162,18 @@ void set_chorus_waveform(uint8_t value) { #ifdef SHOW_DEBUG Serial.print(F("Set CHORUS_WAVEFORM ")); - //Serial.println(value); - Serial.println("=> Setting CHORUS_WAVEFORM not yet available!"); + Serial.println(value); #endif switch (value) { case 1: - //modulator.waveform(WAVEFORM_TRIANGLE); + modulator.waveform(WAVEFORM_TRIANGLE); break; case 2: - //modulator.waveform(WAVEFORM_SINE); + modulator.waveform(WAVEFORM_SINE); + break; default: - //modulator.waveform(WAVEFORM_TRIANGLE); + modulator.waveform(WAVEFORM_TRIANGLE); break; } configuration.chorus_waveform = value; diff --git a/mozzi/DCfilter.h b/mozzi/DCfilter.h deleted file mode 100644 index 2b2dde7..0000000 --- a/mozzi/DCfilter.h +++ /dev/null @@ -1,89 +0,0 @@ -/* - * DCfilter.h - * - * Copyright 2012 Tim Barrass. - * - * This file is part of Mozzi. - * - * Mozzi is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. - * - */ - -#ifndef DCFILTER_H -#define DCFILTER_H - -/* -tb2010 adapted from: -robert bristow-johnson, DSP Trick: Fixed-Point DC Blocking Filter with Noise-Shaping -http://www.dspguru.com/book/export/html/126 - -y[n] = x[n] - x[n-1] + a * y[n-1] - -Where y[n] is the output at the current time n, and x[n] is the input at the current time n. - -also, see DC Blocker Algorithms, http://www.ingelec.uns.edu.ar/pds2803/materiales/articulos/04472252.pdf - */ - -/** -A DC-blocking filter useful for highlighting changes in control signals. -The output of the filter settles to 0 if the incoming signal stays constant. If the input changes, the -filter output swings to track the change and eventually settles back to 0. -*/ -class DCfilter -{ -public: -/** -Instantiate a DC-blocking filter. -@param pole sets the responsiveness of the filter, -how long it takes to settle to 0 if the input signal levels out at a constant value. -*/ - DCfilter(float pole):acc(0),prev_x(0),prev_y(0) - { - A = (int)(32768.0*(1.0 - pole)); - } - -/* almost original - // timing: 20us - int next(int x) - { - setPin13High(); - acc -= prev_x; - prev_x = (long)x<<15; - acc += prev_x; - acc -= A*prev_y; - prev_y = acc>>15; // quantization happens here - int filtered = (int)prev_y; - // acc has y[n] in upper 17 bits and -e[n] in lower 15 bits - setPin13Low(); - return filtered; - } - */ - - /** - Filter the incoming value and return the result. - @param x the value to filter - @return filtered signal - */ - // timing :8us - inline - int next(int x) - { - acc += ((long)(x-prev_x)<<16)>>1; - prev_x = x; - acc -= (long)A*prev_y; // acc has y[n] in upper 17 bits and -e[n] in lower 15 bits - prev_y = (acc>>16)<<1; // faster than >>15 but loses bit 0 - if (acc & 32784) prev_y += 1; // adds 1 if it was in the 0 bit position lost in the shifts above - return prev_y; - } - -private: - long acc; - int prev_x, prev_y,A; -}; - -/** -@example 05.Control_Filters/DCFilter/DCFilter.ino -This example demonstrates the DCFilter class. -*/ - -#endif // #ifndef DCFILTER_H diff --git a/mozzi/DCfilter.ino b/mozzi/DCfilter.ino deleted file mode 100644 index 56fb2be..0000000 --- a/mozzi/DCfilter.ino +++ /dev/null @@ -1,50 +0,0 @@ -/* Example of filtering an analog input to remove DC bias, - using Mozzi sonification library. - - Demonstrates DCfilter(), DC-blocking filter useful for - highlighting changes in control signals. - The output of the filter settles to 0 if the incoming signal stays constant. - If the input changes, the filter output swings to track the change and - eventually settles back to 0. - - Mozzi documentation/API - https://sensorium.github.io/Mozzi/doc/html/index.html - - Mozzi help/discussion/announcements: - https://groups.google.com/forum/#!forum/mozzi-users - - Tim Barrass 2013, CC by-nc-sa. - -*/ - -#include -#include - -int sensorPin = A0; - -DCfilter dcFiltered(0.9); // parameter sets how long the filter takes to settle - -void setup() { - //Serial.begin(9600); // for Teensy 3.1, beware printout can cause glitches - Serial.begin(115200); - startMozzi(); -} - - -void updateControl(){ - // read the value from the sensor: - int sensorValue = mozziAnalogRead(sensorPin); - Serial.print(sensorValue); - Serial.print(" Filtered = "); - Serial.println(dcFiltered.next(sensorValue)); -} - - -int updateAudio(){ - return 0; -} - - -void loop(){ - audioHook(); -} diff --git a/synth_waveform_extended.cpp b/synth_waveform_extended.cpp new file mode 100644 index 0000000..3f985ff --- /dev/null +++ b/synth_waveform_extended.cpp @@ -0,0 +1,403 @@ +/* Audio Library for Teensy 3.X + * Copyright (c) 2018, 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. + */ + +#include +#include "synth_waveform_extended.h" +#include "arm_math.h" +#include "utility/dspinst.h" + + +// uncomment for more accurate but more computationally expensive frequency modulation +//#define IMPROVE_EXPONENTIAL_ACCURACY + + +void AudioSynthWaveformExtended::update(void) +{ + audio_block_t *block; + int16_t *bp, *end; + int32_t val1, val2; + int16_t magnitude15; + uint32_t i, ph, index, index2, scale; + const uint32_t inc = phase_increment; + + ph = phase_accumulator + phase_offset; + if (magnitude == 0) { + phase_accumulator += inc * AUDIO_BLOCK_SAMPLES; + return; + } + block = allocate(); + if (!block) { + phase_accumulator += inc * AUDIO_BLOCK_SAMPLES; + return; + } + bp = block->data; + + switch(tone_type) { + case WAVEFORM_SINE: + for (i=0; i < AUDIO_BLOCK_SAMPLES; i++) { + index = ph >> 24; + val1 = AudioWaveformSine[index]; + val2 = AudioWaveformSine[index+1]; + scale = (ph >> 8) & 0xFFFF; + val2 *= scale; + val1 *= 0x10000 - scale; + *bp++ = multiply_32x32_rshift32(val1 + val2, magnitude); + ph += inc; + } + break; + + case WAVEFORM_ARBITRARY: + if (!arbdata) { + release(block); + phase_accumulator += inc * AUDIO_BLOCK_SAMPLES; + return; + } + // len = 256 + for (i=0; i < AUDIO_BLOCK_SAMPLES; i++) { + index = ph >> 24; + index2 = index + 1; + if (index2 >= 256) index2 = 0; + val1 = *(arbdata + index); + val2 = *(arbdata + index2); + scale = (ph >> 8) & 0xFFFF; + val2 *= scale; + val1 *= 0x10000 - scale; + *bp++ = multiply_32x32_rshift32(val1 + val2, magnitude); + ph += inc; + } + break; + + case WAVEFORM_SQUARE: + magnitude15 = signed_saturate_rshift(magnitude, 16, 1); + for (i=0; i < AUDIO_BLOCK_SAMPLES; i++) { + if (ph & 0x80000000) { + *bp++ = -magnitude15; + } else { + *bp++ = magnitude15; + } + ph += inc; + } + break; + + case WAVEFORM_SAWTOOTH: + for (i=0; i < AUDIO_BLOCK_SAMPLES; i++) { + *bp++ = signed_multiply_32x16t(magnitude, ph); + ph += inc; + } + break; + + case WAVEFORM_SAWTOOTH_REVERSE: + for (i=0; i < AUDIO_BLOCK_SAMPLES; i++) { + *bp++ = signed_multiply_32x16t(0xFFFFFFFFu - magnitude, ph); + ph += inc; + } + break; + + case WAVEFORM_TRIANGLE: + for (i=0; i < AUDIO_BLOCK_SAMPLES; i++) { + uint32_t phtop = ph >> 30; + if (phtop == 1 || phtop == 2) { + *bp++ = ((0xFFFF - (ph >> 15)) * magnitude) >> 16; + } else { + *bp++ = (((int32_t)ph >> 15) * magnitude) >> 16; + } + ph += inc; + } + break; + + case WAVEFORM_TRIANGLE_VARIABLE: + do { + uint32_t rise = 0xFFFFFFFF / (pulse_width >> 16); + uint32_t fall = 0xFFFFFFFF / (0xFFFF - (pulse_width >> 16)); + for (i=0; i < AUDIO_BLOCK_SAMPLES; i++) { + if (ph < pulse_width/2) { + uint32_t n = (ph >> 16) * rise; + *bp++ = ((n >> 16) * magnitude) >> 16; + } else if (ph < 0xFFFFFFFF - pulse_width/2) { + uint32_t n = 0x7FFFFFFF - (((ph - pulse_width/2) >> 16) * fall); + *bp++ = (((int32_t)n >> 16) * magnitude) >> 16; + } else { + uint32_t n = ((ph + pulse_width/2) >> 16) * rise + 0x80000000; + *bp++ = (((int32_t)n >> 16) * magnitude) >> 16; + } + ph += inc; + } + } while (0); + break; + + case WAVEFORM_PULSE: + magnitude15 = signed_saturate_rshift(magnitude, 16, 1); + for (i=0; i < AUDIO_BLOCK_SAMPLES; i++) { + if (ph < pulse_width) { + *bp++ = magnitude15; + } else { + *bp++ = -magnitude15; + } + ph += inc; + } + break; + + case WAVEFORM_SAMPLE_HOLD: + for (i=0; i < AUDIO_BLOCK_SAMPLES; i++) { + *bp++ = sample; + uint32_t newph = ph + inc; + if (newph < ph) { + sample = random(magnitude) - (magnitude >> 1); + } + ph = newph; + } + break; + } + phase_accumulator = ph - phase_offset; + + if (tone_offset) { + bp = block->data; + end = bp + AUDIO_BLOCK_SAMPLES; + do { + val1 = *bp; + *bp++ = signed_saturate_rshift(val1 + tone_offset, 16, 0); + } while (bp < end); + } + transmit(block, 0); + release(block); +} + +//-------------------------------------------------------------------------------- + +void AudioSynthWaveformExtendedModulated::update(void) +{ + audio_block_t *block, *moddata, *shapedata; + int16_t *bp, *end; + int32_t val1, val2; + int16_t magnitude15; + uint32_t i, ph, index, index2, scale, priorphase; + const uint32_t inc = phase_increment; + + moddata = receiveReadOnly(0); + shapedata = receiveReadOnly(1); + + // Pre-compute the phase angle for every output sample of this update + ph = phase_accumulator; + priorphase = phasedata[AUDIO_BLOCK_SAMPLES-1]; + if (moddata && modulation_type == 0) { + // Frequency Modulation + bp = moddata->data; + for (i=0; i < AUDIO_BLOCK_SAMPLES; i++) { + int32_t n = (*bp++) * modulation_factor; // n is # of octaves to mod + int32_t ipart = n >> 27; // 4 integer bits + n &= 0x7FFFFFF; // 27 fractional bits + #ifdef IMPROVE_EXPONENTIAL_ACCURACY + // exp2 polynomial suggested by Stefan Stenzel on "music-dsp" + // mail list, Wed, 3 Sep 2014 10:08:55 +0200 + int32_t x = n << 3; + n = multiply_accumulate_32x32_rshift32_rounded(536870912, x, 1494202713); + int32_t sq = multiply_32x32_rshift32_rounded(x, x); + n = multiply_accumulate_32x32_rshift32_rounded(n, sq, 1934101615); + n = n + (multiply_32x32_rshift32_rounded(sq, + multiply_32x32_rshift32_rounded(x, 1358044250)) << 1); + n = n << 1; + #else + // exp2 algorithm by Laurent de Soras + // https://www.musicdsp.org/en/latest/Other/106-fast-exp2-approximation.html + n = (n + 134217728) << 3; + n = multiply_32x32_rshift32_rounded(n, n); + n = multiply_32x32_rshift32_rounded(n, 715827883) << 3; + n = n + 715827882; + #endif + uint32_t scale = n >> (14 - ipart); + uint64_t phstep = (uint64_t)inc * scale; + uint32_t phstep_msw = phstep >> 32; + if (phstep_msw < 0x7FFE) { + ph += phstep >> 16; + } else { + ph += 0x7FFE0000; + } + phasedata[i] = ph; + } + release(moddata); + } else if (moddata) { + // Phase Modulation + bp = moddata->data; + for (i=0; i < AUDIO_BLOCK_SAMPLES; i++) { + // more than +/- 180 deg shift by 32 bit overflow of "n" + uint32_t n = (uint16_t)(*bp++) * modulation_factor; + phasedata[i] = ph + n; + ph += inc; + } + release(moddata); + } else { + // No Modulation Input + for (i=0; i < AUDIO_BLOCK_SAMPLES; i++) { + phasedata[i] = ph; + ph += inc; + } + } + phase_accumulator = ph; + + // If the amplitude is zero, no output, but phase still increments properly + if (magnitude == 0) { + if (shapedata) release(shapedata); + return; + } + block = allocate(); + if (!block) { + if (shapedata) release(shapedata); + return; + } + bp = block->data; + + // Now generate the output samples using the pre-computed phase angles + switch(tone_type) { + case WAVEFORM_SINE: + for (i=0; i < AUDIO_BLOCK_SAMPLES; i++) { + ph = phasedata[i]; + index = ph >> 24; + val1 = AudioWaveformSine[index]; + val2 = AudioWaveformSine[index+1]; + scale = (ph >> 8) & 0xFFFF; + val2 *= scale; + val1 *= 0x10000 - scale; + *bp++ = multiply_32x32_rshift32(val1 + val2, magnitude); + } + break; + + case WAVEFORM_ARBITRARY: + if (!arbdata) { + release(block); + if (shapedata) release(shapedata); + return; + } + // len = 256 + for (i=0; i < AUDIO_BLOCK_SAMPLES; i++) { + ph = phasedata[i]; + index = ph >> 24; + index2 = index + 1; + if (index2 >= 256) index2 = 0; + val1 = *(arbdata + index); + val2 = *(arbdata + index2); + scale = (ph >> 8) & 0xFFFF; + val2 *= scale; + val1 *= 0x10000 - scale; + *bp++ = multiply_32x32_rshift32(val1 + val2, magnitude); + } + break; + + case WAVEFORM_PULSE: + if (shapedata) { + magnitude15 = signed_saturate_rshift(magnitude, 16, 1); + for (i=0; i < AUDIO_BLOCK_SAMPLES; i++) { + uint32_t width = ((shapedata->data[i] + 0x8000) & 0xFFFF) << 16; + if (phasedata[i] < width) { + *bp++ = magnitude15; + } else { + *bp++ = -magnitude15; + } + } + break; + } // else fall through to orginary square without shape modulation + + case WAVEFORM_SQUARE: + magnitude15 = signed_saturate_rshift(magnitude, 16, 1); + for (i=0; i < AUDIO_BLOCK_SAMPLES; i++) { + if (phasedata[i] & 0x80000000) { + *bp++ = -magnitude15; + } else { + *bp++ = magnitude15; + } + } + break; + + case WAVEFORM_SAWTOOTH: + for (i=0; i < AUDIO_BLOCK_SAMPLES; i++) { + *bp++ = signed_multiply_32x16t(magnitude, phasedata[i]); + } + break; + + case WAVEFORM_SAWTOOTH_REVERSE: + for (i=0; i < AUDIO_BLOCK_SAMPLES; i++) { + *bp++ = signed_multiply_32x16t(0xFFFFFFFFu - magnitude, phasedata[i]); + } + break; + + case WAVEFORM_TRIANGLE_VARIABLE: + if (shapedata) { + for (i=0; i < AUDIO_BLOCK_SAMPLES; i++) { + uint32_t width = (shapedata->data[i] + 0x8000) & 0xFFFF; + uint32_t rise = 0xFFFFFFFF / width; + uint32_t fall = 0xFFFFFFFF / (0xFFFF - width); + uint32_t halfwidth = width << 15; + uint32_t n; + ph = phasedata[i]; + if (ph < halfwidth) { + n = (ph >> 16) * rise; + *bp++ = ((n >> 16) * magnitude) >> 16; + } else if (ph < 0xFFFFFFFF - halfwidth) { + n = 0x7FFFFFFF - (((ph - halfwidth) >> 16) * fall); + *bp++ = (((int32_t)n >> 16) * magnitude) >> 16; + } else { + n = ((ph + halfwidth) >> 16) * rise + 0x80000000; + *bp++ = (((int32_t)n >> 16) * magnitude) >> 16; + } + ph += inc; + } + break; + } // else fall through to orginary triangle without shape modulation + + case WAVEFORM_TRIANGLE: + for (i=0; i < AUDIO_BLOCK_SAMPLES; i++) { + ph = phasedata[i]; + uint32_t phtop = ph >> 30; + if (phtop == 1 || phtop == 2) { + *bp++ = ((0xFFFF - (ph >> 15)) * magnitude) >> 16; + } else { + *bp++ = (((int32_t)ph >> 15) * magnitude) >> 16; + } + } + break; + case WAVEFORM_SAMPLE_HOLD: + for (i=0; i < AUDIO_BLOCK_SAMPLES; i++) { + ph = phasedata[i]; + if (ph < priorphase) { // does not work for phase modulation + sample = random(magnitude) - (magnitude >> 1); + } + priorphase = ph; + *bp++ = sample; + } + break; + } + + if (tone_offset) { + bp = block->data; + end = bp + AUDIO_BLOCK_SAMPLES; + do { + val1 = *bp; + *bp++ = signed_saturate_rshift(val1 + tone_offset, 16, 0); + } while (bp < end); + } + if (shapedata) release(shapedata); + transmit(block, 0); + release(block); +} diff --git a/synth_waveform_extended.h b/synth_waveform_extended.h new file mode 100644 index 0000000..843ddf4 --- /dev/null +++ b/synth_waveform_extended.h @@ -0,0 +1,221 @@ +/* Audio Library for Teensy 3.X + Copyright (c) 2014, 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. +*/ + +/* + Extension for setting the waveform while running by H. Wirtz +*/ + +#ifndef synth_waveform_extended_h_ +#define synth_waveform_extended_h_ + +#include +#include "AudioStream.h" +#include "arm_math.h" + +// waveforms.c +extern "C" { + extern const int16_t AudioWaveformSine[257]; +} + + +#define WAVEFORM_SINE 0 +#define WAVEFORM_SAWTOOTH 1 +#define WAVEFORM_SQUARE 2 +#define WAVEFORM_TRIANGLE 3 +#define WAVEFORM_ARBITRARY 4 +#define WAVEFORM_PULSE 5 +#define WAVEFORM_SAWTOOTH_REVERSE 6 +#define WAVEFORM_SAMPLE_HOLD 7 +#define WAVEFORM_TRIANGLE_VARIABLE 8 + +class AudioSynthWaveformExtended : public AudioStream +{ + public: + AudioSynthWaveformExtended(void) : AudioStream(0, NULL), + phase_accumulator(0), phase_increment(0), phase_offset(0), + magnitude(0), pulse_width(0x40000000), + arbdata(NULL), sample(0), tone_type(WAVEFORM_SINE), + tone_offset(0) { + } + + void frequency(float freq) { + if (freq < 0.0) { + freq = 0.0; + } else if (freq > AUDIO_SAMPLE_RATE_EXACT / 2) { + freq = AUDIO_SAMPLE_RATE_EXACT / 2; + } + phase_increment = freq * (4294967296.0 / AUDIO_SAMPLE_RATE_EXACT); + if (phase_increment > 0x7FFE0000u) phase_increment = 0x7FFE0000; + } + void phase(float angle) { + if (angle < 0.0) { + angle = 0.0; + } else if (angle > 360.0) { + angle = angle - 360.0; + if (angle >= 360.0) return; + } + phase_offset = angle * (4294967296.0 / 360.0); + } + void amplitude(float n) { // 0 to 1.0 + if (n < 0) { + n = 0; + } else if (n > 1.0) { + n = 1.0; + } + magnitude = n * 65536.0; + } + void offset(float n) { + if (n < -1.0) { + n = -1.0; + } else if (n > 1.0) { + n = 1.0; + } + tone_offset = n * 32767.0; + } + void pulseWidth(float n) { // 0.0 to 1.0 + if (n < 0) { + n = 0; + } else if (n > 1.0) { + n = 1.0; + } + pulse_width = n * 4294967296.0; + } + void waveform(short t_type) { + phase_offset = 0; + tone_type = t_type; + } + void begin(short t_type) { + phase_offset = 0; + tone_type = t_type; + } + void begin(float t_amp, float t_freq, short t_type) { + amplitude(t_amp); + frequency(t_freq); + phase_offset = 0; + tone_type = t_type; + } + void arbitraryWaveform(const int16_t *data, float maxFreq) { + arbdata = data; + } + virtual void update(void); + + private: + uint32_t phase_accumulator; + uint32_t phase_increment; + uint32_t phase_offset; + int32_t magnitude; + uint32_t pulse_width; + const int16_t *arbdata; + int16_t sample; // for WAVEFORM_SAMPLE_HOLD + short tone_type; + int16_t tone_offset; +}; + + +class AudioSynthWaveformExtendedModulated : public AudioStream +{ + public: + AudioSynthWaveformExtendedModulated(void) : AudioStream(2, inputQueueArray), + phase_accumulator(0), phase_increment(0), modulation_factor(32768), + magnitude(0), arbdata(NULL), sample(0), tone_offset(0), + tone_type(WAVEFORM_SINE), modulation_type(0) { + } + + void frequency(float freq) { + if (freq < 0.0) { + freq = 0.0; + } else if (freq > AUDIO_SAMPLE_RATE_EXACT / 2) { + freq = AUDIO_SAMPLE_RATE_EXACT / 2; + } + phase_increment = freq * (4294967296.0 / AUDIO_SAMPLE_RATE_EXACT); + if (phase_increment > 0x7FFE0000u) phase_increment = 0x7FFE0000; + } + void amplitude(float n) { // 0 to 1.0 + if (n < 0) { + n = 0; + } else if (n > 1.0) { + n = 1.0; + } + magnitude = n * 65536.0; + } + void offset(float n) { + if (n < -1.0) { + n = -1.0; + } else if (n > 1.0) { + n = 1.0; + } + tone_offset = n * 32767.0; + } + void waveform(short t_type) { + tone_type = t_type; + } + void begin(short t_type) { + tone_type = t_type; + } + void begin(float t_amp, float t_freq, short t_type) { + amplitude(t_amp); + frequency(t_freq); + tone_type = t_type; + } + void arbitraryWaveform(const int16_t *data, float maxFreq) { + arbdata = data; + } + void frequencyModulation(float octaves) { + if (octaves > 12.0) { + octaves = 12.0; + } else if (octaves < 0.1) { + octaves = 0.1; + } + modulation_factor = octaves * 4096.0; + modulation_type = 0; + } + void phaseModulation(float degrees) { + if (degrees > 9000.0) { + degrees = 9000.0; + } else if (degrees < 30.0) { + degrees = 30.0; + } + modulation_factor = degrees * (65536.0 / 180.0); + modulation_type = 1; + } + virtual void update(void); + + private: + audio_block_t *inputQueueArray[2]; + uint32_t phase_accumulator; + uint32_t phase_increment; + uint32_t modulation_factor; + int32_t magnitude; + const int16_t *arbdata; + uint32_t phasedata[AUDIO_BLOCK_SAMPLES]; + int16_t sample; // for WAVEFORM_SAMPLE_HOLD + int16_t tone_offset; + uint8_t tone_type; + uint8_t modulation_type; +}; + + +#endif