diff --git a/AdaVoice/README.md b/AdaVoice/README.md new file mode 100644 index 000000000..f54bc83af --- /dev/null +++ b/AdaVoice/README.md @@ -0,0 +1,25 @@ +Adafruit Voice Changer + Effects Box +======== + +Build your own custom voice changer and effects box. +Perfect for a costume, toy, art project, annoying the guy +in the next cubicle, etc. + +Check out the full tutorial at http://learn.adafruit.com/wave-shield-voice-changer + +To build this project you'll need: + + + * Adafruit Microphone Amp - http://www.adafruit.com/products/1063 + * Arduino Uno - http://www.adafruit.com/products/50 + * Adafruit Wave Shield Pack - http://www.adafruit.com/products/175 +(Comes with wave shield, SD card, Speaker, wire) + * Matrix Keypad - http://www.adafruit.com/products/419 + * 10K potentiometer - http://www.adafruit.com/products/562 + + +For portable use, a 6 x AA battery pack will last for many many hours + * http://www.adafruit.com/products/248 + +If you have a bigger speaker you want to power, check out our 3.7W +Class D amplifier board. http://www.adafruit.com/products/987 diff --git a/AdaVoice/adavoice/adavoice.ino b/AdaVoice/adavoice/adavoice.ino new file mode 100644 index 000000000..108d9139e --- /dev/null +++ b/AdaVoice/adavoice/adavoice.ino @@ -0,0 +1,346 @@ +/* +ADAVOICE is an Arduino-based voice pitch changer plus WAV playback. +Fun for Halloween costumes, comic convention getups and other shenanigans! + +Hardware requirements: + - Arduino Uno, Duemilanove or Diecimila (not Mega or Leonardo compatible). + - Adafruit Wave Shield + - Speaker attached to Wave Shield output + - Battery for portable use +If using the voice pitch changer, you will also need: + - Adafruit Microphone Breakout + - 10K potentiometer for setting pitch (or hardcode in sketch) +If using the WAV playback, you will also need: + - SD card + - Keypad, buttons or other sensor(s) for triggering sounds +Software requirements: + - WaveHC library for Arduino + - Demo WAV files on FAT-formatted SD card + +This example sketch uses a 3x4 keypad for triggering sounds...but with +some changes could be adapted to use several discrete buttons, Hall effect +sensors, force-sensing resistors (FSRs), I2C keypads, etc. (or if you just +want the voice effect, no buttons at all). + +Connections: + - 3.3V to mic amp+, 1 leg of potentiometer and Arduino AREF pin + - GND to mic amp-, opposite leg of potentiometer + - Analog pin 0 to mic amp output + - Analog pin 1 to center tap of potentiometer + - Wave Shield output to speaker or amplifier + - Matrix is wired to pins A2, A3, A4, A5 (rows) and 6, 7, 8 (columns) + - Wave shield is assumed wired as in product tutorial + +Potentiometer sets playback pitch. Pitch adjustment does NOT work in +realtime -- audio sampling requires 100% of the ADC. Pitch setting is +read at startup (or reset) and after a WAV finishes playing. + +POINT SPEAKER AWAY FROM MIC to avoid feedback. + +Written by Adafruit industries, with portions adapted from the +'PiSpeakHC' sketch included with WaveHC library. +*/ + +#include +#include + +SdReader card; // This object holds the information for the card +FatVolume vol; // This holds the information for the partition on the card +FatReader root; // This holds the information for the volumes root directory +FatReader file; // This object represent the WAV file for a pi digit or period +WaveHC wave; // This is the only wave (audio) object, -- we only play one at a time +#define error(msg) error_P(PSTR(msg)) // Macro allows error messages in flash memory + +#define ADC_CHANNEL 0 // Microphone on Analog pin 0 + +// Wave shield DAC: digital pins 2, 3, 4, 5 +#define DAC_CS_PORT PORTD +#define DAC_CS PORTD2 +#define DAC_CLK_PORT PORTD +#define DAC_CLK PORTD3 +#define DAC_DI_PORT PORTD +#define DAC_DI PORTD4 +#define DAC_LATCH_PORT PORTD +#define DAC_LATCH PORTD5 + +uint16_t in = 0, out = 0, xf = 0, nSamples; // Audio sample counters +uint8_t adc_save; // Default ADC mode + +// WaveHC didn't declare it's working buffers private or static, +// so we can be sneaky and borrow the same RAM for audio sampling! +extern uint8_t + buffer1[PLAYBUFFLEN], // Audio sample LSB + buffer2[PLAYBUFFLEN]; // Audio sample MSB +#define XFADE 16 // Number of samples for cross-fade +#define MAX_SAMPLES (PLAYBUFFLEN - XFADE) // Remaining available audio samples + +// Keypad information: +uint8_t + rows[] = { A2, A3, A4, A5 }, // Keypad rows connect to these pins + cols[] = { 6, 7, 8 }, // Keypad columns connect to these pins + r = 0, // Current row being examined + prev = 255, // Previous key reading (or 255 if none) + count = 0; // Counter for button debouncing +#define DEBOUNCE 10 // Number of iterations before button 'takes' + +// Keypad/WAV information. Number of elements here should match the +// number of keypad rows times the number of columns, plus one: +const char *sound[] = { + "breath" , "destroy", "saber" , // Row 1 = Darth Vader sounds + "zilla" , "crunch" , "burp" , // Row 2 = Godzilla sounds + "hithere", "smell" , "squirrel", // Row 3 = Dug the dog sounds + "carhorn", "foghorn", "door" , // Row 4 = Cartoon/SFX sound + "startup" }; // Extra item = boot sound + + +//////////////////////////////////// SETUP + +void setup() { + uint8_t i; + + Serial.begin(9600); + + // The WaveHC library normally initializes the DAC pins...but only after + // an SD card is detected and a valid file is passed. Need to init the + // pins manually here so that voice FX works even without a card. + pinMode(2, OUTPUT); // Chip select + pinMode(3, OUTPUT); // Serial clock + pinMode(4, OUTPUT); // Serial data + pinMode(5, OUTPUT); // Latch + digitalWrite(2, HIGH); // Set chip select high + + // Init SD library, show root directory. Note that errors are displayed + // but NOT regarded as fatal -- the program will continue with voice FX! + if(!card.init()) SerialPrint_P("Card init. failed!"); + else if(!vol.init(card)) SerialPrint_P("No partition!"); + else if(!root.openRoot(vol)) SerialPrint_P("Couldn't open dir"); + else { + PgmPrintln("Files found:"); + root.ls(); + // Play startup sound (last file in array). + playfile(sizeof(sound) / sizeof(sound[0]) - 1); + } + + // Optional, but may make sampling and playback a little smoother: + // Disable Timer0 interrupt. This means delay(), millis() etc. won't + // work. Comment this out if you really, really need those functions. + TIMSK0 = 0; + + // Set up Analog-to-Digital converter: + analogReference(EXTERNAL); // 3.3V to AREF + adc_save = ADCSRA; // Save ADC setting for restore later + + // Set keypad rows to outputs, set to HIGH logic level: + for(i=0; i= DEBOUNCE) { // Yes. Held beyond debounce threshold? + if(wave.isplaying) wave.stop(); // Stop current WAV (if any) + else stopPitchShift(); // or stop voice effect + playfile(button); // and play new sound. + while(digitalRead(cols[c]) == LOW); // Wait for button release. + prev = 255; // Reset debounce values. + count = 0; + } + } else { // Not same button as prior pass. + prev = button; // Record new button and + count = 0; // restart debounce counter. + } + } + } + + // Restore current row to HIGH logic state and advance row counter... + digitalWrite(rows[r], HIGH); + if(++r >= sizeof(rows)) { // If last row scanned... + r = 0; // Reset row counter + // If no new sounds have been triggered at this point, and if the + // pitch-shifter is not running, re-start it... + if(!wave.isplaying && !(TIMSK2 & _BV(TOIE2))) startPitchShift(); + } +} + + +//////////////////////////////////// HELPERS + +// Open and start playing a WAV file +void playfile(int idx) { + char filename[13]; + + (void)sprintf(filename,"%s.wav", sound[idx]); + Serial.print("File: "); + Serial.println(filename); + + if(!file.open(root, filename)) { + PgmPrint("Couldn't open file "); + Serial.print(filename); + return; + } + if(!wave.create(file)) { + PgmPrintln("Not a valid WAV"); + return; + } + wave.play(); +} + + +//////////////////////////////////// PITCH-SHIFT CODE + +void startPitchShift() { + + // Read analog pitch setting before starting audio sampling: + int pitch = analogRead(1); + Serial.print("Pitch: "); + Serial.println(pitch); + + // Right now the sketch just uses a fixed sound buffer length of + // 128 samples. It may be the case that the buffer length should + // vary with pitch for better results...further experimentation + // is required here. + nSamples = 128; + //nSamples = F_CPU / 3200 / OCR2A; // ??? + //if(nSamples > MAX_SAMPLES) nSamples = MAX_SAMPLES; + //else if(nSamples < (XFADE * 2)) nSamples = XFADE * 2; + + memset(buffer1, 0, nSamples + XFADE); // Clear sample buffers + memset(buffer2, 2, nSamples + XFADE); // (set all samples to 512) + + // WaveHC library already defines a Timer1 interrupt handler. Since we + // want to use the stock library and not require a special fork, Timer2 + // is used for a sample-playing interrupt here. As it's only an 8-bit + // timer, a sizeable prescaler is used (32:1) to generate intervals + // spanning the desired range (~4.8 KHz to ~19 KHz, or +/- 1 octave + // from the sampling frequency). This does limit the available number + // of speed 'steps' in between (about 79 total), but seems enough. + TCCR2A = _BV(WGM21) | _BV(WGM20); // Mode 7 (fast PWM), OC2 disconnected + TCCR2B = _BV(WGM22) | _BV(CS21) | _BV(CS20); // 32:1 prescale + OCR2A = map(pitch, 0, 1023, + F_CPU / 32 / (9615 / 2), // Lowest pitch = -1 octave + F_CPU / 32 / (9615 * 2)); // Highest pitch = +1 octave + + // Start up ADC in free-run mode for audio sampling: + DIDR0 |= _BV(ADC0D); // Disable digital input buffer on ADC0 + ADMUX = ADC_CHANNEL; // Channel sel, right-adj, AREF to 3.3V regulator + ADCSRB = 0; // Free-run mode + ADCSRA = _BV(ADEN) | // Enable ADC + _BV(ADSC) | // Start conversions + _BV(ADATE) | // Auto-trigger enable + _BV(ADIE) | // Interrupt enable + _BV(ADPS2) | // 128:1 prescale... + _BV(ADPS1) | // ...yields 125 KHz ADC clock... + _BV(ADPS0); // ...13 cycles/conversion = ~9615 Hz + + TIMSK2 |= _BV(TOIE2); // Enable Timer2 overflow interrupt + sei(); // Enable interrupts +} + +void stopPitchShift() { + ADCSRA = adc_save; // Disable ADC interrupt and allow normal use + TIMSK2 = 0; // Disable Timer2 Interrupt +} + +ISR(ADC_vect, ISR_BLOCK) { // ADC conversion complete + + // Save old sample from 'in' position to xfade buffer: + buffer1[nSamples + xf] = buffer1[in]; + buffer2[nSamples + xf] = buffer2[in]; + if(++xf >= XFADE) xf = 0; + + // Store new value in sample buffers: + buffer1[in] = ADCL; // MUST read ADCL first! + buffer2[in] = ADCH; + if(++in >= nSamples) in = 0; +} + +ISR(TIMER2_OVF_vect) { // Playback interrupt + uint16_t s; + uint8_t w, inv, hi, lo, bit; + int o2, i2, pos; + + // Cross fade around circular buffer 'seam'. + if((o2 = (int)out) == (i2 = (int)in)) { + // Sample positions coincide. Use cross-fade buffer data directly. + pos = nSamples + xf; + hi = (buffer2[pos] << 2) | (buffer1[pos] >> 6); // Expand 10-bit data + lo = (buffer1[pos] << 2) | buffer2[pos]; // to 12 bits + } if((o2 < i2) && (o2 > (i2 - XFADE))) { + // Output sample is close to end of input samples. Cross-fade to + // avoid click. The shift operations here assume that XFADE is 16; + // will need adjustment if that changes. + w = in - out; // Weight of sample (1-n) + inv = XFADE - w; // Weight of xfade + pos = nSamples + ((inv + xf) % XFADE); + s = ((buffer2[out] << 8) | buffer1[out]) * w + + ((buffer2[pos] << 8) | buffer1[pos]) * inv; + hi = s >> 10; // Shift 14 bit result + lo = s >> 2; // down to 12 bits + } else if (o2 > (i2 + nSamples - XFADE)) { + // More cross-fade condition + w = in + nSamples - out; + inv = XFADE - w; + pos = nSamples + ((inv + xf) % XFADE); + s = ((buffer2[out] << 8) | buffer1[out]) * w + + ((buffer2[pos] << 8) | buffer1[pos]) * inv; + hi = s >> 10; // Shift 14 bit result + lo = s >> 2; // down to 12 bits + } else { + // Input and output counters don't coincide -- just use sample directly. + hi = (buffer2[out] << 2) | (buffer1[out] >> 6); // Expand 10-bit data + lo = (buffer1[out] << 2) | buffer2[out]; // to 12 bits + } + + // Might be possible to tweak 'hi' and 'lo' at this point to achieve + // different voice modulations -- robot effect, etc.? + + DAC_CS_PORT &= ~_BV(DAC_CS); // Select DAC + // Clock out 4 bits DAC config (not in loop because it's constant) + DAC_DI_PORT &= ~_BV(DAC_DI); // 0 = Select DAC A, unbuffered + DAC_CLK_PORT |= _BV(DAC_CLK); DAC_CLK_PORT &= ~_BV(DAC_CLK); + DAC_CLK_PORT |= _BV(DAC_CLK); DAC_CLK_PORT &= ~_BV(DAC_CLK); + DAC_DI_PORT |= _BV(DAC_DI); // 1X gain, enable = 1 + DAC_CLK_PORT |= _BV(DAC_CLK); DAC_CLK_PORT &= ~_BV(DAC_CLK); + DAC_CLK_PORT |= _BV(DAC_CLK); DAC_CLK_PORT &= ~_BV(DAC_CLK); + for(bit=0x08; bit; bit>>=1) { // Clock out first 4 bits of data + if(hi & bit) DAC_DI_PORT |= _BV(DAC_DI); + else DAC_DI_PORT &= ~_BV(DAC_DI); + DAC_CLK_PORT |= _BV(DAC_CLK); DAC_CLK_PORT &= ~_BV(DAC_CLK); + } + for(bit=0x80; bit; bit>>=1) { // Clock out last 8 bits of data + if(lo & bit) DAC_DI_PORT |= _BV(DAC_DI); + else DAC_DI_PORT &= ~_BV(DAC_DI); + DAC_CLK_PORT |= _BV(DAC_CLK); DAC_CLK_PORT &= ~_BV(DAC_CLK); + } + DAC_CS_PORT |= _BV(DAC_CS); // Unselect DAC + + if(++out >= nSamples) out = 0; +} + diff --git a/AdaVoice/adavoice_face/adavoice_face.ino b/AdaVoice/adavoice_face/adavoice_face.ino new file mode 100644 index 000000000..9de146445 --- /dev/null +++ b/AdaVoice/adavoice_face/adavoice_face.ino @@ -0,0 +1,479 @@ +/* +This is a mash-up of the adavoice and wavface sketches. +Using an Arduino, Wave Shield and some supporting components, +creates a voice-changing mask with animated features. + +The BOM for the complete project is rather lengthy to put here, so +please see this tutorial for info (and parts) for the voice changer: +http://learn.adafruit.com/wave-shield-voice-changer +And see this tutorial for info/parts for the face animation: +http://learn.adafruit.com/animating-multiple-led-backpacks + +This version doesn't do WAV playback, as there's no keypad or +buttons. Just the voice. Some WAV-related hooks have been left +in place if you want to adapt it. This will be difficult though, +as RAM is VERY tight...we're using nearly the whole thing. +*/ + +#include +#include +#include +#include "Adafruit_LEDBackpack.h" +#include "Adafruit_GFX.h" + +// The SD card stuff is declared but not actually used in this sketch -- +// I wanted to keep the ability for WAVs here, even if they're not being +// exercised right now. RAM is VERY tight with all this in here...so if +// you adapt the sketch and run out of ram, and if you don't need WAV +// playback, I'd start by tearing this stuff out... +SdReader card; // This object holds the information for the card +FatVolume vol; // This holds the information for the partition on the card +FatReader root; // This holds the information for the volumes root directory +FatReader file; // This object represent the WAV file for a pi digit or period +WaveHC wave; // This is the only wave (audio) object, -- we only play one at a time +#define error(msg) error_P(PSTR(msg)) // Macro allows error messages in flash memory + +#define ADC_CHANNEL 0 // Microphone on Analog pin 0 + +// Wave shield DAC: digital pins 2, 3, 4, 5 +#define DAC_CS_PORT PORTD +#define DAC_CS PORTD2 +#define DAC_CLK_PORT PORTD +#define DAC_CLK PORTD3 +#define DAC_DI_PORT PORTD +#define DAC_DI PORTD4 +#define DAC_LATCH_PORT PORTD +#define DAC_LATCH PORTD5 + +uint16_t in = 0, out = 0, xf = 0, nSamples; // Audio sample counters +uint8_t adc_save; // Default ADC mode + +// WaveHC didn't declare it's working buffers private or static, +// so we can be sneaky and borrow the same RAM for audio sampling! +extern uint8_t + buffer1[PLAYBUFFLEN], // Audio sample LSB + buffer2[PLAYBUFFLEN]; // Audio sample MSB +#define XFADE 16 // Number of samples for cross-fade +#define MAX_SAMPLES (PLAYBUFFLEN - XFADE) // Remaining available audio samples + +// Used for averaging all the audio samples currently in the buffer +uint8_t oldsum = 0; +unsigned long newsum = 0L; + +#define MATRIX_EYES 0 +#define MATRIX_MOUTH_LEFT 1 +#define MATRIX_MOUTH_MIDDLE 2 +#define MATRIX_MOUTH_RIGHT 3 + +Adafruit_8x8matrix matrix[4] = { // Array of Adafruit_8x8matrix objects + Adafruit_8x8matrix(), Adafruit_8x8matrix(), + Adafruit_8x8matrix(), Adafruit_8x8matrix() }; + +static const uint8_t PROGMEM // Bitmaps are stored in program memory + blinkImg[][8] = { // Eye animation frames + { B11111100, // Fully open eye + B11111110, // The eye matrices are installed + B11111111, // in the mask at a 45 degree angle... + B11111111, // you can edit these bitmaps if you opt + B11111111, // for a rectilinear arrangement. + B11111111, + B01111111, + B00111111 }, + { B11110000, + B11111100, + B11111110, + B11111110, + B11111111, + B11111111, + B01111111, + B00111111 }, + { B11100000, + B11111000, + B11111100, + B11111110, + B11111110, + B01111111, + B00111111, + B00011111 }, + { B11000000, + B11110000, + B11111000, + B11111100, + B11111110, + B01111110, + B00111111, + B00011111 }, + { B10000000, + B11100000, + B11111000, + B11111100, + B01111100, + B01111110, + B00111110, + B00001111 }, + { B10000000, + B11000000, + B11100000, + B11110000, + B01111000, + B01111100, + B00111110, + B00001111 }, + { B10000000, + B10000000, + B11000000, + B01000000, + B01100000, + B00110000, + B00011100, + B00000111 }, + { B10000000, // Fully closed eye + B10000000, + B10000000, + B01000000, + B01000000, + B00100000, + B00011000, + B00000111 } }, + pupilImg[] = { // Pupil bitmap + B00000000, // (only top-left 7x7 is used) + B00011000, + B00111000, + B01110000, + B01100000, + B00000000, + B00000000, + B00000000 }, + landingImg[] = { // This is a bitmask of where + B01111000, // the pupil can safely "land" when + B11111100, // a new random position is selected, + B11111110, // so it doesn't run too far off the + B11111110, // edge and look bad. If you edit the + B11111110, // eye or pupil bitmaps, you may want + B01111110, // to adjust this...use '1' for valid + B00111100, // pupil positions, '0' for off-limits + B00000000 }, // points. + mouthImg[][24] = { // Mouth animation frames + { B00000000, B11000011, B00000000, // Mouth position 0 + B00000001, B01000010, B10000000, // Unlike the original 'roboface' + B00111110, B01111110, B01111100, // sketch (using AF animation), + B11000000, B00000000, B00000011, // the mouth positions here are + B00000000, B00000000, B00000000, // proportional to the current + B00000000, B00000000, B00000000, // sound amplitude. Position 0 + B00000000, B00000000, B00000000, // is closed, #7 is max open. + B00000000, B00000000, B00000000 }, + { B00000000, B00000000, B00000000, // Mouth position 1 + B00000000, B11000011, B00000000, + B00000011, B01111110, B11000000, + B00111110, B00000000, B01111100, + B11000000, B00000000, B00000011, + B00000000, B00000000, B00000000, + B00000000, B00000000, B00000000, + B00000000, B00000000, B00000000 }, + { B00000000, B00000000, B00000000, // Mouth position 2 + B00000011, B11111111, B11000000, + B00011011, B01111110, B11011000, + B01111110, B01111110, B01111110, + B00000000, B00000000, B00000000, + B00000000, B00000000, B00000000, + B00000000, B00000000, B00000000, + B00000000, B00000000, B00000000 }, + { B00000000, B00000000, B00000000, // Mouth position 3 + B00000011, B11111111, B11000000, + B01111011, B11111111, B11011110, + B00011111, B01111110, B11111000, + B00001110, B01111110, B01110000, + B00000000, B00000000, B00000000, + B00000000, B00000000, B00000000, + B00000000, B00000000, B00000000 }, + { B00000000, B00000000, B00000000, // Mouth position 4 + B00000001, B11111111, B10000000, + B01111001, B11111111, B10011110, + B00111101, B11111111, B10111100, + B00011111, B01111110, B11111000, + B00000110, B01111110, B01100000, + B00000000, B00000000, B00000000, + B00000000, B00000000, B00000000 }, + { B00000000, B00000000, B00000000, // Mouth position 5 + B00000001, B11111111, B10000000, + B00111001, B11111111, B10011100, + B00011101, B11111111, B10111000, + B00011111, B11111111, B11111000, + B00001111, B01111110, B11110000, + B00000110, B01111110, B01100000, + B00000000, B00000000, B00000000 }, + { B00000000, B00000000, B00000000, // Mouth position 6 + B00000001, B11111111, B10000000, + B00111001, B11111111, B10011100, + B00111101, B11111111, B10111100, + B00011111, B11111111, B11111100, + B00011111, B11111111, B11111000, + B00001111, B01111110, B11110000, + B00000110, B01111110, B01100000 }, + { B00000000, B01111110, B00000000, // Mouth position 7 + B00001001, B11111111, B10010000, + B00011001, B11111111, B10011000, + B00011101, B11111111, B10111000, + B00011111, B11111111, B11111000, + B00011111, B11111111, B11111000, + B00001111, B01111110, B11110000, + B00000110, B01111110, B01100000 } }, + blinkIndex[] = { 1, 2, 3, 4, 5, 6, 7, 6, 5, 3, 2, 1 }, // Blink bitmap sequence + matrixAddr[] = { 0x70, 0x71, 0x72, 0x73 }; // I2C addresses of 4 matrices + +uint8_t + blinkCountdown = 100, // Countdown to next blink (in frames) + gazeCountdown = 75, // Countdown to next eye movement + gazeFrames = 50, // Duration of eye movement (smaller = faster) + mouthPos = 0, // Current image number for mouth + mouthCountdown = 10; // Countdown to next mouth change +int8_t + eyeX = 3, eyeY = 3, // Current eye position + newX = 3, newY = 3, // Next eye position + dX = 0, dY = 0; // Distance from prior to new position + + +//////////////////////////////////// SETUP + +void setup() { + uint8_t i; + + Serial.begin(9600); + + // Seed random number generator from an unused analog input: + randomSeed(analogRead(A2)); + + // Initialize each matrix object: + for(i=0; i<4; i++) { + matrix[i].begin(pgm_read_byte(&matrixAddr[i])); + } + + // The WaveHC library normally initializes the DAC pins...but only after + // an SD card is detected and a valid file is passed. Need to init the + // pins manually here so that voice FX works even without a card. + pinMode(2, OUTPUT); // Chip select + pinMode(3, OUTPUT); // Serial clock + pinMode(4, OUTPUT); // Serial data + pinMode(5, OUTPUT); // Latch + digitalWrite(2, HIGH); // Set chip select high + + // Init SD library, show root directory. Note that errors are displayed + // but NOT regarded as fatal -- the program will continue with voice FX! + if(!card.init()) SerialPrint_P("Card init. failed!"); + else if(!vol.init(card)) SerialPrint_P("No partition!"); + else if(!root.openRoot(vol)) SerialPrint_P("Couldn't open dir"); + else { + PgmPrintln("Files found:"); + root.ls(); + } + + // Optional, but may make sampling and playback a little smoother: + // Disable Timer0 interrupt. This means delay(), millis() etc. won't + // work. Comment this out if you really, really need those functions. + TIMSK0 = 0; + + // Set up Analog-to-Digital converter: + analogReference(EXTERNAL); // 3.3V to AREF + adc_save = ADCSRA; // Save ADC setting for restore later + + startPitchShift(); // and start the pitch-shift mode by default. +} + + +//////////////////////////////////// LOOP + +void loop() { + // Draw eyeball in current state of blinkyness (no pupil). Note that + // only one eye needs to be drawn. Because the two eye matrices share + // the same address, the same data will be received by both. + matrix[MATRIX_EYES].clear(); + // When counting down to the next blink, show the eye in the fully- + // open state. On the last few counts (during the blink), look up + // the corresponding bitmap index. + matrix[MATRIX_EYES].drawBitmap(0, 0, + blinkImg[ + (blinkCountdown < sizeof(blinkIndex)) ? // Currently blinking? + pgm_read_byte(&blinkIndex[blinkCountdown]) : // Yes, look up bitmap # + 0 // No, show bitmap 0 + ], 8, 8, LED_ON); + // Decrement blink counter. At end, set random time for next blink. + if(--blinkCountdown == 0) blinkCountdown = random(5, 180); + + // Add a pupil atop the blinky eyeball bitmap. + // Periodically, the pupil moves to a new position... + if(--gazeCountdown <= gazeFrames) { + // Eyes are in motion - draw pupil at interim position + matrix[MATRIX_EYES].drawBitmap( + newX - (dX * gazeCountdown / gazeFrames) - 2, + newY - (dY * gazeCountdown / gazeFrames) - 2, + pupilImg, 7, 7, LED_OFF); + if(gazeCountdown == 0) { // Last frame? + eyeX = newX; eyeY = newY; // Yes. What's new is old, then... + do { // Pick random positions until one is within the eye circle + newX = random(7); newY = random(7); + } while(!((pgm_read_byte(&landingImg[newY]) << newX) & 0x80)); + dX = newX - eyeX; // Horizontal distance to move + dY = newY - eyeY; // Vertical distance to move + gazeFrames = random(3, 15); // Duration of eye movement + gazeCountdown = random(gazeFrames, 120); // Count to end of next movement + } + } else { + // Not in motion yet -- draw pupil at current static position + matrix[MATRIX_EYES].drawBitmap(eyeX - 2, eyeY - 2, pupilImg, 7, 7, LED_OFF); + } + + drawMouth(mouthImg[oldsum / 32]); + + // Refresh all of the matrices in one quick pass + for(uint8_t i=0; i<4; i++) matrix[i].writeDisplay(); + + delay(20); // ~50 FPS +} + +// Draw mouth image across three adjacent displays +void drawMouth(const uint8_t *img) { + for(uint8_t i=0; i<3; i++) { + matrix[MATRIX_MOUTH_LEFT + i].clear(); + matrix[MATRIX_MOUTH_LEFT + i].drawBitmap(i * -8, 0, img, 24, 8, LED_ON); + } +} + + +//////////////////////////////////// PITCH-SHIFT CODE + +void startPitchShift() { + + // Read analog pitch setting before starting audio sampling: + int pitch = analogRead(1); + Serial.print("Pitch: "); + Serial.println(pitch); + + // Right now the sketch just uses a fixed sound buffer length of + // 128 samples. It may be the case that the buffer length should + // vary with pitch for better results...further experimentation + // is required here. + nSamples = 128; + //nSamples = F_CPU / 3200 / OCR2A; // ??? + //if(nSamples > MAX_SAMPLES) nSamples = MAX_SAMPLES; + //else if(nSamples < (XFADE * 2)) nSamples = XFADE * 2; + + memset(buffer1, 0, nSamples + XFADE); // Clear sample buffers + memset(buffer2, 2, nSamples + XFADE); // (set all samples to 512) + + // WaveHC library already defines a Timer1 interrupt handler. Since we + // want to use the stock library and not require a special fork, Timer2 + // is used for a sample-playing interrupt here. As it's only an 8-bit + // timer, a sizeable prescaler is used (32:1) to generate intervals + // spanning the desired range (~4.8 KHz to ~19 KHz, or +/- 1 octave + // from the sampling frequency). This does limit the available number + // of speed 'steps' in between (about 79 total), but seems enough. + TCCR2A = _BV(WGM21) | _BV(WGM20); // Mode 7 (fast PWM), OC2 disconnected + TCCR2B = _BV(WGM22) | _BV(CS21) | _BV(CS20); // 32:1 prescale + OCR2A = map(pitch, 0, 1023, + F_CPU / 32 / (9615 / 2), // Lowest pitch = -1 octave + F_CPU / 32 / (9615 * 2)); // Highest pitch = +1 octave + + // Start up ADC in free-run mode for audio sampling: + DIDR0 |= _BV(ADC0D); // Disable digital input buffer on ADC0 + ADMUX = ADC_CHANNEL; // Channel sel, right-adj, AREF to 3.3V regulator + ADCSRB = 0; // Free-run mode + ADCSRA = _BV(ADEN) | // Enable ADC + _BV(ADSC) | // Start conversions + _BV(ADATE) | // Auto-trigger enable + _BV(ADIE) | // Interrupt enable + _BV(ADPS2) | // 128:1 prescale... + _BV(ADPS1) | // ...yields 125 KHz ADC clock... + _BV(ADPS0); // ...13 cycles/conversion = ~9615 Hz + + TIMSK2 |= _BV(TOIE2); // Enable Timer2 overflow interrupt + sei(); // Enable interrupts +} + +void stopPitchShift() { + ADCSRA = adc_save; // Disable ADC interrupt and allow normal use + TIMSK2 = 0; // Disable Timer2 Interrupt +} + +ISR(ADC_vect, ISR_BLOCK) { // ADC conversion complete + + // Save old sample from 'in' position to xfade buffer: + buffer1[nSamples + xf] = buffer1[in]; + buffer2[nSamples + xf] = buffer2[in]; + if(++xf >= XFADE) xf = 0; + + // Store new value in sample buffers: + buffer1[in] = ADCL; // MUST read ADCL first! + buffer2[in] = ADCH; + + newsum += abs((((int)buffer2[in] << 8) | buffer1[in]) - 512); + + if(++in >= nSamples) { + in = 0; + oldsum = (uint8_t)((newsum / nSamples) >> 1); // 0-255 + newsum = 0L; + } +} + +ISR(TIMER2_OVF_vect) { // Playback interrupt + uint16_t s; + uint8_t w, inv, hi, lo, bit; + int o2, i2, pos; + + // Cross fade around circular buffer 'seam'. + if((o2 = (int)out) == (i2 = (int)in)) { + // Sample positions coincide. Use cross-fade buffer data directly. + pos = nSamples + xf; + hi = (buffer2[pos] << 2) | (buffer1[pos] >> 6); // Expand 10-bit data + lo = (buffer1[pos] << 2) | buffer2[pos]; // to 12 bits + } if((o2 < i2) && (o2 > (i2 - XFADE))) { + // Output sample is close to end of input samples. Cross-fade to + // avoid click. The shift operations here assume that XFADE is 16; + // will need adjustment if that changes. + w = in - out; // Weight of sample (1-n) + inv = XFADE - w; // Weight of xfade + pos = nSamples + ((inv + xf) % XFADE); + s = ((buffer2[out] << 8) | buffer1[out]) * w + + ((buffer2[pos] << 8) | buffer1[pos]) * inv; + hi = s >> 10; // Shift 14 bit result + lo = s >> 2; // down to 12 bits + } else if (o2 > (i2 + nSamples - XFADE)) { + // More cross-fade condition + w = in + nSamples - out; + inv = XFADE - w; + pos = nSamples + ((inv + xf) % XFADE); + s = ((buffer2[out] << 8) | buffer1[out]) * w + + ((buffer2[pos] << 8) | buffer1[pos]) * inv; + hi = s >> 10; // Shift 14 bit result + lo = s >> 2; // down to 12 bits + } else { + // Input and output counters don't coincide -- just use sample directly. + hi = (buffer2[out] << 2) | (buffer1[out] >> 6); // Expand 10-bit data + lo = (buffer1[out] << 2) | buffer2[out]; // to 12 bits + } + + // Might be possible to tweak 'hi' and 'lo' at this point to achieve + // different voice modulations -- robot effect, etc.? + + DAC_CS_PORT &= ~_BV(DAC_CS); // Select DAC + // Clock out 4 bits DAC config (not in loop because it's constant) + DAC_DI_PORT &= ~_BV(DAC_DI); // 0 = Select DAC A, unbuffered + DAC_CLK_PORT |= _BV(DAC_CLK); DAC_CLK_PORT &= ~_BV(DAC_CLK); + DAC_CLK_PORT |= _BV(DAC_CLK); DAC_CLK_PORT &= ~_BV(DAC_CLK); + DAC_DI_PORT |= _BV(DAC_DI); // 1X gain, enable = 1 + DAC_CLK_PORT |= _BV(DAC_CLK); DAC_CLK_PORT &= ~_BV(DAC_CLK); + DAC_CLK_PORT |= _BV(DAC_CLK); DAC_CLK_PORT &= ~_BV(DAC_CLK); + for(bit=0x08; bit; bit>>=1) { // Clock out first 4 bits of data + if(hi & bit) DAC_DI_PORT |= _BV(DAC_DI); + else DAC_DI_PORT &= ~_BV(DAC_DI); + DAC_CLK_PORT |= _BV(DAC_CLK); DAC_CLK_PORT &= ~_BV(DAC_CLK); + } + for(bit=0x80; bit; bit>>=1) { // Clock out last 8 bits of data + if(lo & bit) DAC_DI_PORT |= _BV(DAC_DI); + else DAC_DI_PORT &= ~_BV(DAC_DI); + DAC_CLK_PORT |= _BV(DAC_CLK); DAC_CLK_PORT &= ~_BV(DAC_CLK); + } + DAC_CS_PORT |= _BV(DAC_CS); // Unselect DAC + + if(++out >= nSamples) out = 0; +} + diff --git a/AdaVoice/dalek/dalek.ino b/AdaVoice/dalek/dalek.ino new file mode 100644 index 000000000..a2ea5e1f4 --- /dev/null +++ b/AdaVoice/dalek/dalek.ino @@ -0,0 +1,281 @@ +/* +Dalek voice effect using Wave Shield. This is based on the adavoice sketch +with a lot of code & comments ripped out, so see that code for more insight, +parts list, etc. This sketch doesn't do any pitch bending, just the Dalek +modulation...you'll need to perform your own monotone & British accent. :) + +This should still let you play sounds off the SD card (voice effect stops +during playback), haven't tested, but worked in prior code this came from. + +Written by Adafruit industries, with portions adapted from the +'PiSpeakHC' sketch included with WaveHC library. +*/ + +#include +#include + +SdReader card; +FatVolume vol; +FatReader root; +FatReader file; +WaveHC wave; + +#define ADC_CHANNEL 0 // Microphone on Analog pin 0 + +// Wave shield DAC: digital pins 2, 3, 4, 5 +#define DAC_CS_PORT PORTD +#define DAC_CS PORTD2 +#define DAC_CLK_PORT PORTD +#define DAC_CLK PORTD3 +#define DAC_DI_PORT PORTD +#define DAC_DI PORTD4 +#define DAC_LATCH_PORT PORTD +#define DAC_LATCH PORTD5 + +uint8_t adc_save; + +// Keypad information: +uint8_t + rows[] = { A2, A3, A4, A5 }, // Keypad rows connect to these pins + cols[] = { 6, 7, 8 }, // Keypad columns connect to these pins + r = 0, // Current row being examined + prev = 255, // Previous key reading (or 255 if none) + count = 0; // Counter for button debouncing +#define DEBOUNCE 10 // Number of iterations before button 'takes' + +// Keypad/WAV information. Number of elements here should match the +// number of keypad rows times the number of columns, plus one: +const char *sound[] = { + "breath" , "destroy", "saber" , // Row 1 = Darth Vader sounds + "zilla" , "crunch" , "burp" , // Row 2 = Godzilla sounds + "hithere", "smell" , "squirrel", // Row 3 = Dug the dog sounds + "carhorn", "foghorn", "door" , // Row 4 = Cartoon/SFX sound + "startup" }; // Extra item = boot sound + + +//////////////////////////////////// SETUP + +void setup() { + uint8_t i; + + Serial.begin(9600); + + pinMode(2, OUTPUT); // Chip select + pinMode(3, OUTPUT); // Serial clock + pinMode(4, OUTPUT); // Serial data + pinMode(5, OUTPUT); // Latch + digitalWrite(2, HIGH); // Set chip select high + + if(!card.init()) Serial.print(F("Card init. failed!")); + else if(!vol.init(card)) Serial.print(F("No partition!")); + else if(!root.openRoot(vol)) Serial.print(F("Couldn't open dir")); + else { + Serial.println(F("Files found:")); + root.ls(); + // Play startup sound (last file in array). + playfile(sizeof(sound) / sizeof(sound[0]) - 1); + } + + // Optional, but may make sampling and playback a little smoother: + // Disable Timer0 interrupt. This means delay(), millis() etc. won't + // work. Comment this out if you really, really need those functions. + TIMSK0 = 0; + + // Set up Analog-to-Digital converter: + analogReference(EXTERNAL); // 3.3V to AREF + adc_save = ADCSRA; // Save ADC setting for restore later + + // Set keypad rows to outputs, set to HIGH logic level: + for(i=0; i= DEBOUNCE) { // Yes. Held beyond debounce threshold? + if(wave.isplaying) wave.stop(); // Stop current WAV (if any) + else stopDalek(); // or stop voice effect + playfile(button); // and play new sound. + while(digitalRead(cols[c]) == LOW); // Wait for button release. + prev = 255; // Reset debounce values. + count = 0; + } + } else { // Not same button as prior pass. + prev = button; // Record new button and + count = 0; // restart debounce counter. + } + } + } + + // Restore current row to HIGH logic state and advance row counter... + digitalWrite(rows[r], HIGH); + if(++r >= sizeof(rows)) { // If last row scanned... + r = 0; // Reset row counter + // If no new sounds have been triggered at this point, restart Dalek... + if(!wave.isplaying) startDalek(); + } +} + + +//////////////////////////////////// HELPERS + +// Open and start playing a WAV file +void playfile(int idx) { + char filename[13]; + + (void)sprintf(filename,"%s.wav", sound[idx]); + Serial.print("File: "); + Serial.println(filename); + + if(!file.open(root, filename)) { + Serial.print(F("Couldn't open file ")); + Serial.print(filename); + return; + } + if(!wave.create(file)) { + Serial.println(F("Not a valid WAV")); + return; + } + wave.play(); +} + + +//////////////////////////////////// DALEK MODULATION CODE + +void startDalek() { + + // Start up ADC in free-run mode for audio sampling: + DIDR0 |= _BV(ADC0D); // Disable digital input buffer on ADC0 + ADMUX = ADC_CHANNEL; // Channel sel, right-adj, AREF to 3.3V regulator + ADCSRB = 0; // Free-run mode + ADCSRA = _BV(ADEN) | // Enable ADC + _BV(ADSC) | // Start conversions + _BV(ADATE) | // Auto-trigger enable + _BV(ADIE) | // Interrupt enable + _BV(ADPS2) | // 128:1 prescale... + _BV(ADPS1) | // ...yields 125 KHz ADC clock... + _BV(ADPS0); // ...13 cycles/conversion = ~9615 Hz +} + +void stopDalek() { + ADCSRA = adc_save; // Disable ADC interrupt and allow normal use +} + +// Dalek sound is produced by a 'ring modulator' which multiplies microphone +// input by a 30 Hz sine wave. sin() is a time-consuming floating-point +// operation so instead a canned 8-bit integer table is used...the number of +// elements here takes into account the ADC sample rate (~9615 Hz) and the +// desired sine wave frequency (traditionally ~30 Hz for Daleks). +// This is actually abs(sin(x)) to slightly simplify some math later. + +volatile uint16_t ringPos = 0; // Current index into ring table below + +static const uint8_t PROGMEM ring[] = { + 0x00, 0x03, 0x05, 0x08, 0x0A, 0x0D, 0x0F, 0x12, + 0x14, 0x17, 0x19, 0x1B, 0x1E, 0x20, 0x23, 0x25, + 0x28, 0x2A, 0x2D, 0x2F, 0x32, 0x34, 0x37, 0x39, + 0x3C, 0x3E, 0x40, 0x43, 0x45, 0x48, 0x4A, 0x4C, + 0x4F, 0x51, 0x54, 0x56, 0x58, 0x5B, 0x5D, 0x5F, + 0x62, 0x64, 0x66, 0x68, 0x6B, 0x6D, 0x6F, 0x72, + 0x74, 0x76, 0x78, 0x7A, 0x7D, 0x7F, 0x81, 0x83, + 0x85, 0x87, 0x89, 0x8C, 0x8E, 0x90, 0x92, 0x94, + 0x96, 0x98, 0x9A, 0x9C, 0x9E, 0xA0, 0xA2, 0xA4, + 0xA6, 0xA8, 0xA9, 0xAB, 0xAD, 0xAF, 0xB1, 0xB3, + 0xB4, 0xB6, 0xB8, 0xBA, 0xBB, 0xBD, 0xBF, 0xC0, + 0xC2, 0xC4, 0xC5, 0xC7, 0xC8, 0xCA, 0xCB, 0xCD, + 0xCE, 0xD0, 0xD1, 0xD3, 0xD4, 0xD5, 0xD7, 0xD8, + 0xD9, 0xDB, 0xDC, 0xDD, 0xDE, 0xE0, 0xE1, 0xE2, + 0xE3, 0xE4, 0xE5, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, + 0xEC, 0xED, 0xED, 0xEE, 0xEF, 0xF0, 0xF1, 0xF2, + 0xF3, 0xF3, 0xF4, 0xF5, 0xF5, 0xF6, 0xF7, 0xF7, + 0xF8, 0xF9, 0xF9, 0xFA, 0xFA, 0xFB, 0xFB, 0xFB, + 0xFC, 0xFC, 0xFD, 0xFD, 0xFD, 0xFE, 0xFE, 0xFE, + 0xFE, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, + 0xFE, 0xFE, 0xFE, 0xFE, 0xFD, 0xFD, 0xFD, 0xFC, + 0xFC, 0xFB, 0xFB, 0xFB, 0xFA, 0xFA, 0xF9, 0xF9, + 0xF8, 0xF7, 0xF7, 0xF6, 0xF5, 0xF5, 0xF4, 0xF3, + 0xF3, 0xF2, 0xF1, 0xF0, 0xEF, 0xEE, 0xED, 0xED, + 0xEC, 0xEB, 0xEA, 0xE9, 0xE8, 0xE7, 0xE5, 0xE4, + 0xE3, 0xE2, 0xE1, 0xE0, 0xDE, 0xDD, 0xDC, 0xDB, + 0xD9, 0xD8, 0xD7, 0xD5, 0xD4, 0xD3, 0xD1, 0xD0, + 0xCE, 0xCD, 0xCB, 0xCA, 0xC8, 0xC7, 0xC5, 0xC4, + 0xC2, 0xC0, 0xBF, 0xBD, 0xBB, 0xBA, 0xB8, 0xB6, + 0xB4, 0xB3, 0xB1, 0xAF, 0xAD, 0xAB, 0xA9, 0xA8, + 0xA6, 0xA4, 0xA2, 0xA0, 0x9E, 0x9C, 0x9A, 0x98, + 0x96, 0x94, 0x92, 0x90, 0x8E, 0x8C, 0x89, 0x87, + 0x85, 0x83, 0x81, 0x7F, 0x7D, 0x7A, 0x78, 0x76, + 0x74, 0x72, 0x6F, 0x6D, 0x6B, 0x68, 0x66, 0x64, + 0x62, 0x5F, 0x5D, 0x5B, 0x58, 0x56, 0x54, 0x51, + 0x4F, 0x4C, 0x4A, 0x48, 0x45, 0x43, 0x40, 0x3E, + 0x3C, 0x39, 0x37, 0x34, 0x32, 0x2F, 0x2D, 0x2A, + 0x28, 0x25, 0x23, 0x20, 0x1E, 0x1B, 0x19, 0x17, + 0x14, 0x12, 0x0F, 0x0D, 0x0A, 0x08, 0x05, 0x03 }; + +ISR(ADC_vect, ISR_BLOCK) { // ADC conversion complete + + uint8_t hi, lo, bit; + int32_t v; // Voice in + uint16_t r; // Ring in + uint32_t o; // Output + + lo = ADCL; + hi = ADCH; + + // Multiply signed 10-bit input by abs(sin(30 Hz)): + v = ((int32_t)hi << 8 | lo) - 512; // voice = -512 to +511 + r = (uint16_t)pgm_read_byte(&ring[ringPos]) + 1; // ring = 1 to 256 + o = v * r + 131072; // 0-261888 (18-bit) + hi = (o >> 14); // Scale 18- to 12-bit + lo = (o >> 16) | (o >> 6); + + if(++ringPos >= sizeof(ring)) ringPos = 0; // Cycle through table + + // Issue result to DAC: + DAC_CS_PORT &= ~_BV(DAC_CS); + DAC_DI_PORT &= ~_BV(DAC_DI); + DAC_CLK_PORT |= _BV(DAC_CLK); DAC_CLK_PORT &= ~_BV(DAC_CLK); + DAC_CLK_PORT |= _BV(DAC_CLK); DAC_CLK_PORT &= ~_BV(DAC_CLK); + DAC_DI_PORT |= _BV(DAC_DI); + DAC_CLK_PORT |= _BV(DAC_CLK); DAC_CLK_PORT &= ~_BV(DAC_CLK); + DAC_CLK_PORT |= _BV(DAC_CLK); DAC_CLK_PORT &= ~_BV(DAC_CLK); + for(bit=0x08; bit; bit>>=1) { + if(hi & bit) DAC_DI_PORT |= _BV(DAC_DI); + else DAC_DI_PORT &= ~_BV(DAC_DI); + DAC_CLK_PORT |= _BV(DAC_CLK); DAC_CLK_PORT &= ~_BV(DAC_CLK); + } + for(bit=0x80; bit; bit>>=1) { + if(lo & bit) DAC_DI_PORT |= _BV(DAC_DI); + else DAC_DI_PORT &= ~_BV(DAC_DI); + DAC_CLK_PORT |= _BV(DAC_CLK); DAC_CLK_PORT &= ~_BV(DAC_CLK); + } + DAC_CS_PORT |= _BV(DAC_CS); +} + diff --git a/AdaVoice/wavs/breath.wav b/AdaVoice/wavs/breath.wav new file mode 100644 index 000000000..352975a18 Binary files /dev/null and b/AdaVoice/wavs/breath.wav differ diff --git a/AdaVoice/wavs/burp.wav b/AdaVoice/wavs/burp.wav new file mode 100644 index 000000000..d6f535d3b Binary files /dev/null and b/AdaVoice/wavs/burp.wav differ diff --git a/AdaVoice/wavs/carhorn.wav b/AdaVoice/wavs/carhorn.wav new file mode 100644 index 000000000..0b67c9a8b Binary files /dev/null and b/AdaVoice/wavs/carhorn.wav differ diff --git a/AdaVoice/wavs/crunch.wav b/AdaVoice/wavs/crunch.wav new file mode 100644 index 000000000..66731124f Binary files /dev/null and b/AdaVoice/wavs/crunch.wav differ diff --git a/AdaVoice/wavs/destroy.wav b/AdaVoice/wavs/destroy.wav new file mode 100644 index 000000000..93e90b21e Binary files /dev/null and b/AdaVoice/wavs/destroy.wav differ diff --git a/AdaVoice/wavs/door.wav b/AdaVoice/wavs/door.wav new file mode 100644 index 000000000..98cf24249 Binary files /dev/null and b/AdaVoice/wavs/door.wav differ diff --git a/AdaVoice/wavs/foghorn.wav b/AdaVoice/wavs/foghorn.wav new file mode 100644 index 000000000..5705115ed Binary files /dev/null and b/AdaVoice/wavs/foghorn.wav differ diff --git a/AdaVoice/wavs/hithere.wav b/AdaVoice/wavs/hithere.wav new file mode 100644 index 000000000..548cfc003 Binary files /dev/null and b/AdaVoice/wavs/hithere.wav differ diff --git a/AdaVoice/wavs/saber.wav b/AdaVoice/wavs/saber.wav new file mode 100644 index 000000000..086ef712a Binary files /dev/null and b/AdaVoice/wavs/saber.wav differ diff --git a/AdaVoice/wavs/smell.wav b/AdaVoice/wavs/smell.wav new file mode 100644 index 000000000..0ab632412 Binary files /dev/null and b/AdaVoice/wavs/smell.wav differ diff --git a/AdaVoice/wavs/squirrel.wav b/AdaVoice/wavs/squirrel.wav new file mode 100644 index 000000000..f033b26b3 Binary files /dev/null and b/AdaVoice/wavs/squirrel.wav differ diff --git a/AdaVoice/wavs/startup.wav b/AdaVoice/wavs/startup.wav new file mode 100644 index 000000000..35ef43afb Binary files /dev/null and b/AdaVoice/wavs/startup.wav differ diff --git a/AdaVoice/wavs/zilla.wav b/AdaVoice/wavs/zilla.wav new file mode 100644 index 000000000..ee94c6083 Binary files /dev/null and b/AdaVoice/wavs/zilla.wav differ