⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 streamoutput.cpp

📁 NXP LPC3000系列 wince BSP包
💻 CPP
📖 第 1 页 / 共 2 页
字号:

#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 + -