Delay Causing Full Program Halt With Tone Library

My problem is that using the ToneLibray (with the fix mention in the last post from here) to play at minimum 3 speakers at different times has brought up the problem that using the delay() functions or millis() or those related functions will completely halt the program. I've tried a work around with loops pausing until a certain time has passed with millis(), but it seems to not work as well. The array that the original loop runs off is a pretty large integer, declared as int CSVSong [][2], with the first part of the entry being the note value and the second being the tick. Essentially, if a note isn't being played at a certain tick and there is not 3 speakers being used, an open speaker will be found and played using the note, and if the note is found, the speaker using it will be turned off.

The problem I'm facing is that, even though it seems that delay() is somewhat frowned upon because it blocks, I still need it to space out the notes and keep the durations and spacings the same. Unfortunately, I can't seem to use it at all.

The board I'm using is an Arduino Mega 2560, I know it's capable of using multiple speakers at one time with the library because I have tested out simple code to play several tunes at once.

I've included the files (except the library), and, I know some of it is really messy and could be improved, but I'm more focused on the problem at hand. I'm always open to suggestions though :slight_smile:

Any help would be greatly appreciated, I've been working on this for some time and, even though I'm really tired and stressed about trying to fix it, I really don't want it to all go to waste because I simply gave up. I don't know all too much about using the timers directly; I was led to believe that several timers were for core functions and by using only 3 speakers, there would still be the essential core timers left open.

ForumProblem.ino (1.38 KB)

CSVStuff.h (21.5 KB)

pitches.h (1.73 KB)

Also, in the file I provided, it used the millis() solution I tried, which didn't work. The marked out line right above it was the original that I wanted to work, but didn't either.

I've tried a work around with loops pausing until a certain time has passed with millis(), but it seems to not work as well.

You've tried writing your own delay() function that still causes problems, eh?

If you are starting a note playing for some duration, don't you need to wait for that note to end before you start another on that pin? A single delay value, between starting notes of differing durations on differing pins is not going to work.

What it seems to me you need is a for loop to iterate over the notes to be played. Within that for loop, you have a while loop that keeps looking for a pin that is not playing a note. When it finds one, it starts that pin playing the next note, and breaks out of the while loop, enabling another iteration of the for loop.

PaulS:
If you are starting a note playing for some duration, don't you need to wait for that note to end before you start another on that pin? A single delay value, between starting notes of differing durations on differing pins is not going to work.

Well, essentially I had not defined a note duration in the tone function, which, as far as I know, keeps playing until it is stopped. In that sense, if a note was started on one speaker, several other speakers could be interacted with while still having the first speaker play indefinitely until its note was called again. If I had included a duration in the tone function, that is my mistake, I was messing around and forgot I left it there.

Essentially, a speaker would keep playing until told to stop, so there would be no need for a delay between the actual notes. When a note is called a second time in the array, the code should essentially figure out the note is already playing and, instead of turning on another speaker with the duplicate note, it stops the speaker with the note called. I like to think of it as each note having a on or off state, so no need for duration. The delay function should simply halt when no notes are being called.

The simple matter is that using any time related function has failed. Delay halts indefinitely and millis() and relevant functions do not return a correct value

The delay function should simply halt when no notes are being called.

That doesn't make sense. If there are no more notes to play, don't do anything. NO delay is needed.

I don't understand where the timing issue is. You seem to want to play multiple notes, on different pins, for undefined periods of time.

How do you determine that any given pin should stop playing one note and become available to be assigned a different note?

What I mean is that the delay is between each array entry.
Say I use speakers one and two with notes F5 and G5, respectively. If the next step is to turn off the speaker playing G5 in 2 seconds, then, while the two speakers are still playing, the delay should hold off for 2 seconds until executing the next entry in the array. Essentially, the note duration is defined by the time between it turning off and on, not an actual value. By only focusing on turning notes off or on at specific times, I don't have to worry about the actual time of the duration.

If the array with the notes went something like this

int CSVSong[][2]={
{ND3,0},
{ND3,113},
{ND3,120},
{ND3,233},
{ND4,240},
{ND4,359},
{NA3,480},
{NA3,659},
{NGS3,840},
{NGS3,953},
{NG3,1080},
{NG3,1199},
{NF3,1320},
{NF3,1547},
{ND3,1560},
{ND3,1673},
{NF3,1680},
{NF3,1793},
{NG3,1800},
{NG3,1913},
{NC3,1920},
{NC3,2033},
{NC3,2040},
{NC3,2153},
{ND4,2160},
{ND4,2279},
{NA3,2400},
{NA3,2579},
{NGS3,2760},
{NGS3,2873}};

it would
-Find an open speaker and play ND3 at tick 0
-Delay for 113 ticks until next entry's tick
-Find a speaker playing ND3 and turn it off at tick 113
-Delay for 7 ticks until next entry's tick
-Find an open speaker and play ND3 at tick 120
-Delay for 133 ticks until next entry's tick
-Find a speaker playing ND3 and turn it off at tick 233
-Delay for 7 ticks until next entry's tick
-Find an open speaker and play ND4 at tick 240
-Delay for 119 ticks until next entry's tick
-Find a speaker playing ND4 and turn it off at tick 359
-... and so on

By this, it can be implied that if I added an entry with a certain note in the beginning and didn't call the note again until the very end of the array, the note would play the whole time.

See, my problem is that because the delay function seems to not be usable, without any form of delay, every note in the array would be instantly called on and off and the song would end instantly. Without any delay, it sounds like loud gibberish, like 5 people talking at the same time. I'm wondering if there is anything wrong with the code that would cause the delay function to delay forever, or if there is something I'm overlooking.

