You could take a few different approaches to get polyphonic sound in your application. Either you do your own mixing or get ALSA to do the mixing for you.
Probably the simplest approach (and the most resource heavy) would be to get ALSA to do the mixing for you. In this case, you open the ALSA dmix plugin once for each pad. You can create a class for each pad and thread them so they run independently. Each class loads the audio data to a buffer and when triggered plays it back to the ALSA dmix plugin. Here is an example (using gtkIOStream which provides C++ libsox audio file handling, threading and ALSA handling) :
#include <Sox.H>
#include <ALSA.H>
#include <Thread.H>
using namespace ALSA;
class Pad : public PlayBack, WaitingThread {
Eigen::Array<int, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> audio; // the Eigen array which holds the audio buffer
public:
/// Constructor opens the audio plawyback device and reads in the audio file data
Pad(char* devName, string audioFile) : PlayBack(devName) {
Sox<int> sox; // instantiate libsox
sox.openRead(audioFile); // open the audio file to read
sox.read(audio); // read in the audio data
}
/// Configure the audio playback ([see here for example][2])
configurePlayback(){
// set channels, format, etc.
}
/// Playback the entire audio sound
play(){
*this<<audio; // stream the audio out through the PlayBack class
}
// In our threaded method, we wait to be signalled then we do playback, then wait again, [see here for examples on thread signalling][3]
void *threadMain(void){
while (continue) {
cond.lock();
cond.wait(); // wait till signalled
play(); // playback the audio
cond.unLock(); // unlock to be signalled again
}
return NULL;
}
};
Pad pad1("dmix", "src/audiosExa/a1.wav"); // instantiate a pad
Pad pad2("dmix", "src/audiosExa/a2.wav"); // instantiate a pad
// in your UI thread, you need to run each of the Pad thread
pad1.run();
pad2.run();
// in your UI thread, you need to signal the waiting pad threads
// Looks something like this for each pad
pad1.cond.lock(); pad1.cond.signal(); pad1.cond.unlock();
If you want to do your own mixing, then you would track which audio pad is being played out and where in the pad's buffer you are, so that each time you need to play out an audio buffer, you sum them together. In code (using gtkIOStream) it would look similar to this snippet taken from ALSAPlaybackTest.C :
const string deviceName="hw:0,0"; // could also be "default"
Playback playBack(deviceName.c_str()); // open the playback device
// configure the parameters of the playback device
Sox<short int> sox1, sox2; // instantiate two audio file readers (libsox)
int ret=sox1.openRead("src/audiosExa/a1.wav"); // load in their audio to an Eigen Array data buffer
Eigen::Array<int, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> pad1, pad2;
sox1.read(pad1);
ret=sox2.openRead("src/audiosExa/a2.wav");
sox2.read(pad2);
// now loop to constantly output, using outBuf to sum all pads which require output
Eigen::Array<int, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> outBuf;
while (continue){
// construct your output buffer by adding portions of your pads here
// something like : outBuf=pad1.block(...)+pad2.block(...)
playBack<<outBuf; // play the audio data
}