📄 bttalk.cpp
字号:
//
// 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.
//
//
#include <windows.h>
#include "utils.h"
#include "queue.h"
// events and channels
LPCTSTR pszChannels[] = {L"WaveIn0", L"WaveIn1", L"WaveOut0", L"WaveOut1"};
#define WAVEIN0 0
#define WAVEIN1 1
#define WAVEOUT0 2
#define WAVEOUT1 3
#define CHANNELS 4
#define HEADERS_PER_STREAM 8
#define BYTES_PER_HEADER 400 // 8bytes/ms ==> 400bytes=50ms
static DWORD g_dwDuration = 60000;
static BOOL g_bDispUnderruns = FALSE;
BOOL InitAudio(HANDLE* phEvents,
LPVOID* phWaves,
PWAVEHDR pStream0,
PWAVEHDR pStream1)
{
int i;
memset(phEvents, 0, sizeof(HANDLE) * CHANNELS);
memset(phWaves , 0, sizeof(void*) * CHANNELS);
memset(pStream0, 0, sizeof(WAVEHDR) * HEADERS_PER_STREAM);
memset(pStream1, 0, sizeof(WAVEHDR) * HEADERS_PER_STREAM);
for (i = 0; i < HEADERS_PER_STREAM; i++)
{
pStream0[i].lpData = (LPSTR) LocalAlloc(0, BYTES_PER_HEADER);
pStream1[i].lpData = (LPSTR) LocalAlloc(0, BYTES_PER_HEADER);
if (!pStream0[i].lpData || !pStream1[i].lpData) {
OutputMessage(L"LocalAlloc failed\n");
return FALSE;
}
pStream0[i].dwBufferLength = BYTES_PER_HEADER;
pStream1[i].dwBufferLength = BYTES_PER_HEADER;
}
for (i = 0; i < CHANNELS; i++)
{
phEvents[i] = CreateEvent(NULL, FALSE, FALSE, NULL);
if (!phEvents[i]) {
OutputMessage(L"CreateEvent failed %d\n", GetLastError());
return FALSE;
}
}
MMRESULT mmr;
WAVEFORMATEX wfx = {0};
wfx.wFormatTag = WAVE_FORMAT_PCM;
wfx.wBitsPerSample = 8;
wfx.nSamplesPerSec = 8000;
wfx.nChannels = 1;
wfx.nBlockAlign = wfx.nChannels * wfx.wBitsPerSample / 8;
wfx.nAvgBytesPerSec = wfx.nBlockAlign * wfx.nSamplesPerSec;
wfx.cbSize = 0;
mmr = waveInOpen((HWAVEIN*)&phWaves[WAVEIN0], 0, &wfx, (DWORD) phEvents[WAVEIN0], NULL, CALLBACK_EVENT);
if (mmr != MMSYSERR_NOERROR) {
OutputMessage(L"MMSYSERR %d opening WaveIn0 GetLastError=%d\n", mmr, GetLastError());
return FALSE;
}
mmr = waveInOpen((HWAVEIN*)&phWaves[WAVEIN1], 1, &wfx, (DWORD) phEvents[WAVEIN1], NULL, CALLBACK_EVENT);
if (mmr != MMSYSERR_NOERROR) {
OutputMessage(L"MMSYSERR %d opening WaveIn1 GetLastError=%d\n", mmr, GetLastError());
return FALSE;
}
mmr = waveOutOpen((HWAVEOUT*)&phWaves[WAVEOUT0], 0, &wfx, (DWORD) phEvents[WAVEOUT0], NULL, CALLBACK_EVENT);
if (mmr != MMSYSERR_NOERROR) {
OutputMessage(L"MMSYSERR %d opening WaveOut0 GetLastError=%d\n", mmr, GetLastError());
return FALSE;
}
mmr = waveOutOpen((HWAVEOUT*)&phWaves[WAVEOUT1], 1, &wfx, (DWORD) phEvents[WAVEOUT1], NULL, CALLBACK_EVENT);
if (mmr != MMSYSERR_NOERROR) {
OutputMessage(L"MMSYSERR %d opening WaveOut1 GetLastError=%d\n", mmr, GetLastError());
return FALSE;
}
return TRUE;
}
BOOL DeinitAudio ( HANDLE* phEvents,
LPVOID* phWaves,
PWAVEHDR pStream0,
PWAVEHDR pStream1)
{
int i;
if (phWaves[WAVEIN0]) {
waveInReset((HWAVEIN)phWaves[WAVEIN0]);
waveInClose((HWAVEIN)phWaves[WAVEIN0]);
}
if (phWaves[WAVEIN1]) {
waveInReset((HWAVEIN)phWaves[WAVEIN1]);
waveInClose((HWAVEIN)phWaves[WAVEIN1]);
}
if (phWaves[WAVEOUT0]) {
waveOutReset((HWAVEOUT)phWaves[WAVEOUT0]);
waveOutClose((HWAVEOUT)phWaves[WAVEOUT0]);
}
if (phWaves[WAVEOUT1]) {
waveOutReset((HWAVEOUT)phWaves[WAVEOUT1]);
waveOutClose((HWAVEOUT)phWaves[WAVEOUT1]);
}
for (i = 0; i < CHANNELS; i++)
{
if (phEvents[i]) {
CloseHandle(phEvents[i]);
}
}
for (i = 0; i < HEADERS_PER_STREAM; i++)
{
if (pStream0[i].lpData) {
LocalFree((HLOCAL)pStream0[i].lpData);
}
if (pStream1[i].lpData) {
LocalFree((HLOCAL)pStream1[i].lpData);
}
}
memset(phEvents, 0, sizeof(HANDLE) * CHANNELS);
memset(phWaves, 0, sizeof(void*) * CHANNELS);
memset(pStream0, 0, sizeof(WAVEHDR) * HEADERS_PER_STREAM);
memset(pStream1, 0, sizeof(WAVEHDR) * HEADERS_PER_STREAM);
return TRUE;
}
BOOL AddAllHeaders(HWAVEIN hwi, PWAVEHDR pHeaders)
{
int i;
MMRESULT mmr;
for (i = 0; i < HEADERS_PER_STREAM; i++)
{
mmr = waveInPrepareHeader(hwi, pHeaders + i, sizeof(WAVEHDR));
if (mmr) {
OutputMessage(L"waveInPrepareHeader failed %d GetLastError=%d\n", mmr, GetLastError());
return FALSE;
}
mmr = waveInAddBuffer(hwi, pHeaders + i, sizeof(WAVEHDR));
if (mmr) {
OutputMessage(L"waveInAddBuffer failed %d GetLastError=%d\n", mmr, GetLastError());
return FALSE;
}
}
return TRUE;
}
//
// stream0: read data from device0 and write it to device1 (WAVEIN0 -> WAVEOUT1)
// stream1: read data from device1 and write it to device0 (WAVEIN1 -> WAVEOUT0)
//
BOOL DoAudioTransfers()
{
HANDLE hEvents[CHANNELS];
LPVOID phWaves[CHANNELS];
WAVEHDR HdrStream0[HEADERS_PER_STREAM];
WAVEHDR HdrStream1[HEADERS_PER_STREAM];
CSimpleQueue RunningQ[CHANNELS];
MMRESULT mmr;
DWORD dwWaitRet;
int i;
if (!InitAudio(hEvents, phWaves, HdrStream0, HdrStream1)) {
DeinitAudio(hEvents, phWaves, HdrStream0, HdrStream1);
return FALSE;
}
if (!AddAllHeaders((HWAVEIN)phWaves[WAVEIN0], HdrStream0)) {
OutputMessage(L"Adding headers to WAVEIN0 failed\n");
DeinitAudio(hEvents, phWaves, HdrStream0, HdrStream1);
return FALSE;
}
if (!AddAllHeaders((HWAVEIN)phWaves[WAVEIN1], HdrStream1)) {
OutputMessage(L"Adding headers to WAVEIN1 failed\n");
DeinitAudio(hEvents, phWaves, HdrStream0, HdrStream1);
return FALSE;
}
for (i = 0; i < HEADERS_PER_STREAM; i++)
RunningQ[WAVEIN0].Enqueue(i);
for (i = 0; i < HEADERS_PER_STREAM; i++)
RunningQ[WAVEIN1].Enqueue(i);
mmr = waveInStart((HWAVEIN)phWaves[WAVEIN0]);
if (mmr) {
OutputMessage(L"waveInStart(WAVEIN0) failed %d GetLastError=%d\n", mmr, GetLastError());
DeinitAudio(hEvents, phWaves, HdrStream0, HdrStream1);
return FALSE;
}
mmr = waveInStart((HWAVEIN)phWaves[WAVEIN1]);
if (mmr) {
OutputMessage(L"waveInStart(WAVEIN1) failed %d GetLastError=%d\n", mmr, GetLastError());
DeinitAudio(hEvents, phWaves, HdrStream0, HdrStream1);
return FALSE;
}
OutputMessage(L"<<<< Audio transfers in progress... >>>>\n");
__int64 i64Start = GetFileTimeMilliseconds();
__int64 i64End = i64Start + g_dwDuration;
__int64 i64LastDisplay = 0;
__int64 i64DisplayTime = 5000; // time display every 5000 ms
while (1)
{
do {
dwWaitRet = WaitForMultipleObjects(CHANNELS, hEvents, FALSE, 200);
if ( dwWaitRet == WAIT_TIMEOUT ) {
} else if ( (dwWaitRet < WAIT_OBJECT_0) || (dwWaitRet >= WAIT_OBJECT_0 + CHANNELS) ) {
OutputMessage(L"WaitForMultipleObjects failed %d GetLastError=%d\n", dwWaitRet, GetLastError());
DeinitAudio(hEvents, phWaves, HdrStream0, HdrStream1);
return FALSE;
}
__int64 i64Now = GetFileTimeMilliseconds();
if (i64Now >= i64End) {
OutputMessage(L">>>> Duration (%dms) of audio transfer expired. Closing audio... <<<< \n", g_dwDuration);
DeinitAudio(hEvents, phWaves, HdrStream0, HdrStream1);
return TRUE;
}
if ( (i64Now - i64LastDisplay) > i64DisplayTime) {
OutputMessage(L">>>> %I64dms remaining in audio transfer...\n", i64End - i64Now);
i64LastDisplay = i64Now;
}
} while (dwWaitRet == WAIT_TIMEOUT);
DWORD dwSource = dwWaitRet;
DWORD dwTarget = (DWORD)-1;
PWAVEHDR pHeaders = NULL;
switch (dwWaitRet)
{
case WAVEIN0:
dwTarget = WAVEOUT1;
pHeaders = HdrStream0;
break;
case WAVEIN1:
dwTarget = WAVEOUT0;
pHeaders = HdrStream1;
break;
case WAVEOUT0:
dwTarget = WAVEIN1;
pHeaders = HdrStream1;
break;
case WAVEOUT1:
dwTarget = WAVEIN0;
pHeaders = HdrStream0;
break;
}
while (1)
{
DWORD dwIdx;
BOOL bHeadComplete = FALSE;
if (RunningQ[dwSource].GetNumElements() > 0) {
RunningQ[dwSource].PeekDequeue(&dwIdx);
if (pHeaders[dwIdx].dwFlags & WHDR_DONE) {
bHeadComplete = TRUE;
}
}
if (!bHeadComplete) {
break;
}
RunningQ[dwSource].Dequeue(&dwIdx);
RunningQ[dwTarget].Enqueue(dwIdx);
if (RunningQ[dwSource].GetNumElements() == 0) {
if (g_bDispUnderruns) {
OutputMessage(L"-- Underrun on channel %s\n", pszChannels[dwSource]);
}
}
if ( (dwSource == WAVEIN0) || (dwSource == WAVEIN1) ) {
mmr = waveInUnprepareHeader((HWAVEIN)phWaves[dwSource], pHeaders + dwIdx, sizeof(WAVEHDR));
if (mmr) {
OutputMessage(L"waveInUnprepareHeader(%s) failed %d GetLastError=%d\n", pszChannels[dwSource], mmr, GetLastError());
DeinitAudio(hEvents, phWaves, HdrStream0, HdrStream1);
return FALSE;
}
pHeaders[dwIdx].dwBytesRecorded = 0;
pHeaders[dwIdx].dwFlags = 0;
pHeaders[dwIdx].dwLoops = 0;
mmr = waveOutPrepareHeader((HWAVEOUT)phWaves[dwTarget], pHeaders + dwIdx, sizeof(WAVEHDR));
if (mmr) {
OutputMessage(L"waveOutPrepareHeader(%s) failed %d GetLastError=%d\n", pszChannels[dwTarget], mmr, GetLastError());
DeinitAudio(hEvents, phWaves, HdrStream0, HdrStream1);
return FALSE;
}
mmr = waveOutWrite((HWAVEOUT)phWaves[dwTarget], pHeaders + dwIdx, sizeof(WAVEHDR));
if (mmr) {
OutputMessage(L"waveOutWrite(%s) failed %d GetLastError=%d\n", pszChannels[dwTarget], mmr, GetLastError());
DeinitAudio(hEvents, phWaves, HdrStream0, HdrStream1);
return FALSE;
}
} else {
mmr = waveOutUnprepareHeader((HWAVEOUT)phWaves[dwSource], pHeaders + dwIdx, sizeof(WAVEHDR));
if (mmr) {
OutputMessage(L"waveOutUnprepareHeader(%s) failed %d GetLastError=%d\n", pszChannels[dwSource], mmr, GetLastError());
DeinitAudio(hEvents, phWaves, HdrStream0, HdrStream1);
return FALSE;
}
pHeaders[dwIdx].dwBufferLength = BYTES_PER_HEADER;
pHeaders[dwIdx].dwBytesRecorded = 0;
pHeaders[dwIdx].dwFlags = 0;
pHeaders[dwIdx].dwLoops = 0;
mmr = waveInPrepareHeader((HWAVEIN)phWaves[dwTarget], pHeaders + dwIdx, sizeof(WAVEHDR));
if (mmr) {
OutputMessage(L"waveInPrepareHeader(%s) failed %d GetLastError=%d\n", pszChannels[dwTarget], mmr, GetLastError());
DeinitAudio(hEvents, phWaves, HdrStream0, HdrStream1);
return FALSE;
}
mmr = waveInAddBuffer((HWAVEIN)phWaves[dwTarget], pHeaders + dwIdx, sizeof(WAVEHDR));
if (mmr) {
OutputMessage(L"waveInAddBuffer(%s) failed %d GetLastError=%d\n", pszChannels[dwTarget], mmr, GetLastError());
DeinitAudio(hEvents, phWaves, HdrStream0, HdrStream1);
return FALSE;
}
}
}
}
ASSERT(0); // no fall through
return 0;
}
int __stdcall WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nShowCmd)
{
DWORD dwRetVal = ERROR_SUCCESS;
DWORD dwInDevices;
DWORD dwOutDevices;
HANDLE hCurThread = (HANDLE) GetCurrentThreadId();
CeSetThreadPriority(hCurThread, 156);
dwInDevices = waveInGetNumDevs();
dwOutDevices = waveOutGetNumDevs();
if ( (dwInDevices != 2) || (dwOutDevices != 2) ) {
OutputMessage(L"There must be two sound devices on the system (InDevices=%d OutDevices=%d)\n", dwInDevices, dwOutDevices);
dwRetVal = 1;
goto exit;
}
DoAudioTransfers();
exit:
return dwRetVal;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -