Using Metro to Play Multiple Speakers at Once

Hi! I’m still new to the Arduino world. I’m trying to have my Arduino play music by reading textfiles. However, I am running into an issue. BTW, I am using an Arduino Mega 2560.

When I try to run this code, the Arduino plays only the 1st note for each file. This is correct. However, it stops right afterwards. It should play through the WHOLE song for each speaker. I happened to get this to work with 2 speakers in a different version of the program. However, I cannot get this to work with 3 or more speakers.

I have been using the Metro library in order to have durations for all kinds of notes (including rests). However, when I removed the Metro.reset() portion of the code, the whole song played through (although not at the correct durations). Is there a limit to how many Metros can run at the same time? If so, what can I do to fix this? Thanks in advance! :slight_smile:

This is my code, BTW:

#include <Metro.h>
#include <SD.h>
#include <String.h>
#include <Tone.h>

File root;

const int arr[7][27] = { 
  {
    26, 28, 29, 52, 55, 58, 104, 110, 117, 208, 220, 233, 415, 440, 466, 831, 880, 932,  1661, 1760, 1865, 3322, 3520, 3729, 0,    0,    0    }
  ,
  {
    29, 31, 33, 58, 62, 65, 117, 123, 131, 233, 247, 262, 466, 494, 523, 932, 988, 1047, 1865, 1976, 2093, 3729, 3951, 4186, 0,    0,    0    }
  ,
  {
    0,  16, 17, 31, 33, 35, 62,  65,  69,  123, 131, 139, 247, 262, 277, 494, 523, 554,  988,  1047, 1109, 1976, 2093, 2217, 3951, 4186, 4435    }
  ,
  {
    17, 18, 19, 35, 37, 39, 69,  73,  78,  139, 147, 156, 277, 294, 311, 554, 587, 622,  1109, 1175, 1245, 2217, 2349, 2489, 4435, 4699, 4978    }
  ,
  {
    19, 21, 22, 39, 41, 44, 78,  82,  87,  156, 165, 175, 311, 330, 349, 622, 659, 698,  1245, 1319, 1397, 2489, 2637, 2794, 4978, 0,    0    }
  ,
  {
    21, 22, 23, 41, 44, 46, 82,  87,  93,  165, 175, 185, 330, 349, 370, 659, 698, 740,  1319, 1397, 1480, 2637, 2794, 2960, 0,    0,    0    }
  ,
  {
    23, 25, 26, 46, 49, 52, 93,  98,  104, 185, 196, 208, 370, 392, 415, 740, 784, 831,  1480, 1568, 1661, 2960, 3136, 3322, 0,    0,    0    }
};
//Rows are as follows: A B C D E F G
//Columns are 0F, 0N, 0S, 1F, 1N, 1S, etc.
void readFile(File f, int t, int numba);//Gets the pitch and duration of each note
int getTempo(File f, int i);//Gets the tempo of 
int translation(String s);

int tempo1 = 0, tempo2 = 0, tempo3 = 0, tempo4 = 0, tempo5 = 0; //Self explanatory.
double duration = 0;//Self explanatory. 
int pitch = 0;//Self explanatory. 
int pitch2 = 0;
int duration2 = 0;
int pitch3 = 0;
int duration3 = 0;
Tone tone1; //Used as an easy reference to a speaker in a port.
Tone tone2;
Tone tone3;

Metro m1 = Metro(0);//This is the special "stopwatch" variable for tone1.
Metro m2 = Metro(0);
Metro m3 = Metro(0);

void setup()
{  
  Serial.begin(9600);//Begins the serial at a rate of 9600 bauds.
  pinMode(53, OUTPUT);//Sets port 10 as the output port (I THINK).
  SD.begin(53);//Used for SD cards and the like. Bother with it.
  tone1.begin(8);//Needed to start the tone variable.
  tone2.begin(9);
  tone3.begin(7);
  root = SD.open("/boss/"); // Just use "/" if you don't want to go to a specific folder....
  File temp = root.openNextFile();//Starts it on the 1st file.
  File temp2 = root.openNextFile();
  File temp3 = root.openNextFile();
  //int i = 0;//Used for 
  if(temp)
  Serial.println("File 1 works");
  else
  Serial.println("File 1 doesn't work");
  if(temp2)
  Serial.println("File 2 works");
  else
  Serial.println("File 2 doesn't work");
  if(temp3)
  Serial.println("File 3 works");
  else
  Serial.println("File 3 doesn't work");
  if(temp || temp2 || temp3)
  {
  int seekPos = 0, seekPos2 = 0, seekPos3 = 0;
  if(temp)
  {
    seekPos = getTempo(temp, 0);
    temp.seek(seekPos);
  }
  if(temp2)
  {
    seekPos2 = getTempo(temp2, 1);
    temp2.seek(seekPos2);
  }
  if(temp3)
  {
    seekPos3 = getTempo(temp3, 2);
    temp3.seek(seekPos3);
  } 
  while(temp.available() || temp2.available() || temp3.available())
  {
    if(m1.check() && temp.available())
    {
      readFile(temp, tempo1, 0);
      tone1.play(pitch, duration);
      m1.interval(duration); //An Issue is here I think
      m1.reset();
    }
    if(m2.check() && temp2.available())
    {
      readFile(temp2, tempo2, 1);
      tone2.play(pitch2, duration2);
      m2.interval(duration2); //An Issue is here I think
      m2.reset();
    }
    if(m3.check() && temp3.available())
    {
      readFile(temp3, tempo3, 2);
      tone3.play(pitch3, duration3);
      m3.interval(duration3); //An Issue is here I think
      m3.reset();
    }
  }  
  }
  Serial.println("Completed");
}

