I am trying to understand a few things about this SoundManagerAlsa script and multi-threading.
Here is how the SoundManagerAlsa script is being called:
Code:
{
SoundManager::SoundManager soundManager(portNumber);
soundManager.openSoundDevice();
soundManager.startServer();
cmdQueue.get(); // blocks until a command is available in the queue
soundManager.shutDown();
}
So from the top here's openSoundDevice():
Code:
void SoundManager::openSoundDevice()
{
initiateSoundDevice(); // Close then reopen the device.
}
now initiateSoundDevice():
Code:
void SoundManager::initiateSoundDevice()
{
using namespace TimeStuff;
if (!m_toneDev || m_audioSetup.threadIsRunning) // If the thread is already running then shut it down and reopen the audio device.
{
printf("%10.3f Shut down thread.\n", fToHMS(now()));
shutDown();
assert(!m_audioSetup.threadIsRunning);
if (m_audioSetup.threadIsRunning)
{
perror("Unable to shut down audio update thread");
}
else
{
printf("%10.3f Open sound device.\n", fToHMS(now()));
for (int i=0 ; audioDeviceName[i] && !m_toneDev ; i++)
{
int result = snd_pcm_open(&m_toneDev, audioDeviceName[i], SND_PCM_STREAM_PLAYBACK, 0);
if (result < 0)
{
std::cerr << "Unable to open sound device(" << audioDeviceName[i] << "), tone canceled. Errmsg: " << snd_strerror(result) << std::endl;
m_toneDev = NULL;
}
else
{
printf("%10.3f Sound device (%s) open. Handle: %p\n", fToHMS(now()), audioDeviceName[i], m_toneDev);
}
}
}
}
if (m_toneDev && !m_audioSetup.threadIsRunning) // Audio device is currently open.
{
m_audioSetup.clear();
/**
* We only want 2 periods in the ring buffer.
* This is enough to allow uninterrupted playback, and it allows us to interrupt ongoing tones reasonably fast.
*
* We can have two periods in the buffer, and a third waiting to get access to the buffer.
* This means that if we want to interrupt an ongoing tone, the reaction time will be between two and three periods.
*
* With
* sampleRate = 44100 samples per second
* 2 periods
* 1024 samples pr period
* we get:
* timePerPeriod = nSamplesPerPeriod / sampleRate = 1024 / 44100 = 23.2ms
*
* This means that this thread will run once per 23,2ms when a tone is being played.
* And if the tone must be interrupted the response time is in the range [2*23.2 - 3*23.2] = [46.4 - 69.6]ms.
*/
snd_pcm_hw_params_t *hwparams;
snd_pcm_sw_params_t *swparams;
snd_pcm_hw_params_alloca(&hwparams); // Allocation (alloca) is deleted when function exits.
snd_pcm_sw_params_alloca(&swparams);
// Get all parameters:
if (m_toneDev) handleSetupError(snd_pcm_hw_params_any( m_toneDev, hwparams ), "Unable to read hardware parameters for sound device.");
if (m_toneDev) handleSetupError(snd_pcm_hw_params_set_rate_resample( m_toneDev, hwparams, 1 ), "Unable to set re-sampling for sound device.");
if (m_toneDev) handleSetupError(snd_pcm_hw_params_set_access( m_toneDev, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED ), "Unable to set interleaved format for sound device.");
if (m_toneDev) handleSetupError(snd_pcm_hw_params_set_format( m_toneDev, hwparams, SND_PCM_FORMAT_S16_LE ), "Unable to set sample format for sound device.");
if (m_toneDev) handleSetupError(snd_pcm_hw_params_set_channels( m_toneDev, hwparams, m_audioSetup.nChannels ), "Unable to set number of channels for sound device.");
if (m_toneDev) handleSetupError(snd_pcm_hw_params_set_rate_near( m_toneDev, hwparams, &m_audioSetup.sampleRate, NULL), "Unable to set sample rate for sound device.");
if (m_toneDev) handleSetupError(snd_pcm_hw_params_set_buffer_size_near(m_toneDev, hwparams, &m_audioSetup.bufferSize ), "Unable to set buffer size for sound device.");
if (m_toneDev) handleSetupError(snd_pcm_hw_params_set_period_size_near(m_toneDev, hwparams, &m_audioSetup.periodSize, NULL), "Unable to set period size for sound device.");
if (m_toneDev) handleSetupError(snd_pcm_hw_params( m_toneDev, hwparams ), "Unable to write hw parameters to sound device.");
if (m_toneDev)
{
m_audioSetup.msPerSample = 1000.0 / static_cast<double>(m_audioSetup.sampleRate);
printf("%10.3f Sound device Hardware setup:\n", fToHMS(now()));
printf(" Millisec per sample: %fms\n", m_audioSetup.msPerSample);
printf(" Millisec per period: %fms\n", m_audioSetup.msPerSample * static_cast<double>(m_audioSetup.periodSize));
printf(" Samples per period: %u\n", static_cast<unsigned>(m_audioSetup.periodSize));
printf(" Samples per buffer: %u\n", static_cast<unsigned>(m_audioSetup.bufferSize));
m_audioSetup.audioBuffer = new int16_t[m_audioSetup.periodSize * m_audioSetup.nChannels];
assert(m_audioSetup.audioBuffer);
if (!m_audioSetup.audioBuffer)
{
std::cerr << "Unable to allocate audio buffer. Tone cancelled." << std::endl;
snd_pcm_close(m_toneDev);
m_toneDev = NULL;
}
}
if (m_toneDev)
{
m_audioSetup.threadIsRunning = true;
m_toneThread = threadfunc::ThreadCreate( 6, threadUpdateTone, this );
}
}
}
The portion that confuses me is:
Code:
if (!m_toneDev || m_audioSetup.threadIsRunning) // If the thread is already running then shut it down and reopen the audio device.
{
printf("%10.3f Shut down thread.\n", fToHMS(now()));
shutDown();
assert(!m_audioSetup.threadIsRunning);
if (m_audioSetup.threadIsRunning)
{
perror("Unable to shut down audio update thread");
}
else
{
printf("%10.3f Open sound device.\n", fToHMS(now()));
for (int i=0 ; audioDeviceName[i] && !m_toneDev ; i++)
{
int result = snd_pcm_open(&m_toneDev, audioDeviceName[i], SND_PCM_STREAM_PLAYBACK, 0);
if (result < 0)
{
std::cerr << "Unable to open sound device(" << audioDeviceName[i] << "), tone canceled. Errmsg: " << snd_strerror(result) << std::endl;
m_toneDev = NULL;
}
else
{
printf("%10.3f Sound device (%s) open. Handle: %p\n", fToHMS(now()), audioDeviceName[i], m_toneDev);
}
}
}
}
What is going on here? Why would you have a multi-threaded audio device? You only have one speaker, you can't play two tones at once, so whats the point? I mean it even stops the currently running thread from running whenever initiateSoundDevice() is called doesn't it? Why would this be a threaded application?