I do want to add this stuff, for you and for all sorts of other projects. If you can take a few moments to give me a better picture of what you're doing, it would really help me. I like to think about creating APIs in terms of actual projects. Thinking about them from the perspective of underlying tech usually ends up with extremely efficient but difficult to use libraries.
Well, here's another project that I've been working on makes extensive use of sound:
It doesn't use a Teensy; it uses a board of my own design based on the Arduino, but with an ATMega1284, 12 bit DAC, and 3W amplifier on board; but I may eventually transition it over to the Teensy because I'd like to be able to mix multiple sound effects.
Anyway, for that kit I modified the WaveHC lib to allow the automatic looping of a sound file, and though I'd have liked perfectly seamless transitions between sound effects, polling in a loop to see when a sound had finished playing so I could begin the next ended up being good enough in this case.
The sound effects generally fall into one of three categories: head, body, or tail. A firing sound effect might have one sound for the head which transitions into a seamlessly looping body, and then multiple tails based on how long the user was firing. Sometimes however, as in the case of the powerup sound, I have a head that plays first, and a body which I queue up when the head begins to play and then the body automatically begins to loop once the head is finished playing.
(This is all something I would not expect your library to handle as there's too many different things you might want to do. I might want to randomly choose which head to play based on how long you'd previously been firing for example.)
Other times I will have a sound effect like the hum playing and the user will trigger another sound effect, but that sound effect does not exist because they deleted it for whatever reason. In that case I don't want to stop the looping hum if there's no new sound to play but the WaveHC lib doesn't allow for that, so I have to access the SD card to check to see if the file exists before I issue the command to play it. I see your library suffers from the same issue:
Code:
bool AudioPlaySDcardWAV::play(const char *filename)
{
stop();
wavfile = SD.open(filename);
if (!wavfile) return false;
Of course there's no right way to handle this, but if you changed it to only stop the wav if the file exists then the programmer could check to see if the play() failed and stop it himself if that's the behavior he wanted, whereas with this setup I have to halt the audio playback before checking to see if the wav exists...
...or do I? I can call that function at any time to play a new wav file, and it's trying to open the file, and returning failure if it fails to do so, but it's not stopping the audio interrupts when it does this. So is that safe to do without stopping the interrupts? Or should this function be stopping the audio interrupts?
Anyway, the basic things I need are:
1. The ability to seamlessly loop a sound. Right now with the pops in there when it reaches the end of a file, even if manually restarting it turns out to be good enough I wouldn't be able to loop an effect.
2. The ability to seamlessly transition from one sound to another. Again, those pops would get in the way of this.
3. The ability to check to see if a sound exists before I play it in case I don't want the previous sound to stop playing if it doesn't. It seems like I can do this already. And it's less of an issue when I can mix sound effects.
And I think that's pretty much it, for this particular kit anyway. The rest of the stuff I mentioned is stuff I can implement in my own API as long as the basic functionality is there.
This is my sound code. I forgot to mention that when playing a sound I also have the ability to clear any queued sounds, and I have the ability to specify if a sound that is queued will need to loop. And as you can see I modified WaveHC to accept a "loop" parameter, so when it reaches the end of the file in its interrupt it automatically begins the next file so the transition is seamless. This may be necessary when moving from the head to the body in my hum sound effect to avoid any pops, I'm not sure, but when firing the transition is so abrupt I don't need it to be perfectly seamless:
Code:
/*
This function plays a sound file.
Unless otherwise specified, playing a sound file with play() will clear the queue of pending sounds.
Normally, only updateSound() needs to call play without it clearing the queue.
If the file does not exist, play() will not halt any currently playing sound, or clear the queue.
*/
int play(char* filename, boolean loopsound = false, boolean emptyQueue = true, boolean logerror = false) {
FatReader tmp;
//if (!FatReader::exists(root2, filename)) return 0; // Check to see if file exists before doing anything.
cli(); // Disable interrupts
if (!tmp.open(root, filename)) { sei(); return 0; }
sei(); // Enable interrupts
wave.stop(); // Halt playback of sound currently playing.
if (emptyQueue) { clearQueue(); }
if (!file.open(root, filename)) { // "root" and "file" are global variables.
if (logerror) { Serial1.println(F("Failed to open WAV file!")); halt(4); }
return 0; // Fail!
}
if (!wave.create(file)) {
if (logerror) { Serial1.println(F("Failed to create WAV object!")); halt(5); }
return 0; // Fail!
}
setVolume(1.0); // Must be set after call to wave.create()
wave.play(loopsound);
return 1; // Success!
}
/*
This function queues up a file to be played after the current one ends. It's used when you want to play a sound effect which has a head and a loop.
*/
void queue(char* filename, boolean loopsound = false, boolean logerror = false) {
queuedFile = filename;
queuedLoop = loopsound;
}
/*
This function begins playing the queued file when the current one ends.
*/
void updateSound() {
if (!wave.isplaying) {
if (queuedFile != NULL) { play(queuedFile, queuedLoop); }
}
}
/*
This function stops any sounds in the queue from being played back.
*/
void clearQueue() {
queuedFile = NULL;
}
/*
This function sets the current playback volume.
newVolume = New volume level. Valid range: [0..1]
Currently, volume must be reset after every new wave file is loaded, but I should probably edit the wave file library to just use a global value.
*/
void setVolume(float newVolume) {
wave.volume = newVolume * 4095.0;
}