Adafruit Voice changer (minimal sketch)?

Adafruit Ethernet, Motor, Proto, Wave, Datalogger, GPS Shields - etc!

Moderators: adafruit_support_bill, adafruit

Please be positive and constructive with your questions and comments.
User avatar
xl97
 
Posts: 201
Joined: Mon Jul 27, 2009 12:51 pm

Adafruit Voice changer (minimal sketch)?

Post by xl97 »

hey gang-

upon reading up on the Voice Changer demo a bit.. I saw there was another sketch called the Adavoice_face (to animate some led matrix as well)..

I also see there was a fix/edit to allow the voice changer sketch to work without any SD card inserted

(ie: voice change effect still work even though the SD card would throw an error).

I am wondering if there is a MINIMAL Voice Changer sketch out there.. that ONLY deals with the voice changer portion.. and not the keypad..etc..etc extra fluff/stuff/options??



Also... ;)

I also cant seem to figure out to correct edit this part of the code for the DAC (my DAC pins are changed/different.. (to free up some other pins)

Code: Select all

// origianl:
// Wave shield DAC: digital pins 2, 3, 4, 5

// current DAC pinout:
// Arduino pins A3(D17), D4, D7, D8 for DAC

#define DAC_CS_PORT PORTD
#define DAC_CS PORTD17
#define DAC_CLK_PORT PORTD
#define DAC_CLK PORTD4
#define DAC_DI_PORT PORTD
#define DAC_DI PORTD7
#define DAC_LATCH_PORT PORTD
#define DAC_LATCH PORTD8

but I get an error saying: "PORTD17 was not declared in this scope"


just trying to break it all down piece by piece to examine and learn from.. so stripping out the extra helps me focus on the required/needed code only.. and edit/add in more later.

thanks in advance for any help! :)

and thanks Phil or the sketch/demo/tut :)

User avatar
pburgess
 
Posts: 4161
Joined: Sun Oct 26, 2008 2:29 am

Re: Adafruit Voice changer (minimal sketch)?

Post by pburgess »

Howdy howdy howdy!

First, regarding the DAC pinout, use:

Code: Select all

// current DAC pinout:
// Arduino pins A3(D17), D4, D7, D8 for DAC

#define DAC_CS_PORT PORTC
#define DAC_CS PORTC3
#define DAC_CLK_PORT PORTD
#define DAC_CLK PORTD4
#define DAC_DI_PORT PORTD
#define DAC_DI PORTD7
#define DAC_LATCH_PORT PORTB
#define DAC_LATCH PORTB0
These are 'raw' PORT registers, which (except for PORTD), don't relate directly to the digital pin numbers used in the Arduino IDE. It's explained a bit on the Arduino site. This is done for speed reasons; digitalWrite() can be a bit pokey.

You'll still need the 'regular' pin numbers (A3, D4, D7, D8) in the pinMode() calls in setup().

Even if not using the SD card, right now it's necessary to invoke the WaveHC library anyway because this sketch shares some buffers that get allocated there. But if you know for a fact that you won't be using the SD card, I could come up with a standalone variant.

User avatar
xl97
 
Posts: 201
Joined: Mon Jul 27, 2009 12:51 pm

Re: Adafruit Voice changer (minimal sketch)?

Post by xl97 »

pburgess wrote:Howdy howdy howdy!

First, regarding the DAC pinout, use:

Code: Select all

// current DAC pinout:
// Arduino pins A3(D17), D4, D7, D8 for DAC

#define DAC_CS_PORT PORTC
#define DAC_CS PORTC3
#define DAC_CLK_PORT PORTD
#define DAC_CLK PORTD4
#define DAC_DI_PORT PORTD
#define DAC_DI PORTD7
#define DAC_LATCH_PORT PORTB
#define DAC_LATCH PORTB0
These are 'raw' PORT registers, which (except for PORTD), don't relate directly to the digital pin numbers used in the Arduino IDE. It's explained a bit on the Arduino site. This is done for speed reasons; digitalWrite() can be a bit pokey.

You'll still need the 'regular' pin numbers (A3, D4, D7, D8) in the pinMode() calls in setup().

Even if not using the SD card, right now it's necessary to invoke the WaveHC library anyway because this sketch shares some buffers that get allocated there. But if you know for a fact that you won't be using the SD card, I could come up with a standalone variant.

Hi Phil-

I just got the 2 x mics I ordered (from your tut)..