void loop()
{
  
}

void readFile(File f, int t, int numba)
{
  int k = 0, iteration = 0;
  String s = "";
  while(f.available() && iteration < 2)
  {
    String s = "";
    boolean b = true;
    while(b && f.available())
    {
      char c = f.read();
      if((c >  '/' && c < ':') || (c > '@' && c < 'H') || (c > '`' && c < 'h') || c == 'r' || c == '.' && c != '\n')
      {
        s+= c;
      }
      else if(c == ' ')
      {
       b = false;
       Serial.println(s);
      }
    }
    if(s != NULL && s.length() >= 1)
    {
        if(iteration%2 == 0)
          {
            if(numba == 0)
            pitch = translation(s);          
            else if(numba == 1)
            pitch2 = translation(s);
            else if(numba == 2)
            pitch3 = translation(s);
          }
        else
        {
          char buf[s.length()+1];
          s.toCharArray(buf,sizeof(buf));
          double WVal=atof(buf);
          double v = (((WVal)/4.0) * (245000.0/(t)));       
          if(numba == 0)
          duration = v;      
          else if(numba == 1)
          duration2 = v;
          else if(numba == 2)
          duration3 = v;
          k++;
        }
        iteration++;
    }
  }  
}
int translation(String s)
{
  int row = 0, column = 0, modifier = 0;
  char a = s.charAt(0), b = s.charAt(1);
  if('a' == tolower(a))
    row = 0;
  else if('b' == tolower(a))
    row = 1;
  else if('c' == tolower(a))
    row = 2;
  else if('d' == tolower(a))
    row = 3;
  else if('e' == tolower(a))
    row = 4;
  else if('f' == tolower(a))
    row = 5;
  else if('g' == tolower(a))
    row = 6;
  else
    return 0;
  if(s.length() == 2)
  {
    modifier = 1;
    if('0' ==  b)
      column = 0;
    else if('1' ==  b)
      column = 1;
    else if('2' ==  b)
      column = 2;
    else if('3' ==  b)
      column = 3;
    else if('4' ==  b)
      column = 4;
    else if('5' ==  b)
      column = 5;
    else if('6' ==  b)
      column = 6;
    else if('7' ==  b)
      column = 7;
    else if('8' ==  b)
      column = 8;
    else
      return 0;
  }
  else if(s.length() == 3)
  {
    char c = s.charAt(2);
    if('f' == tolower(b))
      modifier = 0;
    else if('n' == tolower(b))
      modifier = 1;
    else if('s' == tolower(b))
      modifier = 2;
    else
      return 0;
    if('0' ==  c)
      column = 0;
    else if('1' ==  c)
      column = 1;
    else if('2' ==  c)
      column = 2;
    else if('3' ==  c)
      column = 3;
    else if('4' ==  c)
      column = 4;
    else if('5' ==  c)
      column = 5;
    else if('6' ==  c)
      column = 6;
    else if('7' ==  c)
      column = 7;
    else if('8' ==  c)
      column = 8;
    else
      return 0;
  }
  else
    return 0;
  if(row > 6 || row < 0 || ((3*column) + modifier) > 26 || ((3*column) + modifier) < 0)
    return 0;
  return arr[row][(3*column) + modifier]; 
}
int getTempo(File f, int i)
{
  String s = "";
  char c;
  boolean b = true;
  while(f.available() && b)
  {
    char c = f.read();
    if(c != ((char) 10) && c != ((char) 32))
    {
      s+= c;
    }
    else
      b = false;
  }
  s.toLowerCase();
  if(s.equals("tempo:"))
  {
    int t = f.position();
    s = NULL;
    b = true;
    while(f.available() && b)
    {
      char c = f.read();
      if(c != ((char) 10) && c != ((char) 32))
      {
        s+= c;
      }
      else
        b = false;
    }
    int k = s.toInt();
    switch(i)
    {
       case 0:
       {
        tempo1 = k;
        break;
       }
      case 1:
       {
        tempo2 = k;
        break;
       } 
      case 2:
       {
        tempo3 = k;
        break;
       } 
      case 3:
       {
        tempo4 = k;
        break;
       } 
      case 4:
       {
        tempo5 = k;
        break;
       }  
    }
    return f.position();
  }
  else
    return 0;
}

I have been using the Metro library

For which you need to provide a link. It is not a core library, so you can not assume that we are all familiar with it.

Is there a limit to how many Metros can run at the same time?

You'd need to look at the library code, and see what resources/technique it is using.

I don't think you can open more than 1 file with the SD library like that, I may be wrong as I haven't used it for a little while, but I'm pretty sure I remember that it can only handle one open file at a time.

@Paul

For which you need to provide a link. It is not a core library, so you can not assume that we are all familiar with it.

Sorry about that. Here's the link to the website that has the library: http://playground.arduino.cc/Code/Metro

You'd need to look at the library code, and see what resources/technique it is using.

I have looked at the cpp file and have noticed that it uses the millis() method. I have a feeling that using more than 2 ports causes the millis() function to not work as intended. I later had the Arduino output the value of millis() during each iteration of the loop. It kept on displaying small values like 49 or 56, then staying at about 57. Is this a problem?

@tobyb121

I don't think you can open more than 1 file with the SD library like that, I may be wrong as I haven't used it for a little while, but I'm pretty sure I remember that it can only handle one open file at a time.

I double checked that and it can open up multiple files at a time. Are you suggesting that there could be a shortage of memory?

As I said I may be wrong about the SD library, if it's working for you then great.

The Metro library will have problems if you don't check the status of it regularly.

Thanks tobyb121!