diff options
Diffstat (limited to 'esp32/lego-piano.ino')
-rw-r--r-- | esp32/lego-piano.ino | 142 |
1 files changed, 85 insertions, 57 deletions
diff --git a/esp32/lego-piano.ino b/esp32/lego-piano.ino index 575f55e..01b3bb7 100644 --- a/esp32/lego-piano.ino +++ b/esp32/lego-piano.ino @@ -17,9 +17,16 @@ */ #include <driver/dac.h> +#include <driver/i2s.h> + +#define TSF_IMPLEMENTATION +#define TSF_NO_STDIO +#include "../ESP8266Audio/src/libtinysoundfont/tsf.h" + +#include "../RingBuffer/src/RingBuf.h" +#include "font.h" int currentLed = 0; -int lastSeen = -1; #define DEBUG 0 @@ -33,15 +40,36 @@ const int ampEnable = 32; const int octave_shift = 2; +const int sampleRate = 11025; + +char pressed[row_count*col_count] = { 0 }; + +tsf* g_TinySoundFont = 0; +const i2s_port_t i2sPort = I2S_NUM_0; + void setup() { Serial.begin(115200); pinMode(ampEnable, OUTPUT); digitalWrite(ampEnable, LOW); - dac_i2s_disable(); + dac_i2s_enable(); dac_output_enable(DAC_CHANNEL_1); + static const i2s_config_t i2s_config = { + .mode = (i2s_mode_t)( I2S_MODE_MASTER | I2S_MODE_TX | I2S_MODE_DAC_BUILT_IN ), + .sample_rate = sampleRate, + .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT, + .channel_format = I2S_CHANNEL_FMT_ALL_RIGHT, + .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1, // high interrupt priority + .dma_buf_count = 8, + .dma_buf_len = 64, + .use_apll = false + }; + + i2s_driver_install(i2sPort, &i2s_config, 0, NULL); + i2s_set_dac_mode(I2S_DAC_CHANNEL_RIGHT_EN); // only dac1 + for (int i=0;i<row_count;++i) { pinMode(rows[i], OUTPUT|PULLUP); pinMode(adc[i], INPUT); @@ -51,29 +79,19 @@ void setup() { } currentLed = 0; - lastSeen = -1; -} - -void play(uint32_t freq) { - dac_cw_config_t wave_config = { - en_ch: DAC_CHANNEL_1, - scale: DAC_CW_SCALE_2, - phase: DAC_CW_PHASE_0, - freq: octave_shift*freq, - offset: 0, - }; - dac_cw_generator_config(&wave_config); - dac_cw_generator_enable(); + g_TinySoundFont = tsf_load_memory(SoundFont, sizeof(SoundFont)); + if (g_TinySoundFont) { + // render at 5kHz + tsf_set_output(g_TinySoundFont, TSF_MONO, sampleRate, 0); + } + else { + Serial.println("failed to start tsf"); + } digitalWrite(ampEnable, HIGH); } -void mute() { - digitalWrite(ampEnable, LOW); - dac_cw_generator_disable(); -} - void tristate(int pin) { pinMode(pin,OUTPUT|PULLDOWN); digitalWrite(pin,LOW); @@ -123,35 +141,9 @@ int sense(int led) { return value < 500; } -uint32_t notes[] = { - 261, // C4 - 277, - 294, // D4 - 311, - 330, // E4 - 349, // F4 - 370, - 392, // G4 - 415, - 440, // A4 - 466, - 494, // B4 - - 523, // C5 - 554, - 587, // D5 - 622, - 659, // E5 - 698, // F5 - 740, - 783, // G5 - 831, - 880, // A5 - 932, - 988, // B5 - - 1046, // C6 -}; +const size_t bufferSize = 1000; +RingBuf<short, bufferSize> buffer; +short intermediateBuffer[bufferSize]; void loop() { #if DEBUG & 0x01 @@ -160,19 +152,55 @@ void loop() { #endif enableLed(currentLed); - delay(5); - if (sense(currentLed)) { - if (lastSeen != currentLed) { - lastSeen = currentLed; + unsigned long t = millis(); + size_t written=100; + short value; + // fill the I2S buffer + while (written > 0) { + // we don't want to drop an element from the buffer if we then + // can't write it! so we peek at the buffer, try to write, and pop + // if successful + value=buffer[0] + 32768; // tsf produces *signed* shorts, i2s wants *unsigned* + i2s_write(i2sPort, &value, sizeof(value), &written, 0); + if (written > 0) buffer.pop(value); + } + + int toFill = buffer.maxSize() - buffer.size(); + if (toFill > 100) { + tsf_render_short(g_TinySoundFont, intermediateBuffer, toFill, 0); + for (int i=0;i<toFill;++i) { + buffer.push(intermediateBuffer[i]); + } + } + + while (millis() == t) { + delayMicroseconds(100); + } + + if (sense(currentLed)) { // currently pressed + if (!pressed[currentLed]) { // was not pressed previously? Serial.print(currentLed); Serial.println(" proximity!"); - play(notes[currentLed]); + + pressed[currentLed]=1; // mark it pressed + if (g_TinySoundFont) { + // start the note + tsf_note_on(g_TinySoundFont, 0, 36 + currentLed, 1.0f); + } } } - else if (lastSeen == currentLed) { - lastSeen = -1; - mute(); + // not pressed currently + else if (pressed[currentLed]) { // was pressed previously? + Serial.print(currentLed); + Serial.println(" released!"); + + pressed[currentLed]=0; // mark it no longer pressed + if (g_TinySoundFont) { + // stop the note + tsf_note_off(g_TinySoundFont, 0, 36 + currentLed); + } } currentLed = (currentLed+1)%(row_count*col_count); + }
\ No newline at end of file |