Oh, and the array is created with a C++ application I made on netbeans to take a midi and output a massive string that could be copied and pasted as an array.

See, my problem is that because the delay function seems to not be usable, without any form of delay, every note in the array would be instantly called on and off and the song would end instantly.

To my mind, you have your program structured ass-backwards.

What it should do is read a record from the file. Then, when it has something to do at some time, it needs to compare now (obtained by calling millis()) to the time it is to do something. If it IS time to do something it should do it, and read another record. If not, nothing should happen on that pass through loop.

I'm wondering if there is anything wrong with the code that would cause the delay function to delay forever, or if there is something I'm overlooking.

I don't see how you can determine how long to delay from one record. The time to delay, as you've said more than once in your last post is based on data from TWO records.

I know, I know, it's a really messy and inefficient coding scheme, but, and while I appreciate the criticism, I just need the delay issue solved. The problem is that even if I were to rewrite the whole program in a way that made sense, I would still run into the same problem I'm facing now. The code is nothing special aside from the fact that it uses Tone's from the Tone Library, and any rewritten code would involve using the Tones, I'm 100% sure the delay would still not work. I need to know if there is something I'm doing wrong or if it's something I can't fix, so when I rewrite it I can either fix the problem before it happens or just give it a whole new approach if it's something I can't handle.

I'm 100% sure the delay would still not work.

Why? You know that there is some time when a note (or more than one note) should be playing. While delay() is not the best method, it will work as long as you don't need to do anything else while the delay() is happening (and, I can't see that you do).

I think the problem is that you are trying to determine how long to delay based on one record, when the time to delay is the difference between the value on record n and the value on record n-1.

113 - 0 means delay 113 milliseconds
120 - 113 means delay 7 milliseconds

So, what you need to do is read a record, and make something happen. Then, read the next record to know when you will next need to do something. delay() until it is time. Then, do the thing that needs doing and read the next record.

Notice that you need to read a record BEFORE the for loop that iterates over the records AND you need to read a record at the start of the for loop. Note that some action happens, based on the first record BEFORE the for loop.

PaulS:
So, what you need to do is read a record, and make something happen. Then, read the next record to know when you will next need to do something. delay() until it is time. Then, do the thing that needs doing and read the next record.

That is exactly what it does, essentially. However, while I'm not doing anything between delays per say, speakers might be running at any given time. Now that I think about it, I may not have added a bit that handles the last entry and does not try to read the next which shouldn't exist. I should think that it would give me a compilation error, but it has not yet.

The only problem is that I am unable to delay, or any pause related time function for that matter. I can't imagine rewriting it using the same core basics in this program only to have a different output if the only special thing is the tone library.

I greatly appreciate the help though.

Also, just in case if anyone is wondering about the wiring, I have three wires connected to pwm pins on 2,3 and 4 on the mega. Each connects to its own resistor of like 1k or something around that, and then goes to its own speaker, where each then goes to the same row on the breadboard that goes to the ground back on the arduino. I don't know how to draw the circuit, but hopefully I described it enough.

Update Note: I found a page describing the timers on the mega, so I moved the wires to 2, 9, and 11 so that they are using different pins, but it still has not fixed anything. I'm currently using this bit of code to test the three speakers

#include "pitches.h"
#include <Tone.h>

Tone t1,t2,t3;
void setup()
{  
 for(int i=0;i<53;i++)
 {
   pinMode(i,OUTPUT);
 }

 t1.begin(2);
 t2.begin(9);
 t3.begin(11);
}

void loop() 
{
 //PlayCSV();
 t1.play(NC3);
 delay(1000);
 t2.play(NC3);
 delay(1000);
 t3.play(NC3);
 delay(1000);
 t3.stop();
 delay(1000);
 t2.stop();
 delay(1000);
 t1.stop();
 delay(1000);
}

but using 3 tones causes the delay to break, so I don't believe anything in the original code that this thread is about is causing the problem. If I remove the line t3.begin(11), the other two speakers work. I really want to use AT LEAST 3 speakers, and honestly I was hoping I could use 4 or 5. I don't know what to do.

Look at the Tone class. I suspect that each instance is nabbing a timer. There are only a limited number of timers to go around. Once timer 0 is shanghaied, delay() will quit working.

PaulS:
I suspect that each instance is nabbing a timer. There are only a limited number of timers to go around. Once timer 0 is shanghaied, delay() will quit working.

I was aware of the "timer nabbing", but I wasn't aware that each of the timers controlled, which seems to be at random, pairs of two or three pwm pins on the board. I changed the speaker output pwm pins from 2,3 and 4 to 2, 9, and 11 because those pwm pins are controlled by timers 1,2, and 3 according to here. That didn't work however. And, from what I've seen from the arduino tone library documentation,

You can output the tones on any pin (arbitrary). The number of tones that can be played simultaneously depends on the number of hardware timers (with CTC capability) available on the microcontroller.

ATmega8: 2 (timers 2, and 1)
ATmega168/328: 3 (timers 2, 1, and 0)
ATmega1280: 6 (timers 2, 3, 4, 5, 1, 0)
The timer order given above is the order in which the timers are allocated. Timer 0 is a sensitive timer on the Arduino since it provides millis() and PWM functionality.

I don't know if that's the case here, as it seems to still not work. Also, I've noticed that it was for the '1280' for the processor. It was set on atmega 2560 before when I uploaded, but after changing the processor to atmega 1280 in the settings on the editor, rebuilding, compiling and uploading, there still is no difference. The current code I'm working with is still the code from several posts before, but still the same problem.

Shameless self bump :C