I’m trying to create a none blocking RTTTL music player. Just to test it out I created a musicPlayer class and called a method via a software timer with no delay. This does work, however all software timers are executed on the same thread so this blocks my display update timer
Is there a way to access another thread? So I’d have the following threads active:
*Particle system
*Main loop
*Software timers
*Music thread
Failing that I was thinking of implementing the tone() and delay() functions using standard PWM and software timers, so this wouldn’t impact the other software timers thread.
Has anyone got any better ideas?
BTW, how come I can only declare an SWtimer at the top level of the file and not from within a function? I’m trying to spawn SWtimers at run-time rather than just use a predefined one.
I've not fully worked it out yet, I'm fairly new to CPP but I have a background in other OO langs so I'm picking it up as I go. All the timers would be one shot and I was hoping to be able to destroy them as the final part of their execution.
All I need to do is play simple music in a none blocking way, just trying to figure out the best way to do it. SWtimers might not be the answer. It would be great if I could just execute a function in its own thread.
Tone should only be blocking when you specify duration.
Can you specify only the pin and frequency? Then, when you go to the next note you just call tone again out of your software timer with the new frequency. If you’re stopping, have a rest, etc., use noTone instead to stop the continuous tone from playing.
I haven’t tried this, but I think it should probably work and is easy to implement.
That’s what I was thinking but it means spawning SWtimers with different durations at runtime. I can’t seem to define a timer from within a method/function.
I suppose I could create 1 recurring timer with the duration of the shortest note then count the ticks and use this as a timebase for the music to be played
@Nemiah, spawing/disposing timers seems inefficient. Your idea of a base timer seems more suitable. If you want to use many timers, create a number of them. You can then stop/start and change their period in your code without disposing of them.
It is quite complicated, I've pretty much made a simple smartphone. The other timer is a 1-second timer that queries the Particle system and displays cell/wifi strength and status, cloud status, battery/charging status etc. All this is written to the status bar on the screen.
The main loop will be used for updating the UI based on user interaction.
Yeah, it seems to me that none of that is so important such that it needs accurate management with a Timer, or even single-second refresh rates.
That is what I would call a 'convienience' use of timers.
Perhaps you would consider moving all of that not-really-timer-necessary stuff into the loop() (use a polling timer method) and see how far you progress on your music playback.
@Nemiah, is the display driven via SPI? In loop() you could query/build the status bar data at regular intervals. Then in a software timer, look for change in the elements and only update the display for the changed elements. Most, if not all of the status you are displaying don’t need fast updating. You could build a display manager that uses a display list of objects to display with each object having their own refresh time.
What I’d do is set the timer to fire at the shortest period you support. If you support 1/64th notes, you’d set it at that period, based on the tempo you’re playing at. Then you use a countdown variable for how long you’ll stay in that tone. If you have a half note, you’d countdown from 32 and when you hit 0 you’d go to the next note or rest.
I was working on a music playing app just last week, and this is the approach I took. So far, it seems to be working, though I haven't finished working on it yet (it's more of a programming/logic exercise for me rather than wanting to make a serious app). I have my timer firing twice as fast as the shortest note (which for my purpose is a 16th note) so I can stop it half a beat early to have the notes be staccato rather than tied together.
typedef struct {
uint16_t freq;
uint16_t count;
uint8_t tied;
} Note;
int hp = A4; // pins for Tone(), for playing 3 notes simultaneously
int mp = D0;
int lp = D1;
int hnCounter; // counters for high notes, mid notes, and low notes
int mnCounter;
int lnCounter;
const int tempo = 100; // 1/4 note = 100 beats/minute
Timer noteTimer(60000/(tempo * 8), noteHandler); // fire once for every 32th note at the designated tempo
int beats = 0;
void noteHandler() {
beats++;
if (beats == highNotes[hnCounter].count - 1 && highNotes[hnCounter].tied == 0) {
noTone(hp); // turn off tone for half a beat for staccato notes
}else if (beats == highNotes[hnCounter].count) {
hnCounter++;
tone(hp, highNotes[hnCounter].freq, 0);
}
// etc. for the other notes
}
Yeah, I see what you mean. Although I'm not sure if using the timer just to make the tone none-blocking is any better of a use as it will block any further timers while music is playing. At least the status update timer only takes a few cycles. I'll have a think, but you're right the status update could easily be handled in the loop.
That's more or less what I've got at the moment, only changes are sent to the screen to prevent flickering. It's things like RSSI, battery level/charging indicators etc.
I'm going to write a couple of classes for handling "views" and "buttons" etc. So I'll have a multi-paged UI system that should take care of its self. This should only need updating on user interaction anyway (as far as I can see), so this would be updated separately from the status bar.
Yeah, that's what I was thinking but after looking at the RTTTL format it's not relly conducive of that method. I would have to parse the RTTTL and generate a set of values with the time offsets for tone/pause. Or I would have to parse along the RTTTL every single tick to find out what's next. It's annoying because the RTTTL realyl is designed to be read one item at a time then go away and do that thing for that period, then come back and read the next.
How are you planning to get the notes into your program? Will they be hard coded, or will you send them in somehow?
I don't really see the problem with converting from the RTTTL format to one than would use a count of beats that the note should last. In my example code for instance, the count would just be 32/duration.
Sorted it, I’ll neaten the code up and publish when I get a bit more time. Briefly here’s how it works.
class musicPlayer
void play()
private void playNote()
you call play with a rtttl string and the method sets up the bpm, octave etc and stores them in the class, then it calls playnote().
This plays the 1st note by parsing the string from where play() left off. Then it sets a SWtimer for the duration of the note to call the next iteration of playNote().
The progress of the rtttl string is saved between iterations so no re-parsing is done, it just reads it a note at a time then sets tone and comes back at the end of the note to read the next one.