📄 streamoutput.cpp
字号:
#include "wavemain.h"
#include "dma.h"
#include "dmadrv.h"
int StreamOutput::m_instances = 0;
int StreamOutput::m_streamcnt = 0;
class HardwareControl *StreamOutput::m_pDeviceContext = NULL;
HANDLE StreamOutput::m_stThread = INVALID_HANDLE_VALUE;
DWORD StreamOutput::m_stthreadID = 0;
volatile DWORD StreamOutput::m_stThreadStop = 0;
volatile DWORD StreamOutput::m_stThreadDone = 0;
HANDLE StreamOutput::m_stEventDma = NULL;
DWORD StreamOutput::m_stsysIntrDMA = SYSINTR_UNDEFINED;
DWORD StreamOutput::m_stdmaCtl = 0;
// Start thread
static void StartThread(class StreamOutput *pOutputStreamContext)
{
pOutputStreamContext->InterruptThread();
}
//********************************************************************
// Initialization and de-init functions
//********************************************************************
// Constructors
StreamOutput::StreamOutput(class HardwareControl *pDeviceContext)
{
DMASETUP_T dmaStp;
DMAINFO_T *dmainfo;
// Is this the first instance?
m_instances++;
if (m_instances == 1)
{
// Defaults
m_pDeviceContext = NULL;
m_stThread = INVALID_HANDLE_VALUE;
m_stthreadID = 0;
m_stThreadStop = 0;
m_stThreadDone = 0;
m_stEventDma = NULL;
m_stsysIntrDMA = SYSINTR_UNDEFINED;
m_stdmaCtl = 0;
// Stream is locked out
m_streamcnt = 1;
// Save device context
m_pDeviceContext = pDeviceContext;
// Default flag states
m_stThread = INVALID_HANDLE_VALUE;
m_stthreadID = 0;
m_stThreadStop = 0;
m_stThreadDone = 0;
// Create DMA output control thread
m_stThread = CreateThread((LPSECURITY_ATTRIBUTES)NULL, 0,
(LPTHREAD_START_ROUTINE) &StartThread,
this, 0, &m_stthreadID);
if (m_stThread == NULL)
{
RETAILMSG(1,
(TEXT("OutputStreamContext: Cannot initialize audio thread\r\n")));
m_stThread = INVALID_HANDLE_VALUE;
m_stThreadDone = 1;
return;
}
// Setup DMA for the I2S1 TX channel
dmaStp.dmaCh = DMAC_AUDIO_TX_CH;
dmaStp.perID = DMA_PERID_I2S1_DMA1;
dmaStp.SrcInc = 1;
dmaStp.DestInc = 0;
dmaStp.SrcWidth = DMAC_CHAN_SRC_WIDTH_16;
dmaStp.DestWidth = DMAC_CHAN_DEST_WIDTH_16;
dmaStp.SrcBurstSize = DMAC_CHAN_SRC_BURST_4;
dmaStp.DestBurstSize = DMAC_CHAN_DEST_BURST_4;
dmaStp.perFlowSource = DMAC_CHAN_FLOW_D_M2P;
dmaStp.destPeripheral = DMAC_DEST_PERIP(DMA_PERID_I2S1_DMA1);
dmaStp.srcPeripheral = 0;
m_stdmaCtl = dmaAllocate(&dmaStp, (4 * 1024)); // Two 2K buffers
if (m_stdmaCtl == 0)
{
// Couldn't setup DMA
RETAILMSG(1, (TEXT("OutputStreamContext: Critical error setting up DMA\r\n")));
m_stdmaCtl = 0;
return;
}
// Create DMA event
m_stEventDma = CreateEvent(NULL, FALSE, FALSE, NULL);
if (m_stEventDma == NULL)
{
RETAILMSG(1,
(TEXT("OutputStreamContext: Failed to create DMA handler event.\r\n")));
m_stEventDma = INVALID_HANDLE_VALUE;
return;
}
// Get sysintr value for DMA channel interrupt
dmainfo = dmaGetInfo(m_stdmaCtl);
if (!KernelIoControl(IOCTL_HAL_REQUEST_SYSINTR, &dmainfo->irq,
sizeof(dmainfo->irq), &m_stsysIntrDMA, sizeof(m_stsysIntrDMA), NULL))
{
RETAILMSG(1,
(TEXT("OutputStreamContext: Failed to request the DMA sysintr.\r\n")));
m_stsysIntrDMA = SYSINTR_UNDEFINED;
return;
}
RETAILMSG(1,
(TEXT("OutputStreamContext: DMA sysintr is %d %d\r\n"), dmainfo->irq, m_stsysIntrDMA));
// Save buffer data
m_buffPhy [0] = (PBYTE) dmainfo->pBuffPhy;
m_buffVirt [0] = (PBYTE) dmainfo->pBuffVirt;
m_buffPhy [1] = m_buffPhy [0] + 2048;
m_buffVirt [1] = m_buffVirt [0] + 2048;
m_buffSizeBytes = dmainfo->buffSize;
RETAILMSG(1,
(TEXT("OutputStreamContext: DMA buffer PHY 0x%x VIRT 0x%x Size %d\r\n"),
m_buffPhy [0], m_buffVirt [0], m_buffSizeBytes));
// Bind interrupt to event
if (InterruptInitialize(m_stsysIntrDMA, m_stEventDma, NULL, 0) == FALSE)
{
// Cannot initialize interrupt
RETAILMSG(1,
(TEXT("OutputStreamContext: Cannot initialize DMA interrupt\r\n")));
return;
}
// Stream is free now
m_streamcnt = 0;
// Setup initial values
m_lpWaveHdrHead = NULL;
m_lpWaveHdrCurrent = NULL;
m_lpWaveHdrTail = NULL;
m_lpCurrData = NULL;
m_lpCurrDataEnd = NULL;
m_dwByteCount = 0;
m_dwLoopCount = 0;
m_bRunning = FALSE;
m_buffsUsed = 0;
// Setup an initial clocking rate for I2S and the CODEC
m_pDeviceContext->hwAdjustSampleRate(16000, 16);
}
}
// Destructor
StreamOutput::~StreamOutput()
{
int to;
m_instances--;
if (m_instances == 0)
{
// Close event handle
if (m_stEventDma == INVALID_HANDLE_VALUE)
{
// Stop thread if it is running
m_stThreadStop = 1;
to = 20;
while ((to > 0) && (m_stThreadDone > 0))
{
SetEvent(m_stEventDma);
Sleep(5);
to--;
}
m_stThread = INVALID_HANDLE_VALUE;
m_stthreadID = 0;
// Close event handle
CloseHandle(m_stEventDma);
m_stEventDma = INVALID_HANDLE_VALUE;
}
// Disable interrupt and return sysIntr value
if (m_stsysIntrDMA != SYSINTR_UNDEFINED)
{
InterruptDisable(m_stsysIntrDMA);
KernelIoControl(IOCTL_HAL_RELEASE_SYSINTR, &m_stsysIntrDMA,
sizeof(m_stsysIntrDMA), NULL, 0, NULL);
m_stsysIntrDMA = SYSINTR_UNDEFINED;
}
// Return DMA buffer and close channel
dmaDeAllocate(m_stdmaCtl);
m_stdmaCtl = 0;
m_pDeviceContext = NULL;
// Safety
m_streamcnt = 1;
}
}
//********************************************************************
// Stream open/close and info functions
//********************************************************************
// Open a stream for input or output
DWORD StreamOutput::stOpenStream(LPWAVEOPENDESC lpWOD,
DWORD dwFlags,
DWORD **StPtr)
{
LPWAVEFORMATEX pWaveFormat = lpWOD->lpFormat;
// Compression is not supported
if ((dwFlags & WAVE_FORMAT_DIRECT) != 0)
{
return MMSYSERR_NOTSUPPORTED;
}
if (!stIsSupportedFormat(pWaveFormat))
{
return WAVERR_BADFORMAT;
}
// Exit now if this was a format check only
if ((dwFlags & WAVE_FORMAT_QUERY) != 0)
{
// No new class is instanced here
return MMSYSERR_NOERROR;
}
// Return 0 if the stream is already open
if (m_streamcnt == 1)
{
return MMSYSERR_ERROR;
}
// Save copy of wave format
memcpy(&m_WaveFormat, pWaveFormat, sizeof(WAVEFORMATEX));
if (m_WaveFormat.wFormatTag == WAVE_FORMAT_PCM)
{
// Doesn't matter, but recommend by Microsoft
m_WaveFormat.cbSize = 0;
}
m_dwFlags = dwFlags;
// Save for callbacks
m_pfnCallback = (DRVCALLBACK *)lpWOD->dwCallback;
m_hWave = lpWOD->hWave;
m_dwInstance = lpWOD->dwInstance;
// Enable I2S clocks and power
m_pDeviceContext->codecPowerUp(1);
m_pDeviceContext->codecClockUp(1);
m_pDeviceContext->I2SFlushFIFO();
// Unpause channel
m_pDeviceContext->I2SPause(0);
// Setup I2S capabilities based on passed data
if (m_WaveFormat.nChannels == 1)
{
m_pDeviceContext->I2SSetStereo(0);
}
else
{
m_pDeviceContext->I2SSetStereo(1);
}
// Setup clocking
m_pDeviceContext->hwAdjustSampleRate(m_WaveFormat.nSamplesPerSec,
m_WaveFormat.wBitsPerSample);
// Clear DMA and enable DMA interrupts
InterruptDone(m_stsysIntrDMA);
// DEBUGMSG(1, (TEXT(" Channels = %d\r\n"), m_WaveFormat.nChannels));
// DEBUGMSG(1, (TEXT(" sampleRate = %d\r\n"), m_WaveFormat.nSamplesPerSec));
// DEBUGMSG(1, (TEXT(" bitsPerSample = %d\r\n"), m_WaveFormat.wBitsPerSample));
// DEBUGMSG(1, (TEXT(" blockAlign = %d\r\n"), m_WaveFormat.nBlockAlign));
// Signal ACM that stream is opened
stDoCallbackStreamOpened();
*StPtr = (DWORD *) this;
return MMSYSERR_NOERROR;
}
// Close an open stream
DWORD StreamOutput::stCloseStream(void)
{
m_streamcnt--;
stDoCallbackStreamClosed();
return MMSYSERR_NOERROR;
}
// Return pointer to current WAVE structure
WAVEFORMATEX *StreamOutput::stGetWaveFormat(void)
{
return &m_WaveFormat;
}
// Return device context for class
class HardwareControl *StreamOutput::stGetDeviceContext(void)
{
return m_pDeviceContext;
}
//********************************************************************
// Stream buffer control functions
//********************************************************************
// Queue a buffer
DWORD StreamOutput::stQueueBuffer(LPWAVEHDR lpWaveHdr)
{
if (!(lpWaveHdr->dwFlags & WHDR_PREPARED))
{
return WAVERR_UNPREPARED;
}
lpWaveHdr->dwFlags |= WHDR_INQUEUE;
lpWaveHdr->dwFlags &= ~WHDR_DONE;
lpWaveHdr->lpNext = NULL;
lpWaveHdr->dwBytesRecorded = 0;
if (!m_lpWaveHdrHead)
{
m_lpWaveHdrHead = lpWaveHdr;
}
else
{
m_lpWaveHdrTail->lpNext = lpWaveHdr;
}
m_lpWaveHdrTail = lpWaveHdr;
// Note: Even if head & tail are valid, current may be NULL if we're in the middle of
// a loop and ran out of data. So, we need to check specifically against current to
// decide if we need to initialize it.
if (!m_lpWaveHdrCurrent)
{
m_lpWaveHdrCurrent = lpWaveHdr;
m_lpCurrData = (PBYTE)lpWaveHdr->lpData;
m_lpCurrDataEnd = (PBYTE)lpWaveHdr->lpData + lpWaveHdr->dwBufferLength;
if (lpWaveHdr->dwFlags & WHDR_BEGINLOOP) // if this is the start of a loop block
{
m_dwLoopCount = lpWaveHdr->dwLoops; // save # of loops
}
}
if (!m_bRunning)
{
stStart();
}
return MMSYSERR_NOERROR;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -