📄 dsoundcardpmo.cpp
字号:
PhysicalMediaOutput::Pause();
}
void
DSoundCardPMO::
Resume()
{
m_bLMCsaidToPlay = true;
// If the LMC is pausing then resume manually
// otherwise, it's a seek or a stop (DSClear has been called)
// and we let the Monitor decide to start playing when there are data
if ((m_DSBufferManager.pDSSecondaryBuffer) && (m_bIsBufferEmptyNow == false))
m_DSBufferManager.pDSSecondaryBuffer->Play( 0, 0, DSBPLAY_LOOPING);
PhysicalMediaOutput::Resume();
}
Error
DSoundCardPMO::
Reset(bool user_stop)
{
if(user_stop)
{
DSReset();
DSClear();
}
return kError_NoErr;
}
void
DSoundCardPMO::
Clear()
{
DSClear();
PhysicalMediaOutput::Clear();
}
void
DSoundCardPMO::
HandleTimeInfoEvent(PMOTimeInfoEvent *pEvent)
{
MediaTimeInfoEvent* pmtpi;
int32 hours, minutes, seconds;
int iTotalTime = 0;
if (m_samples_per_second <= 0)
return;
// Since the Monitor modifies the m_iTotalBytesWritten, we meed exclusive access
m_pDSBufferSem->Wait();
if (pEvent->GetFrameNumber() != m_iLastFrame + 1)
{
m_iTotalBytesWritten = m_samples_per_frame *
pEvent->GetFrameNumber() *
m_iBytesPerSample;
}
m_iLastFrame = pEvent->GetFrameNumber();
iTotalTime = (m_iTotalBytesWritten) / (m_iBytesPerSample * m_samples_per_second);
m_pDSBufferSem->Signal();
hours = iTotalTime / 3600;
minutes = (iTotalTime - hours) / 60;
seconds = iTotalTime - hours * 3600 - minutes * 60;
if (minutes < 0 || minutes > 59 || seconds < 0 || seconds > 59)
return;
pmtpi = new MediaTimeInfoEvent(hours, minutes, seconds, 0,
(float)iTotalTime, 0);
m_pTarget->AcceptEvent(pmtpi);
}
Error
DSoundCardPMO::
GetData(void *&pBuffer)
{
Error eRet;
unsigned iRead;
iRead = m_data_size;
eRet = ((EventBuffer *)m_pInputBuffer)->BeginRead(pBuffer, iRead);
if (eRet != kError_NoErr)
return eRet;
eRet = ((EventBuffer *)m_pInputBuffer)->EndRead(0);
if (eRet != kError_NoErr)
return eRet;
return kError_NoErr;
}
Error
DSoundCardPMO::
Write(void *pBuffer)
{
int nState;
int32 wrote;
for(; !m_bExit;)
{
// wait until we are in a good state to write
nState = DSMonitorBufferState();
if (nState == -1 || nState == OVERFLOW || nState == STOPPING)
{
::Sleep(WRITESLEEPTIME);
}
else
break;
}
if (m_bExit)
return kError_Interrupt;
return DSWriteToSecBuffer(wrote, pBuffer, m_data_size);
}
Error
DSoundCardPMO::
DSWriteToSecBuffer(int32& wrote, void *pBuffer, int32 length)
{
HRESULT hResult;
LPVOID ptr1;
DWORD dwBytes1;
LPVOID ptr2;
DWORD dwBytes2;
Error result = kError_UnknownErr;
unsigned iRead;
iRead = m_data_size;
result = ((EventBuffer *)m_pInputBuffer)->BeginRead(pBuffer, iRead);
if (result != kError_NoErr)
return result;
// grab the write semaphore
m_pDSWriteSem->Wait();
// get a lock to a memory region to write to
hResult = m_DSBufferManager.pDSSecondaryBuffer->Lock( m_DSBufferManager.dwWritePtr,
length,
&ptr1,
&dwBytes1,
&ptr2, &dwBytes2,
0);
if (hResult == DSERR_BUFFERLOST)
{
hResult = m_DSBufferManager.pDSSecondaryBuffer->Restore();
if (FAILED(hResult))
{
wrote = 0;
m_pDSWriteSem->Signal();
m_pContext->log->Log(LogOutput, "Exiting DSWriteToSecBuffer with DSound error\n");
return result;
}
hResult = m_DSBufferManager.pDSSecondaryBuffer->Lock( m_DSBufferManager.dwWritePtr,
length,
&ptr1,
&dwBytes1,
&ptr2,
&dwBytes2,
0);
}
if (FAILED(hResult))
{
wrote = 0;
m_pDSWriteSem->Signal();
m_pContext->log->Log(LogOutput, "Exiting DSWriteToSecBuffer with DSound error\n");
return result;
}
// copy the samples
if (pBuffer)
memcpy(ptr1, pBuffer, dwBytes1);
else
memset(ptr1, 0, dwBytes1);
m_DSBufferManager.dwWritePtr += dwBytes1;
if (m_DSBufferManager.dwWritePtr >= m_DSBufferManager.dwBufferSize)
{
m_DSBufferManager.dwWritePtr = 0;
}
if (ptr2)
{
if (pBuffer)
memcpy(ptr2, (unsigned char *)pBuffer + dwBytes1, dwBytes2);
else
memset(ptr2, 0, dwBytes2);
m_DSBufferManager.dwWritePtr = dwBytes2;
}
// unlock the memory region
m_DSBufferManager.pDSSecondaryBuffer->Unlock( ptr1, dwBytes1, ptr2, dwBytes2);
if (FAILED(hResult))
{
wrote = 0;
m_pDSWriteSem->Signal();
m_pContext->log->Log(LogOutput, "Exiting DSWriteToSecBuffer with DSound error\n");
return result;
}
wrote = length;
// release the write semaphore
m_pDSWriteSem->Signal();
result = ((EventBuffer *)m_pInputBuffer)->EndRead(iRead);
if (result != kError_NoErr)
return result;
return kError_NoErr;
}
int32
DSoundCardPMO::
DSMonitorBufferState()
{
DWORD dwBuffered;
DSState new_state;
int32 wrote;
DWORD dwReadPos;
DWORD dwWritePos;
if (m_DSBufferManager.pDSSecondaryBuffer == NULL)
return -1;
m_pDSBufferSem->Wait();
new_state = m_DSBufferManager.state;
m_DSBufferManager.pDSSecondaryBuffer->GetCurrentPosition( &dwReadPos,
&dwWritePos);
if (m_DSBufferManager.dwWritePtr >= dwReadPos)
{
dwBuffered = m_DSBufferManager.dwWritePtr - dwReadPos;
}
else
{
dwBuffered = (m_DSBufferManager.dwBufferSize - dwReadPos) +
m_DSBufferManager.dwWritePtr;
}
if (dwReadPos >= m_DSBufferManager.dwOldReadPos)
{
m_iTotalBytesWritten += dwReadPos - m_DSBufferManager.dwOldReadPos;
}
else
{
m_iTotalBytesWritten += (m_DSBufferManager.dwBufferSize - m_DSBufferManager.dwOldReadPos) +
dwReadPos;
}
m_DSBufferManager.dwOldReadPos = dwReadPos;
switch(m_DSBufferManager.state)
{
case UNDERFLOW:
// m_pContext->log->Log(LogOutput, "UNDERFLOW : %d\n", dwBuffered*100/m_DSBufferManager.dwBufferSize);
if (dwBuffered > m_DSBufferManager.dwTrigger)
{
if (dwBuffered >= m_DSBufferManager.dwBufferSize)
{
new_state = OVERFLOW;
}
else
{
new_state = NORMAL;
}
// restart the buffer
if (m_bLMCsaidToPlay && m_bIsBufferEmptyNow)
m_DSBufferManager.pDSSecondaryBuffer->Play( 0, 0, DSBPLAY_LOOPING);
m_bIsBufferEmptyNow = false;
}
break;
case NORMAL:
// m_pContext->log->Log(LogOutput, "NORMAL : %d\n", dwBuffered*100/m_DSBufferManager.dwBufferSize);
if (dwBuffered < m_DSBufferManager.dwUnderflow)
{
new_state = STOPPING;
DSWriteToSecBuffer(wrote, NULL, m_DSBufferManager.dwZerofill);
}
else if (dwBuffered >= m_DSBufferManager.dwOverflow)
{
new_state = OVERFLOW;
}
else
{
new_state = NORMAL;
}
break;
case OVERFLOW:
// m_pContext->log->Log(LogOutput, "OVERFLOW : %d\n", dwBuffered*100/m_DSBufferManager.dwBufferSize);
if (dwBuffered < m_DSBufferManager.dwUnderflow)
{
new_state = STOPPING;
DSWriteToSecBuffer( wrote, NULL, m_DSBufferManager.dwZerofill);
}
else if (dwBuffered < m_DSBufferManager.dwOverflow)
{
new_state = NORMAL;
}
break;
case STOPPING:
// m_pContext->log->Log(LogOutput, "STOPPING : %d\n", dwBuffered*100/m_DSBufferManager.dwBufferSize);
if (dwBuffered < m_DSBufferManager.dwZerofill)
{
Reset(true);
new_state = UNDERFLOW;
}
break;
}
m_DSBufferManager.state = new_state;
m_pDSBufferSem->Signal();
return new_state;
}
void
DSoundCardPMO::
DSReset()
{
if (m_DSBufferManager.pDSSecondaryBuffer)
{
m_DSBufferManager.pDSSecondaryBuffer->Stop();
}
}
void
DSoundCardPMO::
DSClear()
{
HRESULT hResult;
LPVOID ptr;
DWORD dwBytes;
// grab the write semaphore
m_pDSWriteSem->Wait();
// init the fields
m_DSBufferManager.dwWritePtr = 0;
m_DSBufferManager.dwOldReadPos = 0;
m_DSBufferManager.state = UNDERFLOW;
// reset the play cursor
m_DSBufferManager.pDSSecondaryBuffer->SetCurrentPosition(0);
// write zeros into secondary buffer
hResult = m_DSBufferManager.pDSSecondaryBuffer->Lock( 0,
m_DSBufferManager.dwBufferSize,
&ptr,
&dwBytes,
NULL,
NULL,
0);
if (SUCCEEDED(hResult))
{
memset(ptr, 0, m_DSBufferManager.dwBufferSize);
m_DSBufferManager.pDSSecondaryBuffer->Unlock( ptr,
dwBytes,
NULL,
0);
}
m_bIsBufferEmptyNow = true;
// grab the write semaphore
m_pDSWriteSem->Signal();
}
void
DSoundCardPMO::
StartWorkerThread(void *pVoidBuffer)
{
((DSoundCardPMO*)pVoidBuffer)->WorkerThread();
}
void
DSoundCardPMO::
WorkerThread(void)
{
void* pBuffer;
Error eErr;
Event* pEvent;
int32 iValue;
// Don't do anything until resume is called.
m_pPauseSem->Wait();
// Wait for prebuffer period
PreBuffer();
m_pContext->prefs->GetPrefInt32(kDecoderThreadPriorityPref, &iValue);
m_pBufferThread->SetPriority(iValue);
for(; !m_bExit;)
{
if (m_bPause)
{
m_pPauseSem->Wait();
continue;
}
// Loop until we get an Init event from the LMC
if (!m_initialized)
{
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(;;)
{
if (m_bPause || m_bExit)
break;
eErr = GetData(pBuffer);
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)->GetEvent();
if (pEvent == NULL)
continue;
if (pEvent->Type() == PMO_Init)
Init(((PMOInitEvent *)pEvent)->GetInfo());
if (pEvent->Type() == PMO_Reset)
Reset(true);
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);
return;
}
break;
}
if (m_bPause || m_bExit)
continue;
Write(pBuffer);
m_pLmc->Wake();
UpdateBufferStatus();
}
m_pContext->log->Log(LogOutput, "PMO: Soundcard thread exiting\n");
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -