// furby sound sampler audio (test) with OLED display // playing a sample from memory in "furby.h" generated using wav2c // used example sketches / libraries from the PicoW set: // I2S -> simpletone // PWMaudio -> PlayRaw // and from external libraries: // Adafruit SSD1306 -> 128x64 I2C #include // arduino core / framework #include // not used but necessary for other displays in the SSD1306 lib #include // the I2C communication lib for the display #include // graphics, drawing functions (sprites, lines) #include // display driver #include // cp process to play audio on PWM pin #include "furby.h" // converted furby sample data (using Wav2c) #include Servo eyelid; Servo beak; #include // Audio output through I2S: Create the I2S port using a PIO state machine I2S i2s(OUTPUT); // GPIO pin numbers #define pBCLK 17 #define pWS (pBCLK + 1) #define pDOUT 16 const int sampleRate = 22050; // minimum for UDA1334A volatile int A = 0; volatile int B = 0; Adafruit_SSD1306 display = Adafruit_SSD1306(128, 32, &Wire); // The sample pointers const int16_t *start = (const int16_t *)out_raw; const int16_t *p = start; // the real sample value pointer // Create the PWM audio device on pin A3 (internal buzzer on Expansion board) PWMAudio pwm(19); unsigned int count = 0; // check where in the sample you are, human readable volatile int hue = 0; // to cycle through LED colours (and misused as frequency setting) volatile int gate = 0; void cb() { // thread process to do audio update regularly while (pwm.availableForWrite()) { if (gate > 0) { i2s.write(*p); // write sample to left channel i2s.write(*p); // write sample to right channel } pwm.write(*p++); count += 2; // was 2 if (count >= B) { count = A; p = start + A / 2; //pwm.setFrequency(22050 + hue * 120); // } } } } void setup() { // first hardware and pins pinMode(LED_BUILTIN, OUTPUT); pinMode(7, INPUT_PULLUP); pinMode(8, INPUT_PULLUP); pinMode(9, INPUT_PULLUP); // for the debug probe we use a three-pin socket for GP0 and GP1 Serial1.begin(115200); // The display uses a standard I2C, on I2C 0, so no changes or pin-assignments necessary display.begin(SSD1306_SWITCHCAPVCC, 0x3C); // Address 0x3C for 128x32 display.clearDisplay(); // start the screen // we use the PWM audio out using a call-back function, so we can use the same // function also to play samples on I2S pwm.onTransmit(cb); // start the audio playback pwm.begin(22050); // set the playback frequency // pin assignment for the I2S i2s.setBCLK(pBCLK); i2s.setDATA(pDOUT); i2s.setBitsPerSample(16); if (!i2s.begin(sampleRate)) { Serial.println("Failed to initialize I2S!"); while (1) ; // do nothing } // now setting two globals (ugly, yes...) for playback: the A and B marker A = 0; B = sounddata_length; eyelid.attach(21); beak.attach(22); } void loop() { static unsigned long looptime; static int mod = 0; static int cv = 0; static int wake = 0; if (millis() > looptime + 49) { // run this code at 20Hz looptime = millis(); cv = analogRead(A0); mod = map(analogRead(A2), 10, 1023, sounddata_length, 10); A = sounddata_length * cv / 1023.0; B = A + mod; if (B > sounddata_length-1000) B = sounddata_length-1000; if (B < A) B = A + 5; if (analogRead(A1) < 100 && gate == 0) { gate = 1; count = A; p = start + A / 2; if(!eyelid.attached()) eyelid.attach(21); } else if (analogRead(A1) > 512 && gate == 1) { gate = 0; } if(gate) wake+=30; else wake-=10; if(wake>300) wake = 300; if(wake<0) wake = 0; if(wake==0) if(eyelid.attached())eyelid.detach(); beak.write(map(gate,0,1,50,130)); eyelid.write(map(wake,0,300,20,160)); // debug info Serial1.print(analogRead(A0)); Serial1.print(','); Serial1.print(gate); Serial1.print(','); Serial1.println(analogRead(A2)); // now build up the graphics display.clearDisplay(); for (int i = 0; i < display.width(); i++) { // this is for building up a sound wave picture int p = sounddata_length / display.width(); // scale the data to the full screen width display.drawLine(i, display.height() / 2 + out_raw[i * p] / 4, i, display.height() / 2 - out_raw[i * p] / 4, SSD1306_WHITE); } int cursor = display.width() * count / sounddata_length; display.drawLine(cursor, 4, cursor, display.height() - 4, SSD1306_WHITE); int cursorA = display.width() * A / sounddata_length; int cursorB = display.width() * B / sounddata_length; display.drawLine(cursorA, 0, cursorA, display.height(), SSD1306_WHITE); display.fillRect(cursorA, 0, 4, 4, SSD1306_WHITE); display.fillRect(cursorB, 0, 4, 4, SSD1306_WHITE); display.drawLine(cursorB, 0, cursorB, display.height(), SSD1306_WHITE); display.display(); // Update screen with each newly-drawn line } }