thanks for the help on the direct port manip/setting stuff.. (that is new to me still)..

I originally meant a sketch that didnt have any keypad code or one that didnt have any led code (re: adavoice_face)...
as I wasnt sure if there was anything post that I may have missed?


but a standalone version would be nice too (one where you know you wont/dont have an SD card to utilize/initialize..etc)

I havent had the time to try and tear out any keypad or matrix code... and strip it down.. (heck I was stuck on editing the port for the correct DAC pins.LOL)


Side note: I finally got (I think) a Finite State Machine going.. where I could fade up/down an LED -and- 'loop' an audio file..

I got some strange results.. (some I think due to timer vs pwm).. and some results that led to some plain old, basic electronics questions.. lol

(I posted on it in the Arduino section here.. just in case anyone was bored) :)

User avatar
pburgess
 
Posts: 4161
Joined: Sun Oct 26, 2008 2:29 am

Re: Adafruit Voice changer (minimal sketch)?

Post by pburgess »

Ah, okay. Fortunately we can rip the keypad stuff out pretty easily, and I'll just leave the SD stuff there for now.

Starting with the Adavoice sketch, this part in the global declarations (before setup()) can all be deleted:

Code: Select all

// 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
and replaced with just one item for an optional startup sound if there's an SD card installed:

Code: Select all

const char *sound[] = { "startup" };
This loop in setup() can be removed:

Code: Select all

  // Set keypad rows to outputs, set to HIGH logic level:
  for(i=0; i<sizeof(rows); i++) {
    pinMode(rows[i], OUTPUT);
    digitalWrite(rows[i], HIGH);
  }
  // Set keypad columns to inputs, enable pull-up resistors:
  for(i=0; i<sizeof(cols); i++) {
    pinMode(cols[i], INPUT);
    digitalWrite(cols[i], HIGH);
  }
And then EVERYTHING inside loop() can be removed…it's 100% open to your own code there. The pitch-shifting stuff all occurs in interrupts.

User avatar
xl97
 
Posts: 201
Joined: Mon Jul 27, 2009 12:51 pm

Re: Adafruit Voice changer (minimal sketch)?

Post by xl97 »

HEy Phil--
thanks for the reply and time..

I got a few minutes away from the family (and before I start dinner) to give it a try..

brief overview is I am NOT using a 10k POT.. trying to hardcode the value..

Code, compiles, and uploads.. and it pays the startup.wav file on the SD card.. but I am not getting any response from the mic..

Mic-

GND - GND
VCC - 3.3v on Arduino
OUT - A0 on Arduino


I saw the tutorial note about the AREF pin/connection?

(I dont think I have that pin broken out on my custom board..only the true Arduino/Waveshield I have)

any way around this for my custom application?

