📄 alsapmo.cpp
字号:
if (err < 0)
{
ReportError("Cannot initialized audio device.");
return (Error)pmoError_DeviceOpenFailed;
}
err = snd_pcm_channel_prepare(m_handle, SND_PCM_CHANNEL_PLAYBACK);
if (err < 0)
{
ReportError("Cannot initialized audio device.");
return (Error)pmoError_DeviceOpenFailed;
}
memcpy(myInfo, info, sizeof(OutputInfo));
snd_pcm_channel_setup_t aInfo;
aInfo.channel = SND_PCM_CHANNEL_PLAYBACK;
err = snd_pcm_channel_setup(m_handle,&aInfo);
if (err < 0)
{
ReportError("Cannot initialized audio device.");
return (Error)pmoError_DeviceOpenFailed;
}
m_iOutputBufferSize = aInfo.buf.block.frag_size * aInfo.buf.block.frags;
m_iBytesPerSample = info->number_of_channels * (info->bits_per_sample / 8);
m_properlyInitialized = true;
return kError_NoErr;
}
Error AlsaPMO::Reset(bool user_stop) {
if (user_stop)
snd_pcm_playback_drain(m_handle);
else
snd_pcm_playback_flush(m_handle);
snd_pcm_playback_prepare(m_handle);
return kError_NoErr;
}
void AlsaPMO::Pause(void)
{
m_iBaseTime = -1;
PhysicalMediaOutput::Pause();
}
bool AlsaPMO::WaitForDrain(void)
{
snd_pcm_channel_status_t ainfo;
for(; !m_bExit && !m_bPause; )
{
ainfo.channel = SND_PCM_CHANNEL_PLAYBACK;
snd_pcm_channel_status(m_handle,&ainfo);
if (ainfo.underrun || ainfo.status == SND_PCM_STATUS_UNDERRUN)
{
return true;
}
WasteTime();
}
return false;
}
void AlsaPMO::HandleTimeInfoEvent(PMOTimeInfoEvent *pEvent)
{
MediaTimeInfoEvent *pmtpi;
int32 hours, minutes, seconds;
int iTotalTime = 0;
snd_pcm_channel_status_t ainfo;
if (m_iBaseTime < 0)
{
m_iBaseTime = (pEvent->GetFrameNumber() *
myInfo->samples_per_frame) /
myInfo->samples_per_second;
}
ainfo.channel = SND_PCM_CHANNEL_PLAYBACK;
snd_pcm_channel_status(m_handle,&ainfo);
iTotalTime = ainfo.scount / (m_iBytesPerSample * myInfo->samples_per_second);
iTotalTime = (iTotalTime + m_iBaseTime) % 86400;
hours = iTotalTime / 3600;
minutes = (iTotalTime / 60) % 60;
seconds = iTotalTime % 60;
if (hours < 0 ||
minutes < 0 || minutes > 59 ||
seconds < 0 || seconds > 59)
return;
pmtpi = new MediaTimeInfoEvent(hours, minutes, seconds, 0,
iTotalTime, 0);
m_pTarget->AcceptEvent(pmtpi);
}
void AlsaPMO::StartWorkerThread(void *pVoidBuffer)
{
((AlsaPMO*)pVoidBuffer)->WorkerThread();
}
void AlsaPMO::WorkerThread(void)
{
void *pBuffer;
Error eErr;
int iRet = -1;
Event *pEvent;
snd_pcm_channel_status_t ainfo;
// Don't do anything until resume is called.
m_pPauseSem->Wait();
// Sleep for a pre buffer period
PreBuffer();
// The following should be abstracted out into the general thread
// classes:
#ifdef __linux__
struct sched_param sParam;
sParam.sched_priority = sched_get_priority_max(SCHED_OTHER);
pthread_setschedparam(pthread_self(), SCHED_OTHER, &sParam);
#endif
ainfo.channel = SND_PCM_CHANNEL_PLAYBACK;
for(; !m_bExit;)
{
if (m_bPause)
{
m_pPauseSem->Wait();
continue;
}
// Loop until we get an Init event from the LMC
if (!m_properlyInitialized)
{
pEvent = ((EventBuffer *)m_pInputBuffer)->GetEvent();
if (pEvent == NULL)
{
m_pLmc->Wake();
WasteTime();
continue;
}
if (pEvent->Type() == PMO_Init)
{
if (IsError(Init(((PMOInitEvent *)pEvent)->GetInfo())))
{
delete pEvent;
break;
}
}
delete pEvent;
continue;
}
// Set up reading a block from the buffer. If not enough bytes are
// available, sleep for a little while and try again.
for(;;)
{
eErr = ((EventBuffer *)m_pInputBuffer)->BeginRead(pBuffer,
m_iDataSize);
if (eErr == kError_EndOfStream || eErr == kError_Interrupt)
break;
if (eErr == kError_NoDataAvail)
{
m_pLmc->Wake();
CheckForBufferUp();
WasteTime();
continue;
}
// Is there an event pending that we need to take care of
// before we play this block of samples?
if (eErr == kError_EventPending)
{
pEvent = ((EventBuffer *)m_pInputBuffer)->PeekEvent();
if (pEvent == NULL)
continue;
if (pEvent->Type() == PMO_Quit &&
((EventBuffer *)m_pInputBuffer)->GetNumBytesInBuffer() > 0)
{
if (WaitForDrain())
{
Reset(true);
m_pTarget->AcceptEvent(new Event(INFO_DoneOutputting));
return;
}
continue;
}
pEvent = ((EventBuffer *)m_pInputBuffer)->GetEvent();
if (pEvent->Type() == PMO_Init)
Init(((PMOInitEvent *)pEvent)->GetInfo());
if (pEvent->Type() == PMO_Reset)
Reset(false);
if (pEvent->Type() == PMO_Info)
HandleTimeInfoEvent((PMOTimeInfoEvent *)pEvent);
if (pEvent->Type() == PMO_Quit)
{
delete pEvent;
m_pTarget->AcceptEvent(new Event(INFO_DoneOutputting));
return;
}
delete pEvent;
continue;
}
if (IsError(eErr))
{
ReportError("Internal error occured.");
m_pContext->log->Error("Cannot read from buffer in PMO "
"worker tread: %d\n", eErr);
break;
}
break;
}
// Now write the block to the audio device. If the block doesn't
// all fit, pause and loop until the entire block has been played.
// This loop could be written using non-blocking io...
for(;!m_bExit && !m_bPause;)
{
iRet = snd_pcm_write(m_handle,pBuffer,m_iDataSize);
if (iRet == -EAGAIN)
{
CheckForBufferUp();
WasteTime();
continue;
}
if (iRet == -EIO)
{
snd_pcm_channel_prepare(m_handle, SND_PCM_CHANNEL_PLAYBACK);
continue;
}
break;
}
if (m_bExit)
{
m_pInputBuffer->EndRead(0);
return;
}
if (m_bPause)
{
if (iRet == -EAGAIN)
m_pInputBuffer->EndRead(0);
else
{
m_pInputBuffer->EndRead(iRet);
UpdateBufferStatus();
}
continue;
}
if (iRet < 0)
{
m_pInputBuffer->EndRead(0);
ReportError("Could not write sound data to the soundcard.");
m_pContext->log->Error("Failed to write to the soundcard: %s\n",
strerror(errno));
break;
}
m_pInputBuffer->EndRead(iRet);
m_pLmc->Wake();
UpdateBufferStatus();
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -