📄 wavepdd.cxx
字号:
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft end-user
// license agreement (EULA) under which you licensed this SOFTWARE PRODUCT.
// If you did not accept the terms of the EULA, you are not authorized to use
// this source code. For a copy of the EULA, please see the LICENSE.RTF on your
// install media.
//
// -----------------------------------------------------------------------------
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Module Name:
//
// wavepdd.cxx
//
// Abstract:
// The PDD (Platform Dependent Driver) is responsible for
// communicating with the audio circuit to start and stop playback
// and/or recording and initialize and deinitialize the circuits.
//
// Functions:
// PDD_AudioMessage
// PDD_AudioInitialize
// PDD_AudioDeinitialize
// PDD_AudioPowerHandler
// PDD_WaveProc
//
// Notes:
//
// -----------------------------------------------------------------------------
#include <windows.h>
#include <types.h>
#include <memory.h>
#include <excpt.h>
#include <nkintr.h>
#include <ceddk.h>
#include <cardserv.h>
#include <devload.h>
#include <ddkreg.h>
#include <giisr.h>
#include <wavedbg.h>
#include <waveddsi.h>
#include "wavemdd.h"
#include "bthsco.h"
typedef struct _STREAM_INFO_PDD
{
BOOL bOpened;
BOOL bStarted;
WAVEFORMATEX wfx; // does not have the trailing info after cbSize
} STREAM_INFO_PDD;
// the events that the audio thread responds to
#define AUDIO_EVENT_EXIT_THREAD 0
#define AUDIO_EVENT_RX_DATA 1
#define AUDIO_EVENT_TX_DATA 2
#define AUDIO_EVENT_STACK_DOWN 3
#define AUDIO_EVENT_STACK_UP 4
#define AUDIO_EVENT_SCO_LINK_DISCONNECT 5
#define NUM_AUDIO_EVENTS 6
#define ABORT_IN 0x1
#define ABORT_OUT 0x2
#define SILENCE 0
#define RX_BUFFER_SIZE 4096
#define MAX_BASEBAND_CONNECTIONS 20
#define DEFAULT_SCO_CONNECT_IN_TIMEOUT 10000
#define DEFAULT_SCO_PERSIST_TIMEOUT 60000
#define TX_BUFFER_SIZE 256
//
// file globals
//
// critical section and variables controlled by it
static CRITICAL_SECTION g_csStreamInfo[2]; // critical section for each g_StreamInfo
static STREAM_INFO_PDD g_StreamInfo[2]; // one for WAPI_IN and one for WAPI_OUT
// critical section and variables controlled by it
static CRITICAL_SECTION g_csRxBuffer; // critical section for g_pRxBuffer,g_dwRxBufferLen,g_dwRxBufferStart
static BYTE g_pRxBuffer[RX_BUFFER_SIZE]; // circular buffer for receiving audio data
static DWORD g_dwRxBufferLen; // length of data in g_pRxBuffer
static DWORD g_dwRxBufferStart; // start of data in g_pRxBuffer
// critical section and variables controlled by it
static CRITICAL_SECTION g_csTxBuffer; // critical section for g_pTxBuffer,g_dwTxPacketsToSend
static UCHAR g_TxBuffer[TX_BUFFER_SIZE]; // static buffer for transmit data
static UCHAR* g_pTxBuffer; // pointer to buffer for transmit data
static DWORD g_dwTxPacketsToSend; // how many more packets should be DataPacketDown'ed
static HANDLE g_hAudioEvents[NUM_AUDIO_EVENTS]; // handle to events used by audio thread
static HANDLE g_hReadyEvent; // handle to event to signal driver is ready
static CRITICAL_SECTION g_csPddWaveProcLock; // lock acquired everytime in PDD_WaveProc
static SCO_INTERFACE g_ScoIntf; // interface to SCO functions
static ULONG g_dwVolume; // current volume setting
static DWORD g_dwScoPacketSize; // how big each packet should be
static DWORD g_dwConcurrentPackets; // max concurrent SCO packets in HCI
static USHORT g_usScoConnHandle; // handle to SCO connection
// registry settings
static BD_ADDR g_bdAddressIn; // what address to accept a connection from (zero ==> all)
static BD_ADDR g_bdAddressOut; // what address to connect to
static BOOL g_bConnectOut; // outbound or inbound connection establishment
static DWORD g_dwConnectInTimeout; // timeout for inbound connection establishment
static DWORD g_dwAirCoding; // CVSD, ALAW, ULAW (for writing device's voice setting)
static BOOL g_fSigned; // Converts data to 8-bit signed samples
static BOOL g_f16Bit; // Converts data from 16-bit to 8-bit
// macros for locking/unlocking g_StreamInfo[apidir]
#define LOCK_STREAM_INFO(apidir) EnterCriticalSection(&(g_csStreamInfo[apidir]))
#define UNLOCK_STREAM_INFO(apidir) LeaveCriticalSection(&(g_csStreamInfo[apidir]))
// macros for locking/unlocking g_pRxBuffer,g_dwRxBufferLen,g_dwRxBufferStart
#define LOCK_RXBUFFER() EnterCriticalSection(&g_csRxBuffer)
#define UNLOCK_RXBUFFER() LeaveCriticalSection(&g_csRxBuffer)
// macros for locking/unlocking g_pTxBuffer,g_dwTxPacketsToSend
#define LOCK_TXBUFFER() EnterCriticalSection(&g_csTxBuffer)
#define UNLOCK_TXBUFFER() LeaveCriticalSection(&g_csTxBuffer)
// message handlers
static MMRESULT PddMsg_Close (WAPI_INOUT apidir);
static MMRESULT PddMsg_GetDevCaps (WAPI_INOUT apidir, PVOID pCaps, UINT wSize);
static MMRESULT PddMsg_Open (WAPI_INOUT apidir, LPWAVEFORMATEX lpFormat, BOOL bQueryFormatOnly);
static MMRESULT PddMsg_Start (WAPI_INOUT apidir, PWAVEHDR pwh);
static MMRESULT PddMsg_Stop (WAPI_INOUT apidir);
static MMRESULT PddMsg_OutGetVolume (PULONG pulVolume);
static MMRESULT PddMsg_OutSetVolume (ULONG ulVolume);
DWORD AudioThreadProc(LPVOID lpParameter);
// SCO callbacks
static int PddSco_DataPacketUp (void *pUserData, USHORT hConnection, PUCHAR pBuffer, DWORD dwBufferLen);
static int PddSco_DataPacketDown (void *pUserData, void *pPacketUserContext, USHORT hConnection, DWORD dwError);
static int PddSco_StackEvent (void *pUserData, int iEvent);
// -----------------------------------------------------------------------------
// PddSco_DataPacketUp
// -----------------------------------------------------------------------------
static int
PddSco_DataPacketUp (
void *pUserData,
USHORT hConnection,
PUCHAR pBuffer,
DWORD dwBufferLen
)
{
DWORD dwEndIndex;
DWORD dwSpaceLeft;
DWORD dwCopyPass1;
DWORD dwCopyPass2;
//BSS_INTMSG2("PddSco_DataPacketUp : h=%#x len=%d", hConnection, dwBufferLen);
//
// discard this data if it is not being captured
//
LOCK_STREAM_INFO(WAPI_IN);
BOOL bRunning = (g_StreamInfo[WAPI_IN].bOpened && g_StreamInfo[WAPI_IN].bStarted);
UNLOCK_STREAM_INFO(WAPI_IN);
if (!bRunning) {
return ERROR_SUCCESS;
}
// general guideline: whenever we are going to lose data
// scrap the oldest data...i.e. keep the most recent data
ASSERT(dwBufferLen <= RX_BUFFER_SIZE);
if (dwBufferLen > RX_BUFFER_SIZE) {
// RX_BUFFER_SIZE should be large enough for any packet size
// otherwise, we will lose data on every packet!
BSS_INTMSG("Amount of data in packet exceeds RxBuffer max capacity!");
// keep the most recent RX_BUFFER_SIZE worth
pBuffer += (dwBufferLen - RX_BUFFER_SIZE);
dwBufferLen = RX_BUFFER_SIZE;
}
// we will copy all the data into g_pRxBuffer overwriting old data (if req'd)
LOCK_RXBUFFER();
dwEndIndex = (g_dwRxBufferStart + g_dwRxBufferLen) % RX_BUFFER_SIZE; // idx to write new data to
dwSpaceLeft = RX_BUFFER_SIZE - dwEndIndex; // # bytes until end of buffer
dwCopyPass1 = min(dwBufferLen, dwSpaceLeft); // # bytes to write from end of data to end of buffer
dwCopyPass2 = dwBufferLen - dwCopyPass1; // # bytes to write from begginging of buffer
memcpy(g_pRxBuffer + dwEndIndex, pBuffer, dwCopyPass1);
// wrap around
if (dwCopyPass2 > 0) {
memcpy(g_pRxBuffer, pBuffer + dwCopyPass1, dwCopyPass2);
}
if ( (g_dwRxBufferLen + dwBufferLen) > RX_BUFFER_SIZE ) {
// old data has been scrapped
DWORD dwOverwrite = (g_dwRxBufferLen + dwBufferLen) - RX_BUFFER_SIZE;
g_dwRxBufferLen = RX_BUFFER_SIZE;
g_dwRxBufferStart = (g_dwRxBufferStart + dwOverwrite) % RX_BUFFER_SIZE;
} else {
g_dwRxBufferLen += dwBufferLen;
}
UNLOCK_RXBUFFER();
SetEvent(g_hAudioEvents[AUDIO_EVENT_RX_DATA]);
return ERROR_SUCCESS;
}
// -----------------------------------------------------------------------------
// PddSco_DataPacketDown
// -----------------------------------------------------------------------------
static int
PddSco_DataPacketDown (
void *pUserData,
void *pPacketUserContext,
USHORT hConnection,
DWORD dwError
)
{
if (dwError) {
BSS_ERRMSG2("PddSco_DataPacketDown : h=%#x err=%d", hConnection, dwError);
}
LOCK_TXBUFFER();
g_dwTxPacketsToSend++;
UNLOCK_TXBUFFER();
//DEBUGMSG(1, (L"BTSCOSND: PddSco_DataPacketDown %d. GetTickCount=%u\n", g_dwTxPacketsToSend, GetTickCount()));
SetEvent(g_hAudioEvents[AUDIO_EVENT_TX_DATA]);
return ERROR_SUCCESS;
}
// -----------------------------------------------------------------------------
// PddSco_StackEvent
// -----------------------------------------------------------------------------
static int
PddSco_StackEvent (
void *pUserData,
int iEvent
)
{
switch (iEvent)
{
case BTH_STACK_DISCONNECT:
case BTH_STACK_DOWN:
SetEvent(g_hAudioEvents[AUDIO_EVENT_STACK_DOWN]);
break;
case BTH_STACK_UP:
SetEvent(g_hAudioEvents[AUDIO_EVENT_STACK_UP]);
break;
case SCO_LINK_DISCONNECT:
SetEvent(g_hAudioEvents[AUDIO_EVENT_SCO_LINK_DISCONNECT]);
break;
}
return ERROR_SUCCESS;
}
void ConvertM08toM16(UINT8 *pSrc, UINT16 * pDst, INT nSamples)
{
while (nSamples-- > 0) {
UINT8 sample8 = *pSrc++;
UINT16 m16;
if (g_fSigned) {
m16 = (UINT16)(sample8 << 8);
}
else {
m16 = (UINT16)((sample8 ^ 0x80) << 8);
}
*pDst++ = m16;
}
}
// -----------------------------------------------------------------------------
// RxBufferToWaveBuffers
// -----------------------------------------------------------------------------
// Copies data from g_pRxBuffer to wave buffer
// -----------------------------------------------------------------------------
static void
RxBufferToWaveBuffers(
PWAVEHDR pwh
)
{
LOCK_STREAM_INFO(WAPI_IN);
BOOL fConvertTo16Bit = (g_StreamInfo[WAPI_IN].wfx.wBitsPerSample == 16);
UNLOCK_STREAM_INFO(WAPI_IN);
while ( (g_dwRxBufferLen > 0) && (pwh != NULL) )
{
if (pwh->dwBytesRecorded >= pwh->dwBufferLength) {
// this WAVEHDR is full
pwh = pwh->lpNext;
continue;
}
// transfer min (rx_bytes_to_end_of_buffer, rx_data_len, wavebuf_space)
DWORD dwRxBytesToEnd = RX_BUFFER_SIZE - g_dwRxBufferStart;
DWORD dwBytesWaveHdr = pwh->dwBufferLength - pwh->dwBytesRecorded;
if (fConvertTo16Bit) {
dwBytesWaveHdr /= 2; // Converting to 16bit needs double space
}
DWORD dwNumBytesToCopy = g_dwRxBufferLen;
if (dwNumBytesToCopy > dwBytesWaveHdr) {
dwNumBytesToCopy = dwBytesWaveHdr;
}
if (dwNumBytesToCopy > dwRxBytesToEnd) {
dwNumBytesToCopy = dwRxBytesToEnd;
}
if (fConvertTo16Bit) {
ConvertM08toM16(g_pRxBuffer + g_dwRxBufferStart, (UINT16*)(pwh->lpData + pwh->dwBytesRecorded), dwNumBytesToCopy);
pwh->dwBytesRecorded += (dwNumBytesToCopy * 2);
}
else {
if (g_fSigned) {
UCHAR* p = g_pRxBuffer + g_dwRxBufferStart;
for (DWORD i = 0; i < dwNumBytesToCopy; i++) {
*p ^= 0x80;
p++;
}
}
memcpy (pwh->lpData + pwh->dwBytesRecorded,
g_pRxBuffer + g_dwRxBufferStart,
dwNumBytesToCopy);
pwh->dwBytesRecorded += dwNumBytesToCopy;
}
g_dwRxBufferLen -= dwNumBytesToCopy;
g_dwRxBufferStart = (g_dwRxBufferStart + dwNumBytesToCopy) % RX_BUFFER_SIZE;
}
if (g_dwRxBufferLen == 0) {
g_dwRxBufferStart = 0; // if no data then normalize the buffer (for cleanliness)
}
}
// -----------------------------------------------------------------------------
// ConvertS16toM08
// -----------------------------------------------------------------------------
// Convert from 16-bit stereo to 8-bit mono
// -----------------------------------------------------------------------------
void ConvertS16toM08(UINT32 *pSrc, UINT8 * pDst, INT nSamples)
{
while (nSamples-- > 0) {
UINT32 sample32 = *pSrc++;
INT16 L = (INT16) (sample32 & 0xffff);
INT16 R = (INT16) (sample32 >> 16);
INT16 M = (L+R) >> 1; // merge left/right w/ simple average
UINT m8;
if (g_fSigned) {
m8 = (UINT8) (M >> 8);
}
else {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -