2 track playback device - seeking elegant/effective solution for transport controls

Status
Not open for further replies.

NewtonBIV

Active member
Hey guys!
I tried to do as much of this on my own since I notice sometimes the same questions will get asked multiple times. I've read a ton of them and actually got pretty far with a device that actually works sort of but when this major issue came up I decided to call in a professional. Any help is majorly appreciated!

So what I need is a device that plays 2 tracks, loaded up with samples pulled from the sd card and simply looped (don't worry i'm NOT asking about seamless looping here). Simple controls really, each 'deck' (this is basically like a set of cdj's on a DJ setup with limited controls over the decks so i use the terminology) would have 2 buttons, one for skipping to the next track and one for going to the previous track, then one global button (rather than on each deck this could be a single button controlling both) to 'restart' both tracks playing once i've found the 2 samples i want. Obviously more buttons would be greatly useful and i could do pots, led, all kinds of stuff but just getting minimal stuff going for now. I've gotten most of the buttons in place then something huge came up. it plays the entire first sample once and THEN starts playing the second one. I need them both to start at the same time (exactly the same if possible which up till now i've been ok with a slight delay).

Wouldn't the best solution be to just use a queue where I could get the tracks lined up and THEN play them? Also, this code has it to where when i scroll through the samples i'm actually listening to each one. that's great but at some point i'd like to simply skip to the track i want and THEN just press a 'PLAY' button for both tracks to trigger then. can add a display later to see the tracks but for now i don't mind simply pressing the seek button a couple times by memory. I'm using a teensy 4 with the audio shield for that version and here is my jumbled code.if anyone has any ideas I appreciate your consideration big time this has been quite the discovery process for me!


#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>
#include <Bounce.h>

AudioPlaySdWav playSdWav2;
AudioPlaySdWav playSdWav1;
AudioMixer4 mixer1;
AudioMixer4 mixer2;
AudioOutputI2S i2s1;
AudioConnection patchCord1(playSdWav2, 0, mixer2, 0);
AudioConnection patchCord2(playSdWav2, 1, mixer1, 1);
AudioConnection patchCord3(playSdWav1, 0, mixer1, 0);
AudioConnection patchCord4(playSdWav1, 1, mixer2, 1);
AudioConnection patchCord5(mixer1, 0, i2s1, 0);
AudioConnection patchCord6(mixer2, 0, i2s1, 1);
AudioControlSGTL5000 sgtl5000_1;

Bounce button0 = Bounce(0, 15); //this is the GLOBAL AUTO STOP & START
Bounce button1 = Bounce(1, 15); //DECK 1 SKIP TO NEXT TRACK
Bounce button2 = Bounce(2, 15); //DECK 1 GO BACK TO PREVIOUS TRACK
Bounce button3 = Bounce(3, 15); //DECK 2 SKIP TO NEXT TRACK
Bounce button4 = Bounce(4, 15); //DECK 2 GO BACK TO PREVIOUS TRACK

#define SDCARD_CS_PIN 10
#define SDCARD_MOSI_PIN 11
#define SDCARD_SCK_PIN 13

void setup() {
Serial.begin(9600);
AudioMemory(8);
sgtl5000_1.enable();
sgtl5000_1.volume(0.5);
SPI.setMOSI(SDCARD_MOSI_PIN);
SPI.setSCK(SDCARD_SCK_PIN);
if (!(SD.begin(SDCARD_CS_PIN))) {
while (1) {
Serial.println("Unable to access the SD card");
delay(500);
}
}
pinMode(0, INPUT_PULLUP); //STOP ALL & START OVER AS IS
pinMode(1, INPUT_PULLUP); //DECK 1 SKIP
pinMode(2, INPUT_PULLUP); //DECK 1 REWIND
pinMode(3, INPUT_PULLUP); //DECK 2 SKIP
pinMode(4, INPUT_PULLUP); //DECK 2 REWIND
delay(1000);
}

int filenumber = 0; //track playing on deck1
int filenumber1 = 0; //track playing on deck2

const char * filelist[4] = {
"DRUM87B1.WAV", "DRUM87B3.WAV", "800001.WAV", "800003.WAV"
}; //DECK 1 TRACKS THESE WILL ALL BE DRUM BEATS FOR THE MOST PART
const char * filelist1[4] = {
"BASS87A#.WAV", "BASS87E1.WAV", "80000006.WAV", "80000008.WAV"
}; //DECK 2 TRACKS ALL OF THE MUSICAL SAMPLES



