From 458b9eb891f6be4be869fb84fe25ccc133de50a0 Mon Sep 17 00:00:00 2001 From: Neale Pickett Date: Wed, 11 Nov 2020 16:29:09 -0700 Subject: [PATCH] Working on teensy 4.0 + PAJ7620 --- synth_waveform.cpp | 249 --------------------------------------------- synth_waveform.h | 121 ---------------------- uilleann.ino | 173 ++++++++++++++++++------------- 3 files changed, 101 insertions(+), 442 deletions(-) delete mode 100644 synth_waveform.cpp delete mode 100644 synth_waveform.h diff --git a/synth_waveform.cpp b/synth_waveform.cpp deleted file mode 100644 index c0cf6dc..0000000 --- a/synth_waveform.cpp +++ /dev/null @@ -1,249 +0,0 @@ -/* 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. - */ - -#if defined(ADAFRUIT_TRELLIS_M4_EXPRESS) - -#include -#include "synth_waveform.h" -#include "arm_math.h" -#include "utility/dspinst.h" - -void AudioSynthWaveformModulated::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); -} - -#endif diff --git a/synth_waveform.h b/synth_waveform.h deleted file mode 100644 index e206807..0000000 --- a/synth_waveform.h +++ /dev/null @@ -1,121 +0,0 @@ -/** Backport modulated waveform to Adafruit fork - */ - - #pragma once - -/* 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. - */ - -#if defined(ADAFRUIT_TRELLIS_M4_EXPRESS) - -#include -#include -#include "AudioStream.h" -#include "arm_math.h" - -#define WAVEFORM_TRIANGLE_VARIABLE 8 - -class AudioSynthWaveformModulated : public AudioStream -{ -public: - AudioSynthWaveformModulated(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 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 diff --git a/uilleann.ino b/uilleann.ino index b3557f3..1fd2fe1 100644 --- a/uilleann.ino +++ b/uilleann.ino @@ -1,38 +1,37 @@ #include #include -#include -#include +#include +#include #include #include +#include #include "synth.h" #include "patches.h" #include "notes.h" #include "fingering.h" +#define DRONES #define DEBUG -#define KNEE_OFFSET 0 #define KEY_OFFSET 2 FMVoice Chanter; FMVoice Drones[3]; FMVoice Regulators[3]; -AudioFilterBiquad biquad1; -AudioMixer4 mixDrones; -AudioMixer4 mixRegulators; -AudioMixer4 mixL; -AudioMixer4 mixR; -AudioOutputAnalogStereo dacs1; - -#ifdef DEBUG -AudioSynthNoiseWhite debug; -#endif +AudioFilterBiquad biquad1; +AudioMixer4 mixDrones; +AudioMixer4 mixRegulators; +AudioMixer4 mixL; +AudioMixer4 mixR; +AudioOutputI2S out1; +AudioSynthNoiseWhite noise; AudioConnection FMVoicePatchCords[] = { -#ifdef DEBUG - {debug, 0, mixL, 3}, - {debug, 0, mixR, 3}, -#endif + //{0, 0, 0, 0}, // For some reason, the first one is ignored + + {noise, 0, mixDrones, 3}, + {noise, 0, mixL, 3}, + {noise, 0, mixR, 3}, {Chanter.outputMixer, 0, biquad1, 0}, {biquad1, 0, mixL, 0}, @@ -50,56 +49,71 @@ AudioConnection FMVoicePatchCords[] = { {mixRegulators, 0, mixL, 2}, {mixRegulators, 0, mixR, 2}, - {mixL, 0, dacs1, 0}, - {mixR, 0, dacs1, 1}, + {mixL, 0, out1, 0}, + {mixR, 0, out1, 1}, FMVoiceWiring(Chanter), FMVoiceWiring(Drones[0]), FMVoiceWiring(Drones[1]), FMVoiceWiring(Drones[2]), - FMVoiceWiring(Drones[3]), FMVoiceWiring(Regulators[0]), FMVoiceWiring(Regulators[1]), FMVoiceWiring(Regulators[2]), - FMVoiceWiring(Regulators[3]), }; int currentPatch = 0; Adafruit_MPR121 cap = Adafruit_MPR121(); -Adafruit_NeoTrellisM4 trellis = Adafruit_NeoTrellisM4(); -MicroOLED oled(9, 1); +Adafruit_SSD1306 display(128, 32, &Wire, -1); QwiicButton bag; +bool use_bag; + +void blink(bool forever) { + for (;;) { + digitalWrite(LED_BUILTIN, true); + delay(200); + digitalWrite(LED_BUILTIN, false); + delay(200); + if (! forever) break; + } +} void setup() { - setupJustPitches(NOTE_D4, PITCH_D4); pinMode(LED_BUILTIN, OUTPUT); + digitalWrite(LED_BUILTIN, true); + + setupJustPitches(NOTE_D4, PITCH_D4); // Wire.begin needs a moment delay(100); Wire.begin(); - // Initialize OLED display - oled.begin(); - oled.clear(ALL); + // Initialize gesture/proximity sensor + if (paj7620Init()) { + // XXX: Error handling + } + + // Initialize display display + if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3c)) { + blink(true); + } + display.clearDisplay(); + display.setTextSize(1); + display.setTextColor(SSD1306_WHITE); + display.print("Starting"); + display.display(); // Initialize bag bag.begin(); - - // Initialize the Trellis - trellis.begin(); + use_bag = bag.isConnected(); // Initialize touch sensor - bool blink = true; while (!cap.begin(0x5A)) { - oled.clear(PAGE); - oled.setCursor(0, 0); - oled.print("No Pipe?"); - oled.display(); - - trellis.setPixelColor(0, blink?0xff6666:0); - blink = !blink; - delay(200); + display.clearDisplay(); + display.setCursor(0, 0); + display.print("Pipe?"); + display.display(); + blink(false); } // Set aside some memory for the audio library @@ -125,17 +139,12 @@ void setup() { } #ifdef DEBUG - debug.amplitude(0.1); + noise.amplitude(0.1); mixL.gain(3, 0.1); mixR.gain(3, 0.1); #endif } -#define BUTTON_UP 0 -#define BUTTON_DOWN 8 -#define BUTTON_PITCH 24 -#define BUTTON_VOLUME 25 - #define INIT_PITCH_ADJUST 0 #define INIT_GAIN 0.7 #define INIT_PATCH 0 @@ -162,7 +171,6 @@ void updateTunables(uint8_t buttons, int note) { float adj = pow(2, pitchAdjust / 32768.0); setupJustPitches(NOTE_D4, PITCH_D4*adj); - trellis.setPixelColor(BUTTON_PITCH, trellis.ColorHSV(uint16_t(pitchAdjust), 255, 80)); if (!note || (note == NOTE_G4)) { // Volume adjust if playing G @@ -178,20 +186,14 @@ void updateTunables(uint8_t buttons, int note) { break; } } - for (int i=0; i<3; i++) { mixL.gain(i, chanterGain); mixR.gain(i, chanterGain); } - trellis.setPixelColor(BUTTON_VOLUME, trellis.ColorHSV(uint16_t(chanterGain * 65535), 255, 80)); if (!note || (note == NOTE_CS5)) { if (buttons == 3) { patch = INIT_PATCH; - } else if (trellis.justPressed(BUTTON_DOWN)) { - patch -= 1; - } else if (trellis.justPressed(BUTTON_UP)) { - patch += 1; } // wrap @@ -201,14 +203,13 @@ void updateTunables(uint8_t buttons, int note) { FMPatch *p = &Bank[patch]; FMVoiceLoadPatch(&Chanter, p); - oled.clear(PAGE); - oled.setFontType(0); - oled.setCursor(0, 0); - oled.print(p->name); - oled.setCursor(0, 10); - oled.print("Patch "); - oled.print(patch); - oled.display(); + display.clearDisplay(); + display.setCursor(0, 0); + display.print(p->name); + display.setCursor(0, 10); + display.print("Patch "); + display.print(patch); + display.display(); } } @@ -216,7 +217,8 @@ const uint8_t CLOSEDVAL = 0x30; const uint8_t OPENVAL = 0x70; const uint8_t GLISSANDO_STEPS = OPENVAL - CLOSEDVAL; -bool playing = false; +uint8_t loopno = 0; +uint8_t last_note = 0; void loop() { uint8_t keys = 0; @@ -225,10 +227,15 @@ void loop() { uint8_t glissandoNote; float glissandoOpenness = 0; bool silent = false; - bool knee = cap.filteredData(KNEE_OFFSET) < CLOSEDVAL; - uint8_t buttons = trellis.isPressed(BUTTON_DOWN)?1:0 | trellis.isPressed(BUTTON_UP)?2:0; + uint8_t paj_knee = 127; + bool knee = false; - trellis.tick(); + loopno++; + + paj7620ReadReg(0x6c, 1, &paj_knee); + if (paj_knee > 240) { + knee = true; + } for (int i = 0; i < 8; i++) { uint16_t val = max(cap.filteredData(i+KEY_OFFSET), CLOSEDVAL); @@ -246,9 +253,6 @@ void loop() { } } - // print key states - //trellis.setPixelColor(7 - i, trellis.ColorHSV(65536/12, 255, 120*openness)); - trellis.setPixelColor(7 - i, trellis.ColorHSV(22222*openness, 255, 40)); } note = uilleann_matrix[keys]; @@ -265,20 +269,37 @@ void loop() { silent = true; } } + // Look up the note name + char *note_name = NoteNames[note % 12]; + if (silent) { + note_name = "-"; + } // Jump octave if the bag is squished //bag = !digitalRead(BAG); - if (bag.isPressed()) { + if (use_bag && bag.isPressed()) { if (keys & bit(7)) { note += 12; glissandoNote += 12; } } - // Read some trellis button states - if (buttons) { - updateTunables(buttons, note); - } +#if 0 + display.clearDisplay(); + display.setCursor(0, 0); + display.print("mem: "); + display.print(AudioMemoryUsageMax()); + display.print(" prx: "); + display.print(paj_knee); + display.setCursor(0, 24); + display.print("Note: "); + display.print(note); + display.print(" n: "); + display.print(loopno); + display.display(); + return; +#endif + if (silent) { FMVoiceNoteOff(&Chanter); @@ -305,4 +326,12 @@ void loop() { FMVoiceNoteOn(&Chanter, pitch); } } + + if (note != last_note) { + display.clearDisplay(); + display.setCursor(0, 0); + display.print(note_name); + display.display(); + last_note = note; + } }