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