aboutsummaryrefslogtreecommitdiff
path: root/esp32/lego-piano.ino
diff options
context:
space:
mode:
Diffstat (limited to 'esp32/lego-piano.ino')
-rw-r--r--esp32/lego-piano.ino142
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