(or maybe its something else?)
/*
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 <WaveHC.h>
#include <WaveUtil.h>

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    PORTC
#define DAC_CS         PORTC3
#define DAC_CLK_PORT   PORTD
#define DAC_CLK        PORTD4
#define DAC_DI_PORT    PORTD
#define DAC_DI         PORTD7
#define DAC_LATCH_PORT PORTB
#define DAC_LATCH      PORTB0

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

const char *sound[] = { "startup" };

//////////////////////////////////// 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(17, OUTPUT); // Chip select
  digitalWrite(17, HIGH); // Set chip select high
  pinMode(4, OUTPUT); // Serial clock
  pinMode(7, OUTPUT); // Serial data
  pinMode(8, OUTPUT); // Latch
  
  //test for trunign on CS pin for SD card if needed
  //pinMode(10, OUTPUT);

  // 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

 
  while(wave.isplaying); // Wait for startup sound to finish...
  startPitchShift();     // and start the pitch-shift mode by default.
}

//////////////////////////////////// LOOP

// As written here, the loop function scans a keypad to triggers sounds
// (stopping and restarting the voice effect as needed).  If all you need
// is a couple of buttons, it may be easier to tear this out and start
// over with some simple digitalRead() calls.

void loop() {
  //
}


//////////////////////////////////// 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);
  int pitch = 512;
  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;
}
heres the sketch


thanks..

User avatar
pburgess
 
Posts: 4161
Joined: Sun Oct 26, 2008 2:29 am

Re: Adafruit Voice changer (minimal sketch)?

Post by pburgess »

You could connect 5V to VCC on the mic and comment out this line:

Code: Select all

  analogReference(EXTERNAL); // 3.3V to AREF
However, the mic picks up considerably more noise at 5V than 3.3, and I think it would be worth your while to 'blue wire' this connection on your custom board.

User avatar
xl97
 
Posts: 201
Joined: Mon Jul 27, 2009 12:51 pm

Re: Adafruit Voice changer (minimal sketch)?

Post by xl97 »

Yeah.. I seem to be having 'noise' problems all over the place with thing anyways.. LOL..

I just did a 'jumper' from the 3.3v output to the AREF pin on the chip (manually holding it there)...

AND IT WORKED!..

so yeah that is/was the problem...

Im gonna try @ 5v.. and see how much worse it is! :)


thanks!..


edit/update:

commenting out that line.. and moving mic VCC to 5V didnt seem to work at all??
measured 5v pin on board.. all checks out..??


edit 2:

So I jumper the 5v to the AREF pin to see if that was the problem (again)...

jumpered.. works. not jumpered.. no worky!. =(

So I absolutely need to have something there?

right now it currently just goes to 100nF cap then ground....


Im stumped?


thanks :)

User avatar
pburgess
 
Posts: 4161
Joined: Sun Oct 26, 2008 2:29 am

Re: Adafruit Voice changer (minimal sketch)?

Post by pburgess »

Ah! I see it…a line in startPitchShift() needs to change if we're using VCC for the analog reference:

Code: Select all

  ADMUX  = ADC_CHANNEL; // Channel sel, right-adj, AREF to 3.3V regulator
becomes:

Code: Select all

  ADMUX  = ADC_CHANNEL | _BV(REFS0); // Channel sel, right-adj, AREF to VCC

User avatar
xl97
 
Posts: 201
Joined: Mon Jul 27, 2009 12:51 pm

Re: Adafruit Voice changer (minimal sketch)?

Post by xl97 »

bingo!

that was it!!!

thanks for the help/second pair of eye!.. (I dont think I would have ever figured that one out myself)


hey Phil..

if you wanna PM your address.. I'll send you one my boards to play with.. (just because you have been so helpful to me in my 'journey')..

I doubt it anything you couldnt do or that couldnt be done better.. (just wanted to throw that out there..LOL).. but I'll send one your way!

let me know.


thanks!

User avatar
wade a
 
Posts: 2
Joined: Wed Dec 05, 2012 7:03 pm

Re: Adafruit Voice changer (minimal sketch)?

Post by wade a »

Hay there guys! I was following your post because I am attempting to do the exact same project ( A voice changer without the key pad and minimal sketch work), but I've run into some problems and questions.

First, I am very new to this kinda work so forgive me for asking alot of questions.Second, I just wanted to check and see if I could copy the sketch that you have here? (Just want to make sure it is alright before i just use it)

-------If I am attempting to eliminate the key pad, I will still need the D-card for the WaveHC files yes?
-If i read the other tutorials correct, I still need to use the WaveHC library for the voice distortion, but what about the WAV files for the digits of pi? Still needed?

-----Do I need the Mic AMP Breakout or can I use a simple mic from solarobotics for a smaller speaker?

Any help would be awesome!

Thanks

User avatar
xl97
 
Posts: 201
Joined: Mon Jul 27, 2009 12:51 pm

Re: Adafruit Voice changer (minimal sketch)?

Post by xl97 »

hi-

the 'current' sketch of 'mine' is altered a bit.. (but reading through this thread, should tell you what needs to be 'changed back' to original settings..

my project is a 'custom' board/version and had custom DAC pins used, as well as no AREF pin broken out..

(leaving both of those as default should still get you a working sketch)

there is NO keypad

this sketch DOES require an SD card to be inserted/initialized (I dont believe Phill posted a version that was standalone, not needing the SD to be inserted)

this sketch should have a 'start.wav' file.. (this plays once the board boots/loads SD card... kinda telling you.. "I'm ready").

this sketch DOES NOT need any PI audio files on it.. (not related to the PI sketch at all)

"I" chose to just use the same breakout mic form the sketch..

I wasnt sure if a stand alone mic without any of the added components (caps, resistors) would be as good/effective (or more noise..etc)



Actually.. looking at my last post of some code..

I dont think there is much/anything to change except for reverting back to the old DAC pin values:


You are using a regular Arduino and a true Adafruit WaveShield..yes? (dont forget to read tut again.. and do not forget to connect AREF pin) :)

/*
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 <WaveHC.h>
#include <WaveUtil.h>

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

const char *sound[] = {
  "startup" };

//////////////////////////////////// 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(17, OUTPUT); // Chip select
  digitalWrite(17, HIGH); // Set chip select high
  pinMode(4, OUTPUT); // Serial clock
  pinMode(7, OUTPUT); // Serial data
  pinMode(8, OUTPUT); // Latch

  //test for trunign on CS pin for SD card if needed
  //pinMode(10, OUTPUT);

  // 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


  while(wave.isplaying); // Wait for startup sound to finish...
  startPitchShift();     // and start the pitch-shift mode by default.
}

//////////////////////////////////// LOOP

// As written here, the loop function scans a keypad to triggers sounds
// (stopping and restarting the voice effect as needed).  If all you need
// is a couple of buttons, it may be easier to tear this out and start
// over with some simple digitalRead() calls.

void loop() {
  //
}


//////////////////////////////////// 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);
  int pitch = 512;
  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;
}


User avatar
wade a
 
Posts: 2
Joined: Wed Dec 05, 2012 7:03 pm

Re: Adafruit Voice changer (minimal sketch)?

Post by wade a »

So i double checked everything, went throught the wave sheild tutorial again, made sure that my board was wiered correct.
Yup, made sure the AREF is connected :)
SD card has the WaveHC fiule on it ( I threw on the Pi just because)

The one thing that I think might be causing a problem is my mic, becuase its not the breakout amp, would running it to pin0 and GND still work?

The other thing is that when I try up loading the sketch, it is telling me that it is done uploading, but there is an error at the same time:

[avrdude: stk500_getsync90: not in sync: resp=0x00]

What is this?
Last edited by wade a on Fri Dec 07, 2012 5:46 pm, edited 1 time in total.

User avatar
xl97
 
Posts: 201
Joined: Mon Jul 27, 2009 12:51 pm

Re: Adafruit Voice changer (minimal sketch)?

Post by xl97 »

Wade A wrote:So i double checked everything, went throught the wave sheild tutorial again, made sure that my board was wiered correct.
Yup, made sure the AREF is connected :)
SD card has the WaveHC fiule on it ( I threw on the Pi just because)

The one thing that I think might be causing a problem is my mic, becuase its not the breakout amp, would running it to pin0 and GND still work?

The other thing is that when I try verifying your sketch, it is comming up with a lot of errors. Is there something that I would be doing in the copy/paste that would be causing this?

you dont need he Pi audio files..

in this line phil has an array that holds the file names:

since we are NOT using any sort of keypad and triggering other sounds.. (and simply a voice changer effect).. it only has one file name in it.

Code: Select all

const char *sound[] = { "startup" };
so when the card initializes/boots it plays the boot sound, called: "startup.wav" from the SD card..


* AREF connected to 3.3v pin on Arduino..


I am NOT sure about the mic.. might need to be powered... I just used the one int he sketch.. +, -, and A0 leads

User avatar
pburgess
 
Posts: 4161
Joined: Sun Oct 26, 2008 2:29 am

Re: Adafruit Voice changer (minimal sketch)?

Post by pburgess »

Yes, you need the amplified mic breakout. A bare mic won't work. Also, connect to Analog pin 0, not Digital 0. That might be the cause of your upload trouble.

keeleon
 
Posts: 32
Joined: Sun Jul 17, 2011 6:39 pm

Re: Adafruit Voice changer (minimal sketch)?

Post by keeleon »

Howdy, I am trying to do the same thing as this thread, so I figured it might be better to post here instead of starting my own.

I would like to use the Mic with no Pot, and possibly 5v (however I'm not averse to jumpering to 3.3v)

However, I have tried all the suggestions in here, and I can get no response from the mic at all. I have verified that all my connections are good and the soldering is done correctly, so it's not a bad wire. I have tried it with both the jumpered 3.3v version and the 5v version, changing the code for both. Is there a way to print something to the monitor so I can verify what's coming out of the mic and that the mic even works at all? I'd hate to think I have a defective piece or that I broke it, but I really have no idea how to even figure that out. I can post pictures if it will help, but I'm pretty confident I have everything wired correctly, and I have cut and pasted the codes written here and from the tutorial, so it's not a type.

Locked
Please be positive and constructive with your questions and comments.

Return to “Arduino Shields from Adafruit”