void loop() {
//DECK 1 PLAYER, TOOK OUT THE FUNCTION THAT WENT THROUGH ALL THE TRACKS SO IT WOULD JUST LOOP WHICHEVER TRACK WAS SELECTED AT THE MOMENT
if (playSdWav1.isPlaying() == false) {
const char *filename = filelist[filenumber];
Serial.print("Start playing ");
Serial.println(filename);
playSdWav1.play(filename);
delay(10);
}
//DECK 2 PLAYER, I'VE DONE THIS WHERE BOTH DECKS HAVE THE SAME SAMPLE AND THEY ARE CLOSE ENOUGH IN SYNC THAT THEY PRETTY MUCH SOUND LIKE ONE SAMPLE FOR ABOUT 20 LOOPS BEFORE GETTING THAT 'PHASER' SOUND
if (playSdWav2.isPlaying() == false) {
const char *filename1 = filelist1[filenumber1];
playSdWav2.play(filename1);
}
//HERE ARE THE BUTTON SELECTORS - FIRST ONE SIMPLY STOPS ALL AND STARTS FROM THE BEGINNING AT WHICHEVER SAMPLE IS SELECTED
button0.update();
if (button0.fallingEdge()) {
playSdWav1.stop();
playSdWav2.stop();
}
//DECK 1 SKIP TO NEXT TRACK UNTIL LAST TRACK IT GOES BACK TO FIRST TRACK
button1.update();
if (button1.fallingEdge()) {
playSdWav1.stop();
filenumber = filenumber + 1;
if (filenumber >= 3) filenumber = filenumber - 3;
}
//DECK 1 PREVIOUS TRACK UNTIL THE BEGINNING THEN IT GOES TO THE LAST TRACK AGAIN
button2.update();
if (button2.fallingEdge()) {
playSdWav1.stop();
filenumber = filenumber - 1;
if (filenumber < 0) filenumber = filenumber + 3;
}
//DECK 2 SKIP TO NEXT TRACK TILL THE END WHEN IT GOES BACK TO BEGINNING
button3.update();
if (button3.fallingEdge()) {
playSdWav2.stop();
filenumber1 = filenumber1 + 1;
if (filenumber1 >= 3) filenumber1 = filenumber1 - 3;
}
//DECK 2 PREVIOUS TRACK TILL THE BEGINNING WHEN IT CYCLES BACK TO LAST TRACK
button4.update();
if (button4.fallingEdge()) {
playSdWav2.stop();
filenumber1 = filenumber1 - 1;
if (filenumber1 < 0) filenumber1 = filenumber1 + 3;
}
}
 
