Add a basic WaveOut device
It can still use more work, but it seems to work
This commit is contained in:
+343
-19
@@ -36,24 +36,71 @@
|
||||
typedef struct {
|
||||
// MMSYSTEM Device
|
||||
volatile ALboolean bWaveShutdown;
|
||||
HANDLE hWaveHdrEvent;
|
||||
HANDLE hWaveThreadEvent;
|
||||
HANDLE hWaveThread;
|
||||
DWORD ulWaveThreadID;
|
||||
ALint lWaveBuffersCommitted;
|
||||
WAVEHDR WaveBuffer[4];
|
||||
HANDLE hWaveHdrEvent;
|
||||
HANDLE hWaveThreadEvent;
|
||||
HANDLE hWaveThread;
|
||||
DWORD ulWaveThreadID;
|
||||
LONG lWaveBuffersCommitted;
|
||||
WAVEHDR WaveBuffer[4];
|
||||
|
||||
union {
|
||||
HWAVEIN In;
|
||||
HWAVEOUT Out;
|
||||
} hWaveHandle;
|
||||
|
||||
RingBuffer *pRing;
|
||||
ALsizei Frequency;
|
||||
|
||||
RingBuffer *pRing;
|
||||
} WinMMData;
|
||||
|
||||
|
||||
static const ALCchar woDefault[] = "WaveOut Default";
|
||||
|
||||
static ALCchar **PlaybackDeviceList;
|
||||
static ALuint NumPlaybackDevices;
|
||||
static ALCchar **CaptureDeviceList;
|
||||
static ALuint NumCaptureDevices;
|
||||
|
||||
|
||||
static void ProbePlaybackDevices(void)
|
||||
{
|
||||
ALuint i;
|
||||
|
||||
for(i = 0;i < NumPlaybackDevices;i++)
|
||||
free(PlaybackDeviceList[i]);
|
||||
|
||||
NumPlaybackDevices = waveOutGetNumDevs();
|
||||
PlaybackDeviceList = realloc(PlaybackDeviceList, sizeof(ALCchar*) * NumPlaybackDevices);
|
||||
for(i = 0;i < NumPlaybackDevices;i++)
|
||||
{
|
||||
WAVEOUTCAPS WaveCaps;
|
||||
|
||||
PlaybackDeviceList[i] = NULL;
|
||||
if(waveOutGetDevCaps(i, &WaveCaps, sizeof(WaveCaps)) == MMSYSERR_NOERROR)
|
||||
{
|
||||
char name[1024];
|
||||
ALuint count, j;
|
||||
|
||||
count = 0;
|
||||
do {
|
||||
if(count == 0)
|
||||
snprintf(name, sizeof(name), "%s via WaveOut", WaveCaps.szPname);
|
||||
else
|
||||
snprintf(name, sizeof(name), "%s #%d via WaveOut", WaveCaps.szPname, count+1);
|
||||
count++;
|
||||
|
||||
for(j = 0;j < i;j++)
|
||||
{
|
||||
if(strcmp(name, PlaybackDeviceList[j]) == 0)
|
||||
break;
|
||||
}
|
||||
} while(j != i);
|
||||
|
||||
PlaybackDeviceList[i] = strdup(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void ProbeCaptureDevices(void)
|
||||
{
|
||||
ALuint i;
|
||||
@@ -94,6 +141,84 @@ static void ProbeCaptureDevices(void)
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
WaveOutProc
|
||||
|
||||
Posts a message to 'PlaybackThreadProc' everytime a WaveOut Buffer is completed and
|
||||
returns to the application (for more data)
|
||||
*/
|
||||
static void CALLBACK WaveOutProc(HWAVEOUT hDevice,UINT uMsg,DWORD_PTR dwInstance,DWORD_PTR dwParam1,DWORD_PTR dwParam2)
|
||||
{
|
||||
ALCdevice *pDevice = (ALCdevice*)dwInstance;
|
||||
WinMMData *pData = pDevice->ExtraData;
|
||||
|
||||
(void)hDevice;
|
||||
(void)dwParam2;
|
||||
|
||||
if(uMsg != WOM_DONE)
|
||||
return;
|
||||
|
||||
// Decrement number of buffers in use
|
||||
InterlockedDecrement(&pData->lWaveBuffersCommitted);
|
||||
|
||||
if(pData->bWaveShutdown == AL_FALSE)
|
||||
{
|
||||
// Notify Wave Processor Thread that a Wave Header has returned
|
||||
PostThreadMessage(pData->ulWaveThreadID, uMsg, 0, dwParam1);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(pData->lWaveBuffersCommitted == 0)
|
||||
{
|
||||
// Signal Wave Buffers Returned event
|
||||
if(pData->hWaveHdrEvent)
|
||||
SetEvent(pData->hWaveHdrEvent);
|
||||
|
||||
// Post 'Quit' Message to WaveOut Processor Thread
|
||||
PostThreadMessage(pData->ulWaveThreadID, WM_QUIT, 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
PlaybackThreadProc
|
||||
|
||||
Used by "MMSYSTEM" Device. Called when a WaveOut buffer has used up its
|
||||
audio data.
|
||||
*/
|
||||
static DWORD WINAPI PlaybackThreadProc(LPVOID lpParameter)
|
||||
{
|
||||
ALCdevice *pDevice = (ALCdevice*)lpParameter;
|
||||
WinMMData *pData = pDevice->ExtraData;
|
||||
LPWAVEHDR pWaveHdr;
|
||||
ALuint FrameSize;
|
||||
MSG msg;
|
||||
|
||||
FrameSize = aluFrameSizeFromFormat(pDevice->Format);
|
||||
|
||||
while(GetMessage(&msg, NULL, 0, 0))
|
||||
{
|
||||
if(msg.message != WOM_DONE || pData->bWaveShutdown)
|
||||
continue;
|
||||
|
||||
pWaveHdr = ((LPWAVEHDR)msg.lParam);
|
||||
|
||||
aluMixData(pDevice, pWaveHdr->lpData, pWaveHdr->dwBufferLength/FrameSize);
|
||||
|
||||
// Send buffer back to play more data
|
||||
waveOutWrite(pData->hWaveHandle.Out, pWaveHdr, sizeof(WAVEHDR));
|
||||
InterlockedIncrement(&pData->lWaveBuffersCommitted);
|
||||
}
|
||||
|
||||
// Signal Wave Thread completed event
|
||||
if(pData->hWaveThreadEvent)
|
||||
SetEvent(pData->hWaveThreadEvent);
|
||||
|
||||
ExitThread(0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
WaveInProc
|
||||
|
||||
@@ -112,7 +237,7 @@ static void CALLBACK WaveInProc(HWAVEIN hDevice,UINT uMsg,DWORD_PTR dwInstance,D
|
||||
return;
|
||||
|
||||
// Decrement number of buffers in use
|
||||
pData->lWaveBuffersCommitted--;
|
||||
InterlockedDecrement(&pData->lWaveBuffersCommitted);
|
||||
|
||||
if(pData->bWaveShutdown == AL_FALSE)
|
||||
{
|
||||
@@ -161,7 +286,7 @@ static DWORD WINAPI CaptureThreadProc(LPVOID lpParameter)
|
||||
|
||||
// Send buffer back to capture more data
|
||||
waveInAddBuffer(pData->hWaveHandle.In,pWaveHdr,sizeof(WAVEHDR));
|
||||
pData->lWaveBuffersCommitted++;
|
||||
InterlockedIncrement(&pData->lWaveBuffersCommitted);
|
||||
}
|
||||
|
||||
// Signal Wave Thread completed event
|
||||
@@ -174,18 +299,191 @@ static DWORD WINAPI CaptureThreadProc(LPVOID lpParameter)
|
||||
}
|
||||
|
||||
|
||||
static ALCboolean WinMMOpenPlayback(ALCdevice *device, const ALCchar *deviceName)
|
||||
static ALCboolean WinMMOpenPlayback(ALCdevice *pDevice, const ALCchar *deviceName)
|
||||
{
|
||||
(void)device;
|
||||
(void)deviceName;
|
||||
WAVEFORMATEX wfexFormat;
|
||||
WinMMData *pData = NULL;
|
||||
UINT_PTR lDeviceID = 0;
|
||||
MMRESULT res;
|
||||
ALuint i = 0;
|
||||
|
||||
// Find the Device ID matching the deviceName if valid
|
||||
if(!deviceName || strcmp(deviceName, woDefault) == 0)
|
||||
lDeviceID = WAVE_MAPPER;
|
||||
else
|
||||
{
|
||||
if(!PlaybackDeviceList)
|
||||
ProbePlaybackDevices();
|
||||
|
||||
for(i = 0;i < NumPlaybackDevices;i++)
|
||||
{
|
||||
if(PlaybackDeviceList[i] &&
|
||||
strcmp(deviceName, PlaybackDeviceList[i]) == 0)
|
||||
{
|
||||
lDeviceID = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(i == NumPlaybackDevices)
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
pData = calloc(1, sizeof(*pData));
|
||||
if(!pData)
|
||||
{
|
||||
alcSetError(pDevice, ALC_OUT_OF_MEMORY);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
pDevice->ExtraData = pData;
|
||||
|
||||
if(aluChannelsFromFormat(pDevice->Format) >= 2)
|
||||
{
|
||||
if(aluBytesFromFormat(pDevice->Format) >= 2)
|
||||
pDevice->Format = AL_FORMAT_STEREO16;
|
||||
else
|
||||
pDevice->Format = AL_FORMAT_STEREO8;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(aluBytesFromFormat(pDevice->Format) >= 2)
|
||||
pDevice->Format = AL_FORMAT_MONO16;
|
||||
else
|
||||
pDevice->Format = AL_FORMAT_MONO8;
|
||||
}
|
||||
|
||||
memset(&wfexFormat, 0, sizeof(WAVEFORMATEX));
|
||||
wfexFormat.wFormatTag = WAVE_FORMAT_PCM;
|
||||
wfexFormat.nChannels = aluChannelsFromFormat(pDevice->Format);
|
||||
wfexFormat.wBitsPerSample = aluBytesFromFormat(pDevice->Format) * 8;
|
||||
wfexFormat.nBlockAlign = wfexFormat.wBitsPerSample *
|
||||
wfexFormat.nChannels / 8;
|
||||
wfexFormat.nSamplesPerSec = pDevice->Frequency;
|
||||
wfexFormat.nAvgBytesPerSec = wfexFormat.nSamplesPerSec *
|
||||
wfexFormat.nBlockAlign;
|
||||
wfexFormat.cbSize = 0;
|
||||
|
||||
if((res=waveOutOpen(&pData->hWaveHandle.Out, lDeviceID, &wfexFormat, (DWORD_PTR)&WaveOutProc, (DWORD_PTR)pDevice, CALLBACK_FUNCTION)) != MMSYSERR_NOERROR)
|
||||
{
|
||||
AL_PRINT("waveInOpen failed: %u\n", res);
|
||||
goto failure;
|
||||
}
|
||||
|
||||
pData->hWaveHdrEvent = CreateEvent(NULL, AL_TRUE, AL_FALSE, "WaveOutAllHeadersReturned");
|
||||
pData->hWaveThreadEvent = CreateEvent(NULL, AL_TRUE, AL_FALSE, "WaveOutThreadDestroyed");
|
||||
if(pData->hWaveHdrEvent == NULL || pData->hWaveThreadEvent == NULL)
|
||||
{
|
||||
AL_PRINT("CreateEvent failed: %lu\n", GetLastError());
|
||||
goto failure;
|
||||
}
|
||||
|
||||
pData->Frequency = pDevice->Frequency;
|
||||
|
||||
pDevice->szDeviceName = strdup((lDeviceID==WAVE_MAPPER) ? woDefault :
|
||||
PlaybackDeviceList[lDeviceID]);
|
||||
return ALC_TRUE;
|
||||
|
||||
failure:
|
||||
if(pData->hWaveThreadEvent)
|
||||
CloseHandle(pData->hWaveThreadEvent);
|
||||
if(pData->hWaveHdrEvent)
|
||||
CloseHandle(pData->hWaveHdrEvent);
|
||||
|
||||
if(pData->hWaveHandle.Out)
|
||||
waveOutClose(pData->hWaveHandle.Out);
|
||||
|
||||
free(pData);
|
||||
pDevice->ExtraData = NULL;
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
static void WinMMClosePlayback(ALCdevice *device)
|
||||
{
|
||||
(void)device;
|
||||
WinMMData *pData = (WinMMData*)device->ExtraData;
|
||||
|
||||
// Close the Wave device
|
||||
CloseHandle(pData->hWaveThreadEvent);
|
||||
pData->hWaveThreadEvent = 0;
|
||||
|
||||
CloseHandle(pData->hWaveHdrEvent);
|
||||
pData->hWaveHdrEvent = 0;
|
||||
|
||||
waveInClose(pData->hWaveHandle.In);
|
||||
pData->hWaveHandle.In = 0;
|
||||
|
||||
free(pData);
|
||||
device->ExtraData = NULL;
|
||||
}
|
||||
|
||||
static ALCboolean WinMMResetPlayback(ALCdevice *device)
|
||||
{
|
||||
WinMMData *pData = (WinMMData*)device->ExtraData;
|
||||
ALbyte *BufferData;
|
||||
ALint lBufferSize;
|
||||
ALuint i;
|
||||
|
||||
pData->hWaveThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)PlaybackThreadProc, (LPVOID)device, 0, &pData->ulWaveThreadID);
|
||||
if(pData->hWaveThread == NULL)
|
||||
return ALC_FALSE;
|
||||
|
||||
device->UpdateSize = (ALuint)((ALuint64)device->UpdateSize *
|
||||
pData->Frequency / device->Frequency);
|
||||
device->Frequency = pData->Frequency;
|
||||
|
||||
pData->lWaveBuffersCommitted = 0;
|
||||
|
||||
// Create 4 Buffers
|
||||
lBufferSize = device->UpdateSize*device->NumUpdates / 4;
|
||||
lBufferSize *= aluFrameSizeFromFormat(device->Format);
|
||||
|
||||
BufferData = calloc(4, lBufferSize);
|
||||
for(i = 0;i < 4;i++)
|
||||
{
|
||||
memset(&pData->WaveBuffer[i], 0, sizeof(WAVEHDR));
|
||||
pData->WaveBuffer[i].dwBufferLength = lBufferSize;
|
||||
pData->WaveBuffer[i].lpData = ((i==0) ? (LPSTR)BufferData :
|
||||
(pData->WaveBuffer[i-1].lpData +
|
||||
pData->WaveBuffer[i-1].dwBufferLength));
|
||||
waveOutPrepareHeader(pData->hWaveHandle.Out, &pData->WaveBuffer[i], sizeof(WAVEHDR));
|
||||
waveOutWrite(pData->hWaveHandle.Out, &pData->WaveBuffer[i], sizeof(WAVEHDR));
|
||||
InterlockedIncrement(&pData->lWaveBuffersCommitted);
|
||||
}
|
||||
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static void WinMMStopPlayback(ALCdevice *device)
|
||||
{
|
||||
WinMMData *pData = (WinMMData*)device->ExtraData;
|
||||
int i;
|
||||
|
||||
if(pData->hWaveThread == NULL)
|
||||
return;
|
||||
|
||||
// Set flag to stop processing headers
|
||||
pData->bWaveShutdown = AL_TRUE;
|
||||
|
||||
// Wait for signal that all Wave Buffers have returned
|
||||
WaitForSingleObjectEx(pData->hWaveHdrEvent, 5000, FALSE);
|
||||
|
||||
// Wait for signal that Wave Thread has been destroyed
|
||||
WaitForSingleObjectEx(pData->hWaveThreadEvent, 5000, FALSE);
|
||||
|
||||
CloseHandle(pData->hWaveThread);
|
||||
pData->hWaveThread = 0;
|
||||
|
||||
pData->bWaveShutdown = AL_FALSE;
|
||||
|
||||
// Release the wave buffers
|
||||
for(i = 0;i < 4;i++)
|
||||
{
|
||||
waveOutUnprepareHeader(pData->hWaveHandle.Out, &pData->WaveBuffer[i], sizeof(WAVEHDR));
|
||||
if(i == 0)
|
||||
free(pData->WaveBuffer[i].lpData);
|
||||
pData->WaveBuffer[i].lpData = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static ALCboolean WinMMOpenCapture(ALCdevice *pDevice, const ALCchar *deviceName)
|
||||
{
|
||||
WAVEFORMATEX wfexCaptureFormat;
|
||||
@@ -233,6 +531,7 @@ static ALCboolean WinMMOpenCapture(ALCdevice *pDevice, const ALCchar *deviceName
|
||||
alcSetError(pDevice, ALC_OUT_OF_MEMORY);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
pDevice->ExtraData = pData;
|
||||
|
||||
memset(&wfexCaptureFormat, 0, sizeof(WAVEFORMATEX));
|
||||
wfexCaptureFormat.wFormatTag = WAVE_FORMAT_PCM;
|
||||
@@ -259,6 +558,8 @@ static ALCboolean WinMMOpenCapture(ALCdevice *pDevice, const ALCchar *deviceName
|
||||
goto failure;
|
||||
}
|
||||
|
||||
pData->Frequency = pDevice->Frequency;
|
||||
|
||||
// Allocate circular memory buffer for the captured audio
|
||||
ulCapturedDataSize = pDevice->UpdateSize*pDevice->NumUpdates;
|
||||
|
||||
@@ -291,11 +592,9 @@ static ALCboolean WinMMOpenCapture(ALCdevice *pDevice, const ALCchar *deviceName
|
||||
pData->WaveBuffer[i].dwLoops = 0;
|
||||
waveInPrepareHeader(pData->hWaveHandle.In, &pData->WaveBuffer[i], sizeof(WAVEHDR));
|
||||
waveInAddBuffer(pData->hWaveHandle.In, &pData->WaveBuffer[i], sizeof(WAVEHDR));
|
||||
pData->lWaveBuffersCommitted++;
|
||||
InterlockedIncrement(&pData->lWaveBuffersCommitted);
|
||||
}
|
||||
|
||||
pDevice->ExtraData = pData;
|
||||
|
||||
pData->hWaveThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)CaptureThreadProc, (LPVOID)pDevice, 0, &pData->ulWaveThreadID);
|
||||
if (pData->hWaveThread == NULL)
|
||||
goto failure;
|
||||
@@ -409,8 +708,8 @@ static void WinMMCaptureSamples(ALCdevice *pDevice, ALCvoid *pBuffer, ALCuint lS
|
||||
static BackendFuncs WinMMFuncs = {
|
||||
WinMMOpenPlayback,
|
||||
WinMMClosePlayback,
|
||||
NULL,
|
||||
NULL,
|
||||
WinMMResetPlayback,
|
||||
WinMMStopPlayback,
|
||||
WinMMOpenCapture,
|
||||
WinMMCloseCapture,
|
||||
WinMMStartCapture,
|
||||
@@ -428,6 +727,14 @@ void alcWinMMDeinit()
|
||||
{
|
||||
ALuint lLoop;
|
||||
|
||||
for(lLoop = 0;lLoop < NumPlaybackDevices;lLoop++)
|
||||
free(PlaybackDeviceList[lLoop]);
|
||||
free(PlaybackDeviceList);
|
||||
PlaybackDeviceList = NULL;
|
||||
|
||||
NumPlaybackDevices = 0;
|
||||
|
||||
|
||||
for(lLoop = 0; lLoop < NumCaptureDevices; lLoop++)
|
||||
free(CaptureDeviceList[lLoop]);
|
||||
free(CaptureDeviceList);
|
||||
@@ -440,7 +747,24 @@ void alcWinMMProbe(int type)
|
||||
{
|
||||
ALuint i;
|
||||
|
||||
if(type == CAPTURE_DEVICE_PROBE)
|
||||
if(type == DEVICE_PROBE)
|
||||
{
|
||||
ProbePlaybackDevices();
|
||||
if(NumPlaybackDevices > 0)
|
||||
AppendDeviceList(woDefault);
|
||||
}
|
||||
else if(type == ALL_DEVICE_PROBE)
|
||||
{
|
||||
ProbePlaybackDevices();
|
||||
if(NumPlaybackDevices > 0)
|
||||
AppendAllDeviceList(woDefault);
|
||||
for(i = 0;i < NumPlaybackDevices;i++)
|
||||
{
|
||||
if(PlaybackDeviceList[i])
|
||||
AppendAllDeviceList(PlaybackDeviceList[i]);
|
||||
}
|
||||
}
|
||||
else if(type == CAPTURE_DEVICE_PROBE)
|
||||
{
|
||||
ProbeCaptureDevices();
|
||||
for(i = 0;i < NumCaptureDevices;i++)
|
||||
|
||||
Reference in New Issue
Block a user