ok well I got it to where I can now 'scroll through' the tracks until I choose one and THEN can simply play both, something seems off though, after rewinding or skipping once the buttons stop working (even though i'll run another sketch and they work fine). Still, looking through all the other entries and seeing if I can find an approach to emulate. Still all ears if there are any suggestions out there! I'm on a teensy 4 along with the appropriate audio shield. Thanks all!

Code:
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>
#include <Bounce.h>

AudioPlaySdWav           playSdWav2;     
AudioPlaySdWav           playSdWav1;    
AudioMixer4              mixer1;     
AudioMixer4              mixer2;       
AudioOutputI2S           i2s1;           
AudioConnection          patchCord1(playSdWav2, 0, mixer2, 0);
AudioConnection          patchCord2(playSdWav2, 1, mixer1, 1);
AudioConnection          patchCord3(playSdWav1, 0, mixer1, 0);
AudioConnection          patchCord4(playSdWav1, 1, mixer2, 1);
AudioConnection          patchCord5(mixer1, 0, i2s1, 0);
AudioConnection          patchCord6(mixer2, 0, i2s1, 1);
AudioControlSGTL5000     sgtl5000_1;     

Bounce button0 = Bounce(0, 15); //this is the GLOBAL AUTO STOP & START
Bounce button1 = Bounce(1, 15); //DECK 1 SKIP TO NEXT TRACK
Bounce button2 = Bounce(2, 15); //DECK 1 GO BACK TO PREVIOUS TRACK
Bounce button3 = Bounce(3, 15); //DECK 2 SKIP TO NEXT TRACK
Bounce button4 = Bounce(4, 15); //DECK 2 GO BACK TO PREVIOUS TRACK

#define SDCARD_CS_PIN    10
#define SDCARD_MOSI_PIN  11
#define SDCARD_SCK_PIN   13

void setup() {
  Serial.begin(9600);
  AudioMemory(8);
  sgtl5000_1.enable();
  sgtl5000_1.volume(0.5);
  SPI.setMOSI(SDCARD_MOSI_PIN);
  SPI.setSCK(SDCARD_SCK_PIN);
  if (!(SD.begin(SDCARD_CS_PIN))) {
    while (1) {
      Serial.println("Unable to access the SD card");
      delay(500);
    }
  }
  pinMode(0, INPUT_PULLUP); //STOP ALL & START OVER AS IS
  pinMode(1, INPUT_PULLUP); //DECK 1 SKIP
  pinMode(2, INPUT_PULLUP); //DECK 1 REWIND
  pinMode(3, INPUT_PULLUP); //DECK 2 SKIP
  pinMode(4, INPUT_PULLUP); //DECK 2 REWIND
  delay(1000);
}
int filenumber = 0; //track playing on deck1
int filenumber1 = 0; //track playing on deck2
const char * filelist[4] = {
  "DRUM87B1.WAV", "DRUM87B3.WAV", "800001.WAV", "800003.WAV"
  };   
const char * filelist1[4] = {
  "BASS87A#.WAV", "BASS87E1.WAV", "80000006.WAV", "80000008.WAV"
  };    
void loop() {
  if (playSdWav1.isPlaying() == false) {
    const char *filename = filelist[filenumber];
    const char *filename1 = filelist1[filenumber1];
    playSdWav1.play(filename);
    playSdWav2.play(filename1);
    delay(10); 
  }
  button0.update();
  button1.update();
  button2.update();
  button3.update();
  button4.update();
  if (button0.fallingEdge()) {
     playSdWav1.stop();
     playSdWav2.stop();
     Serial.println("Starting Over");
    delay(5);
  }
  if (button1.fallingEdge()) {
    filenumber = filenumber + 1;
    if (filenumber >= 3) filenumber = 0;
    Serial.println("Next Track Deck1");
  }
  if (button2.fallingEdge()) {
    filenumber = filenumber - 1;
    if (filenumber < 0) filenumber = filenumber + 4;
    Serial.println("Rewind Track Deck1");
  }
  if (button3.fallingEdge()) {
    filenumber1 = filenumber1 + 1;
    if (filenumber1 >= 3) filenumber1 = 0;
    Serial.println("Next Track Deck2");
  }
  if (button4.fallingEdge()) {
    filenumber1 = filenumber1 - 1;
    if (filenumber1 < 0) filenumber1 = filenumber1 + 4;
    Serial.println("Rewind Track Deck2");
  }
}
 
Hello!

well, I pilfered like 3 peoples' code off of here and got a sort of Frankenstein's monster thing going. It actually works pretty well I just need to tie it altoghether in one final piece of logic that enables my global playback button to, when pressed, take the filenumber and convert it into a BPM,then automatically figure out how many beats to wait till it needs to retrigger the samples. I've got two approaches to accomplish this one with booleans then one simply assigning a 'barstate' to each sequencer step. Anyway, wanted to put it up here in case anyone thinks I need to see a therapist or something haha. Thank you so much for providing me the fodder to do all this with! Teensy 4 with the appropriate audio shield.

Code:
#include "AudioSampleSnare.h"     
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>
#include <Bounce.h>
AudioPlaySdWav           playSdWav2;     //deck2 object
AudioPlaySdWav           playSdWav1;     //deck1 object
AudioPlayMemory          playMem1;      //metronome 
AudioMixer4              mixer2;       
AudioMixer4              mixer1;         
AudioOutputI2S           i2s1;        
AudioConnection          patchCord1(playSdWav2, 0, mixer1, 2);
AudioConnection          patchCord2(playSdWav2, 1, mixer2, 2);
AudioConnection          patchCord3(playSdWav1, 0, mixer1, 1);
AudioConnection          patchCord4(playSdWav1, 1, mixer2, 1);
AudioConnection          patchCord5(playMem1, 0, mixer1, 0);
AudioConnection          patchCord6(playMem1, 0, mixer2, 0);
AudioConnection          patchCord7(mixer2, 0, i2s1, 1);
AudioConnection          patchCord8(mixer1, 0, i2s1, 0);
AudioControlSGTL5000     sgtl5000_1;     
#define SDCARD_CS_PIN    10
#define SDCARD_MOSI_PIN  11
#define SDCARD_SCK_PIN   13
Bounce globalPlay = Bounce(0, 15);    //supposed to start playing which ever samples are currently loaded onto both decks
Bounce skipButton1 = Bounce(1, 15);   //deck1 skips to next sample in line without actually changing any playback, won't trigger till next time globalplay is pressed
Bounce rewButton1 = Bounce(2, 15);    //deck1 goes to previous track without triggering playback, that only happens next time globalplay is pressed
Bounce skipButton2 = Bounce(4, 15);   //deck2 skips to next sample in line without actually changing any playback, won't trigger till next time globalplay is pressed
Bounce rewButton2 = Bounce(5, 15);    //deck2 goes to previous track without triggering playback, that only happens next time globalplay is pressed
Bounce globalStop = Bounce(6, 15);    //stops all playback
int filenumber = 0; //sample currently playing or in queue to play on deck1
int filenumber1 = 0; //sample currently playing or in queue to play on deck2
int barsCounter = 0;
int tempoPin = A0; //plan on ditching this for my formula which will take the filename (i.e. 8001) and, based on the size of the number, divide it by 100, 1000, 10000, or 100000
int millisBPM = 0; //was in precount may keep it 
int bars;
int bpm;
int barState = 1;   //each counter in sequence will be 1 out of 4 'bar states' i.e. quarterState, halfstate, and so on, this integer tags each with a number 1 through 4 identifying which state they are
elapsedMillis blinkTime;
int tempoCounter = 0; //still not sure where we use this
bool didPlay = false;
bool didBarPlay = false;
bool isRec = false;
bool isPlay = false;
//I designed the booleans below, categorizing each sample's length which I know based on the number I've given each sample, all the way from track 7201.RAW which is a 1 bar drumbeat at 72 bpm, all the way to 16000479.RAW which is a 32 count piano chord progression with vocals overlaid and is 160BPM
bool quarterState = false;  //this state is a 1 BAR sample, 4 beats long, meaning it needs to trigger every 4 beats, it has a 'barstate' identifier as '1'
bool halfState = false;   //this state is a 2 BAR sample, 8 beats long, meaning it needs to trigger every 8 beats, it has a 'barstate' identifier as '2'
bool wholeState = false;  //this state is a 4 BAR sample, 16 beats long, meaning it needs to trigger every 16 beats, it has a 'barstate' identifier as '3'
bool doubleState = false; //this state is an 8 BAR sample, 32 beats long, meaning it needs to trigger every 32 beats, it has a 'barstate' identifier as '4'


void setup() {
  Serial.begin(9600);
  AudioMemory(10);
  sgtl5000_1.enable();
  sgtl5000_1.volume(0.5);
  SPI.setMOSI(SDCARD_MOSI_PIN);
  SPI.setSCK(SDCARD_SCK_PIN);
  if (!(SD.begin(SDCARD_CS_PIN))) {
    while (1) {
      Serial.println("Unable to access the SD card");
      delay(500);
    }
  }
  pinMode(0, INPUT_PULLUP);
  pinMode(1, INPUT_PULLUP);
  pinMode(2, INPUT_PULLUP);
  pinMode(4, INPUT_PULLUP);
  pinMode(5, INPUT_PULLUP);
  pinMode(6, INPUT_PULLUP);
  mixer1.gain(0, 0.3);
  mixer1.gain(1, 0.3);
  mixer1.gain(2, 0.3);
  mixer2.gain(0, 0.3);
  mixer2.gain(1, 0.3);
  mixer2.gain(2, 0.3);
}
  const char * filelist[4] = {
  "9001.WAV", "8001.WAV", "800001.WAV", "800003.WAV"            //picked easy whole round numbered samples to work with while having to figure out all the math
  };   
  const char * filelist1[4] = {
  "80000014.WAV", "7500002.WAV", "80000006.WAV", "80000008.WAV"
  }; 

void loop() {
  const char *filename = filelist[filenumber];
  const char *filename1 = filelist1[filenumber1];
     globalPlay.update();
     if (globalPlay.fallingEdge()) {
     playSdWav1.play(filename);
     playSdWav2.play(filename1);
     isPlay = true;
     barsCounter = 0;       //starts the metronome going, or at leaststarts it over again
     Serial.print("Playing file: ");
     Serial.println(filename);
     delay(5);
    }
/*if (*filename < 9999) {
      //bpm = *filename/100;
      Serial.println(bpm);  //so for instance if the track is labelled 9001.wav it's a 90 bpm which you get by dividing by 100, plus it'll go into 'barState = 1' also known as quarterState = true
      quarterState = true;
      barState = 1; // needs to trigger every 4 beats or every bar, or basically any time there is any barstate other than nothing
    //while (barState != NULL && !didPlay) {
   // playSdWav1.play(filename); 
    }
    else if (*filename >9999 && *filename <= 99999){          //60000/1000 = 60 bpm 8 count sample
     // bpm = *filename/1000;
      barState = 2;
      halfState = true;
    }
    else if (*filename >99999 && *filename <= 999999){        //600000/10000 = 60 bpm 16 count sample
      //bpm = *filename/10000;
      barState = 3;
      wholeState = true;
    }
    else if (*filename >999999 && *filename <= 999999){       //6000000/100000 = 60 bpm 32 count sample
      //bpm = *filename/100000;
      barState = 4;
      doubleState = true;
    }
 */   
    
bpm = map(analogRead(tempoPin),0,1023,60,140);       //works great I just baked the bpm's into the file names already               /////////////////BPM///////////////////////
millisBPM = 60000/bpm;
bars = millisBPM/2;
if(blinkTime > millisBPM/4 ){                                                                                                        //////////////////BAR COUNTER///////////
      barsCounter ++;
      didPlay = false;
      didBarPlay = false;
      blinkTime = 0;
      Serial.println(barsCounter);
      if (barsCounter >= 64){                                                                                                       ////////////////COUNTER RESET/////////////////
      barsCounter = 0;}
      }
  if (barsCounter == 0 && !didPlay){                                                                                                  ///////////////////CLICK////////////////////
    barState = 4;    //i can either trigger the files using this barstate or by using the booleans not sure which to go with yet.
    doubleState = true;
    wholeState = true;
    halfState = true;
    quarterState = true;
    playMem1.play(AudioSampleSnare);
    didPlay = true; 
  } else if (barsCounter == 4 && !didPlay){                                          ///////////////////CLICK////////////////////
    barState = 1;
    quarterState = true;
    Serial.println(bpm);
    playMem1.play(AudioSampleSnare);
    didPlay = true; 
  } else if (barsCounter == 8 && !didPlay){
    barState = 2;
    halfState = true;
    quarterState = true;
    playMem1.play(AudioSampleSnare);
    didPlay = true;      
  } else if (barsCounter == 12 && !didPlay){
    barState = 1;
    quarterState = true;
    playMem1.play(AudioSampleSnare);
    didPlay = true;      
  } else if (barsCounter == 16 && !didPlay){
    barState = 3;
    wholeState = true;
    halfState = true;
    quarterState = true;
    playMem1.play(AudioSampleSnare);
    didPlay = true;  
  } else if(barsCounter == 20 && !didPlay){
    barState = 1;
    quarterState = true;
    Serial.println(bpm);
    playMem1.play(AudioSampleSnare);
    didPlay = true;      
  } else if (barsCounter == 24 && !didPlay){
    barState = 2;
    halfState = true;
    quarterState = true;
    playMem1.play(AudioSampleSnare);
    didPlay = true;     
  } else if (barsCounter == 28 && !didPlay){
    barState = 1;
    quarterState = true;
    playMem1.play(AudioSampleSnare);
    didPlay = true;    
  } else if (barsCounter == 32 && !didPlay){
    barState = 4; 
    doubleState = true;
    wholeState = true;
    halfState = true;
    quarterState = true;
    playMem1.play(AudioSampleSnare);
    didPlay = true; 
  } else if(barsCounter == 36 && !didPlay){
    barState = 1;
    quarterState = true;
    Serial.println(bpm);
    playMem1.play(AudioSampleSnare);
    didPlay = true;    
  } else if (barsCounter == 40 && !didPlay){
    barState = 2;
    halfState = true;
    quarterState = true;
    playMem1.play(AudioSampleSnare);
    didPlay = true;      
  } else if (barsCounter == 44 && !didPlay){
    barState = 1; 
    quarterState = true;
    playMem1.play(AudioSampleSnare);
    didPlay = true;     
  } else if (barsCounter == 48 && !didPlay){
    barState = 3;
    wholeState = true;
    halfState = true;
    quarterState = true;
    playMem1.play(AudioSampleSnare);
    didPlay = true;
  } else if(barsCounter == 52 && !didPlay){
    barState = 1;
    quarterState = true;
    Serial.println(bpm);
    playMem1.play(AudioSampleSnare);
    didPlay = true;      
  } else if (barsCounter == 56 && !didPlay){
    barState = 2;
    halfState = true;
    quarterState = true;
    playMem1.play(AudioSampleSnare);
    didPlay = true;     
  } else if (barsCounter == 60 && !didPlay){
    barState = 1;
    quarterState = true;
    playMem1.play(AudioSampleSnare);
    didPlay = true;  
  } else if (barsCounter == 64 && !didPlay){
    barState = 4; 
    doubleState = true;
    wholeState = true;
    halfState = true;
    quarterState = true;
    playMem1.play(AudioSampleSnare);
    didPlay = true; 
  }
  globalStop.update();
  skipButton1.update();
  skipButton2.update();
  rewButton1.update();
  rewButton2.update();
 if (skipButton1.fallingEdge()) {
    filenumber = filenumber + 1;
    if (filenumber >= 3) filenumber = 0;
    Serial.println("Next Track Deck1");
  }
  if (rewButton1.fallingEdge()) {
    filenumber = filenumber - 1;
    if (filenumber < 0) filenumber = filenumber + 4;
    Serial.println("Rewind Track Deck1");
  }
  if (skipButton2.fallingEdge()) {
    filenumber1 = filenumber1 + 1;
    if (filenumber1 >= 3) filenumber1 = 0;
    Serial.println("Next Track Deck2");
  }
  if (rewButton2.fallingEdge()) {
    filenumber1 = filenumber1 - 1;
    if (filenumber1 < 0) filenumber1 = filenumber1 + 4;
    Serial.println("Rewind Track Deck2");
  }  
  if (globalStop.fallingEdge()) {
     playSdWav1.stop();
     playSdWav2.stop();
     isPlay = false;
     isRec = false;
     tempoCounter = 0;
     barsCounter = 0;
     Serial.print("Stopped file");
     delay(5);
  } 
}
 
Well howdy! Maybe i came off as arrogant or something, if so i'm truly sorry. Trust me i have absolutely no right whatsoever to be arrogant about any of this stuff. last I coded was some simulated airport problem in school. then once I built a webcrawler out of python which didn't do what I wanted.

well anyway hope it's cool i'm just going to keep on updating the group. I've drawn a face on my breadboard out of resistors and named it wilson, maybe he can help me out here. It's been an amazing journey, and there actually is something to having to figure out these issues yourself. I actually had the dang thing doing nearly exactly what I wanted it to do. I could have one deck playing that would adjust BPM and trigger on exactly the phrasing I wanted it to based on the name of the sample. I could also have 2 decks playing samples simultaneously, scrolling through tracks and sounding great. Then I tied together the function that told the player how many beats to wait until retriggering the sample (this solution was made to seamlessly loop as the player was operating).It still worked, for both samples, and even if both samples were different lengths! Only problem was the 2nd sample on retrigger sounded like a group of robots being massacred rather than the pleasant elevator music sounding Rhodes melody i'd recorded previously on the seventy-three. I've attached the code , it's on a Teensy 4 with the appropriate audio shield and all the soldering has been adequate as all the buttons are operating exactly as desired except for now this little glitch. I have another sd card with all RAW files and the end product will be using that, i've just switched to WAV while setting it up not sure why just thought it may be easier or something.

If I don't hear anything no worries hoping I can still keep up this journey to the end.Spent a lot of time reading these and let me just say there is much kindness on here compared to some of the nastiness out there thrown at morons like me just getting our feet wet and lost in the sauce. Almost totally finished then i'm thinking old school batman sound effects for the batcave i've building for my son using the adafruit soundboard!


Code:
#include "AudioSampleSnare.h"     
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>
#include <Bounce.h>
AudioPlaySdWav           playSdWav2;     //deck2 object
AudioPlaySdWav           playSdWav1;     //deck1 object
AudioPlayMemory          playMem1;      //metronome 
AudioMixer4              mixer2;       
AudioMixer4              mixer1;         
AudioOutputI2S           i2s1;        
AudioConnection          patchCord1(playSdWav2, 0, mixer1, 2);
AudioConnection          patchCord2(playSdWav2, 1, mixer2, 2);
AudioConnection          patchCord3(playSdWav1, 0, mixer1, 1);
AudioConnection          patchCord4(playSdWav1, 1, mixer2, 1);
AudioConnection          patchCord5(playMem1, 0, mixer1, 0);
AudioConnection          patchCord6(playMem1, 0, mixer2, 0);
AudioConnection          patchCord7(mixer2, 0, i2s1, 1);
AudioConnection          patchCord8(mixer1, 0, i2s1, 0);
AudioControlSGTL5000     sgtl5000_1;     
#define SDCARD_CS_PIN    10
#define SDCARD_MOSI_PIN  11
#define SDCARD_SCK_PIN   13
Bounce globalPlay = Bounce(0, 15);   //global play button starts both decks and tells each one to retrigger on the appropriate barcounts based on barstate & bpm
Bounce skipButton1 = Bounce(1, 15);  //rest of buttons simply seek through samples on each 'deck'
Bounce rewButton1 = Bounce(2, 15);  
Bounce skipButton2 = Bounce(4, 15);  
Bounce rewButton2 = Bounce(5, 15);   
Bounce globalStop = Bounce(6, 15); 
int filenumber = 0; 
int filenumber1 = 0;
int barsCounter = 0;
int millisBPM = 0; 
int bpm = 90;  //just so the first time it isn't counting at mach3
int barState;
int barState1;
elapsedMillis blinkTime;
bool quarterState = false;  //every 4 beats it should retrigger
bool halfState = false;     //every 8 beats
bool wholeState = false;    //every 16 beats
bool doubleState = false;   //ever 32 beats
bool didPlay = false;
   const char * filelist[4] = {
  "9001.WAV", "800003.WAV", "800001.WAV", "800003.WAV"                //mostly drum beats varying from 1 bar to even up to 8 bars or 64 beats
  };   
  const char * filelist1[4] = {
  "80000014.WAV", "7500002.WAV", "80000006.WAV", "80000008.WAV"       //anything else, and usually at least 4 bars or 16beats, but up to 64 as well, bpm's from 60 up to 180
  }; 
const char *filename = filelist[filenumber];
const char *filename1 = filelist1[filenumber1];


String fileString = (filename);         /////////arduous process of changing '8001.RAW' to simply the int 8001 so it can be converted to barState and BPM
String fileString1 = (filename1);
int dotFile = fileString.lastIndexOf('.');
int dotFile1 = fileString1.lastIndexOf('.');
String newString = fileString.substring(0, dotFile);
String newString1 = fileString1.substring(0, dotFile1);
int newFile = newString.toInt();  
int newFile1 = newString1.toInt(); 

void setup() {
  Serial.begin(9600);
  AudioMemory(60);
  sgtl5000_1.enable();
  sgtl5000_1.volume(0.5);
  SPI.setMOSI(SDCARD_MOSI_PIN);
  SPI.setSCK(SDCARD_SCK_PIN);
  if (!(SD.begin(SDCARD_CS_PIN))) {
    while (1) {
      Serial.println("Unable to access the SD card");
      delay(500);
    }
  }
  
  
  pinMode(0, INPUT_PULLUP);
  pinMode(1, INPUT_PULLUP);
  pinMode(2, INPUT_PULLUP);
  pinMode(4, INPUT_PULLUP);
  pinMode(5, INPUT_PULLUP);
  pinMode(6, INPUT_PULLUP);
  
  mixer1.gain(0, 0.3);
  mixer1.gain(1, 0.3);
  mixer1.gain(2, 0.3);
  mixer2.gain(0, 0.3);
  mixer2.gain(1, 0.3);
  mixer2.gain(2, 0.3);
  
}


void loop() 
{

if ((newFile) <= 9999) {                                                        ////////////////determines each deck's sample BPM as well as which barState to program the retriggering schedules
      bpm = (newFile)/100;
      barState = 1;
    } else if ((newFile) > 9999 && (newFile) <= 99999) {
      bpm = (newFile)/1000;
      barState = 2;
    }  else if ((newFile) > 99999 && (newFile) <= 999999) {
      bpm = (newFile)/10000;
      barState = 3;
    }  else if ((newFile) > 999999 && (newFile) <= 9999999) {
      bpm = (newFile)/100000;
      barState = 4;
    }
if ((newFile1) <= 9999) {
      barState1 = 1;
    } else if ((newFile1) > 9999 && (newFile1) <= 99999) {
      barState1 = 2;
    }  else if ((newFile1) > 99999 && (newFile1) <= 999999) {
      barState1 = 3;
    }  else if ((newFile1) > 999999 && (newFile1) <= 9999999) {
      barState1 = 4;
    }
    
millisBPM = 60000/bpm;
if (blinkTime > millisBPM)
      {                                                                                                        //////////////////BAR COUNTER///////////
        barsCounter ++;  
        blinkTime = 0;
        didPlay = false;
        quarterState = false;
        halfState = false;
        wholeState = false;
        doubleState = false;
        Serial.println(barsCounter);
    if (barsCounter >= 64)
      {                                                                                                       
        barsCounter = 0;
      }
   }
  
if (barsCounter == 0 && !didPlay){                                                                                           ///////////////////CLICK////////////////////
    Serial.println(bpm);   
    doubleState = true;
    wholeState = true;
    halfState = true;
    quarterState = true;
    didPlay = true; 
  } else if (barsCounter == 4 && !didPlay){                                          
    quarterState = true;
    didPlay = true; 
  } else if (barsCounter == 8 && !didPlay){
    Serial.println(bpm);
    halfState = true;
    quarterState = true;
    didPlay = true;      
  } else if (barsCounter == 12 && !didPlay){
    quarterState = true;
    didPlay = true;      
  } else if (barsCounter == 16 && !didPlay){
    Serial.println(bpm);
    wholeState = true;
    halfState = true;
    quarterState = true;
    didPlay = true;  
  } else if(barsCounter == 20 && !didPlay){
    quarterState = true;
    didPlay = true;      
  } else if (barsCounter == 24 && !didPlay){
    halfState = true;
    quarterState = true;
    didPlay = true;     
  } else if (barsCounter == 28 && !didPlay){
    quarterState = true;
    didPlay = true;    
  } else if (barsCounter == 32 && !didPlay){ 
    Serial.println(bpm);
    doubleState = true;
    wholeState = true;
    halfState = true;
    quarterState = true;
    didPlay = true; 
  } else if(barsCounter == 36 && !didPlay){
    quarterState = true;
    didPlay = true;    
  } else if (barsCounter == 40 && !didPlay){
    halfState = true;
    quarterState = true;
    didPlay = true;      
  } else if (barsCounter == 44 && !didPlay){
    quarterState = true;
    didPlay = true;     
  } else if (barsCounter == 48 && !didPlay){
    Serial.println(bpm);
    wholeState = true;
    halfState = true;
    quarterState = true;
    didPlay = true;
  } else if(barsCounter == 52 && !didPlay){
    quarterState = true;
    didPlay = true;      
  } else if (barsCounter == 56 && !didPlay){
    halfState = true;
    quarterState = true;
    didPlay = true;     
  } else if (barsCounter == 60 && !didPlay){
    quarterState = true;
    didPlay = true;  
  } else if (barsCounter == 64 && !didPlay){ 
    Serial.println(bpm);
    doubleState = true;
    wholeState = true;
    halfState = true;
    quarterState = true;
    didPlay = true; 
    }
      
skipButton1.update();                                                               //////BUTTONS BABY
skipButton2.update();
rewButton1.update();
rewButton2.update();
globalPlay.update();
globalStop.update();

  do {                                                                        ////HOW CAN I DO A SWITCH CASE THAT OCCURS WITHIN A DO-WHILE LOOP, WHEN 2 SEPARATE OBJECTS NEED TO UNDERGO 2 SEPARATE SWITCH CASE PROCESSES?
    switch (barState) {
      case 1:
          if (quarterState == true) {
            playSdWav1.play(filename);
          }
        break;
      case 2:
          if (halfState == true) {
            playSdWav1.play(filename);
          }
        break;
      case 3:
          if (wholeState == true) {
            playSdWav1.play(filename);          
          }
        break;
      case 4:
          if (doubleState == true) {
            playSdWav1.play(filename);
          }
        break;
      default:
          playSdWav1.play(filename);
        break;   
                    }
       switch (barState1) {
      case 1:
          if (quarterState == true) {
            playSdWav2.play(filename1);
          }
        break;
      case 2:
          if (halfState == true) {
            playSdWav2.play(filename1);
          }
        break;
      case 3:
          if (wholeState == true) {
            playSdWav2.play(filename1);          
          }
        break;
      case 4:
          if (doubleState == true) {
            playSdWav2.play(filename1);
          }
        break;
      default:
          playSdWav2.play(filename1);
        break;   
                    }
  } while (globalPlay.fallingEdge());           //////////////when global play is pressed, samples will be played every 4, 8, 16, or 32 beats depending on which barstate the filename is in
 
  if (skipButton1.fallingEdge()) {
    filenumber = filenumber + 1;
    if (filenumber >= 3) filenumber = 0;
    Serial.println("Next Track Deck1");
  }
  if (rewButton1.fallingEdge()) {
    filenumber = filenumber - 1;
    if (filenumber < 0) filenumber = filenumber + 4;
    Serial.println("Rewind Track Deck1");
  }
  if (skipButton2.fallingEdge()) {
    filenumber1 = filenumber1 + 1;
    if (filenumber1 >= 3) filenumber1 = 0;
    Serial.println("Next Track Deck2");
  }
  if (rewButton2.fallingEdge()) {
    filenumber1 = filenumber1 - 1;
    if (filenumber1 < 0) filenumber1 = filenumber1 + 4;
    Serial.println("Rewind Track Deck2");
  }  
  if (globalStop.fallingEdge()) {
     playSdWav1.stop();
     playSdWav2.stop();
     barsCounter = 0;
     Serial.print("Stopped file");
     delay(5);
  }
}
 
Very cool to see this working out for progress. Have seen more than a few issues self resolved in ongoing self posts.

Having related posts to find and learn from is good too. With new or involved things it takes the right reader to stop by with the time to find the issue or the inspiration for a good question from the information presented.

As for where it is now - wondering what used audio memory and resources would show? Not sure if stopping and starting sounds is leaking anything?

String objects are in use - not sure how dynamically they are used-reused. It doesn't seem to have arrays or other obvious things that might lead to trouble.
 
Very cool to see this working out for progress. Have seen more than a few issues self resolved in ongoing self posts.

Having related posts to find and learn from is good too. With new or involved things it takes the right reader to stop by with the time to find the issue or the inspiration for a good question from the information presented.

As for where it is now - wondering what used audio memory and resources would show? Not sure if stopping and starting sounds is leaking anything?

String objects are in use - not sure how dynamically they are used-reused. It doesn't seem to have arrays or other obvious things that might lead to trouble.

Hello hello! Thanks a million Defragster! Lot's to unpack in your statement - all very well received though. I try to make things interesting if i'm going to subject the internet to my own personal thoughts so it's extremely relieving to have anything to do with what may be seen as an approving glance from a passerby, much less one so obviously steeped in knowledge on the subject at hand (were your other postings & comments any indication of such).

I particular resonate with the fact that you tie in how helpful it is to find other peoples' postings of a similar nature, and to find inspiration and 'ways out' of a problem by doing so.

Trying to sort of figure out how to do a post-op or autopsy of the memory to address the first point toward the task at hand. Assuming there is more to it that simply reading the 'sketch uses 41824 bytes (2%) of program storage space....' which is all I'd been doing. thanks for the tip there I have a feeling there's an entire can of worms there. I will say that as of yet every trigger press has been entirely re-triggering the sound from the beginning so that i can sit there pressing the button repeatedly as quickly as I can and it never seems to cause any bleed over effect one might expect with a leakage issue of the type you mention. That of course does not mean that when the microcontroller automatically re-triggers the sound that there is no leakage. Something to investigate for sure!

One thing that absolutely baffled me was the fact that this entire time i hadn't been working with 2 arrays on the SD card holding all of the samples! You pointed it out and I thought for a second maybe you hadn't seen it but i think you are right in that I've simply got them stored as const characters! I had thought the whole '*filelist[4] = ' was the declaration of the array as well as I guess what you call the initialization, where we tell the program how many elements in the array.

Regardless, if it is not an array then i've got to make it one. I'll bet that makes a huge difference!

Thanks again Defragster and everyone for keeping this forum so rich in content and solution design!
 
Ok my lord. Incredible. I'm either a complete moron, or I'm so good will hunting that I just didn't see such a simplistic solution right in front of me. So I've eliminated the need for nearly all of that stuff i had before. Turns out the only principle I needed really at all was what I created when I named all of samples in a schema that indicates by it's very structure the proper timing for seamless looping along with the BPM to calculate it. I just needed to build the math into the sketch that's it! So exciting to hear it actually load up a sample, start playing it and then amazingly retrigger the sample again at exactly the right millisecond so there is no gap or stilted break. If anyone is out there the only thing i need to rethink now is how i'm going to add a second sample object that does the same thing. Should I treat it like an entirely new deck building out new declarations, functions, and all? Or should I try and set up a sort of 'global' boiler room algorithm where regardless of which deck is loading then next sample from the list it merely sends the filename through the function which returns back to it instructions on scheduling the retriggers? Sort of tried that second one but fell into the same trap trying to return a const char using a string or vice versa. finally just stopped and decided to post my progress. this has been really cool guys I appreciate all the help seriously. Whether this goes or not i'm thinking of trying out the tsunami just to be lazy and throw it in an enclosure & be done with it. Regardless i'll finish this project out and build many other things with all the teensy stuff. Just won't be blogging on here anymore haha sorry I leaned on this so much, what a great service & product you guys provide!

Code:
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>
#include <Bounce.h>

AudioPlaySdRaw           playSdRaw2;    
AudioPlaySdRaw           playSdRaw1;    
AudioMixer4              mixer1;     
AudioOutputI2S           i2s1;          
AudioConnection          patchCord1(playSdRaw2, 0, mixer1, 1);
AudioConnection          patchCord2(playSdRaw1, 0, mixer1, 0);
AudioConnection          patchCord3(mixer1, 0, i2s1, 0);
AudioControlSGTL5000     sgtl5000_1;  
#define SDCARD_CS_PIN    10
#define SDCARD_MOSI_PIN  11
#define SDCARD_SCK_PIN   13
Bounce playButton = Bounce(0, 15);  
Bounce playButton1 = Bounce(1, 15);
Bounce skipButton1 = Bounce(2, 15); 
Bounce rewButton1 = Bounce(3, 15);  
Bounce skipButton2 = Bounce(4, 15);  
Bounce rewButton2 = Bounce(5, 15);   
Bounce globalStop = Bounce(6, 15);

int filenumber = 0;                                                                     ///////I tried converting these to an array of string objects but didn't seem to help anything so just created another///
const char *filelist[4] = {"870001.RAW", "950001.RAW", "960001.RAW"};                   ///////////group of character elements for deck 2 called filelist1[4] = seems to work just fine until I try //////////
//const char *filelist1[4] = {"870162.RAW", "950082.RAW", "960242.RAW"};                ///////////playing both objects at the same time beginning at exactly the same microsecond regardless of how their ////         
const char *filename = filelist[filenumber];                                            ///////////2 different barState schedules might vary. /////////////////////////////  ///////////////////////////                             

String fileString = (filename);                                                   ////////////////////////////////////////////////////                         
int dotFile = fileString.lastIndexOf('.');                                        /////////////The arduous process of knocking the .RAW off the filename so I can then process the remaining int for BPM & barState
String newString = fileString.substring(0, dotFile);                              ////////////////////////////////////////////////////
int newFile = newString.toInt();   

int bpm;
int newDelay;
int millBpm = 60000/bpm;

void setup() {
Serial.begin(9600);
AudioMemory(80);
sgtl5000_1.enable();
sgtl5000_1.volume(0.5);
SPI.setMOSI(SDCARD_MOSI_PIN);
SPI.setSCK(SDCARD_SCK_PIN);
if (!(SD.begin(SDCARD_CS_PIN))) {
   while (1) {
 Serial.println("Unable to access the SD card");
 delay(500); }
  } 
}

void playFile(const char *filename) {     /////here is the crossroads, was thinking of doing a (newFile || newFile1 < 10000) kind of thing, anything seems more elegant than doubling all of it up in another 
                                         ///////monstronsity called void playFile1(const char *filename1)........but it's looking more and more like that's what i'll have to do right
if (newFile < 10000){
    bpm = newFile/100;
    millBpm = 60000/bpm;
    newDelay = millBpm * 4;
} else if (newFile >= 10000 && newFile < 100000){
    bpm = newFile/1000;
    millBpm = 60000/bpm;
    newDelay = millBpm * 8;
} else if (newFile >= 100000 && newFile < 1000000){
    bpm = newFile/10000;
    millBpm = 60000/bpm;
    newDelay = millBpm * 16;
} else if (newFile >= 1000000 && newFile < 10000000){
    bpm = newFile/100000;
    millBpm = 60000/bpm;
    newDelay = millBpm * 32; }  
    playSdRaw1.play(filename);
    delay(newDelay); }

    
void loop() 
{  
  playFile(filename);   //////////////need more stuff in here I guess, so far though this has been working just fine!
}
 
Status
Not open for further replies.
Back
Top