aboutsummaryrefslogtreecommitdiffstats
path: root/Alc/winmm.c
diff options
context:
space:
mode:
authorChris Robinson <[email protected]>2010-11-27 14:13:40 -0800
committerChris Robinson <[email protected]>2010-11-27 14:13:40 -0800
commitd3bb5d4fcb91af97b396d0f22cb56b519468328f (patch)
tree70b12de4faa9dece14ae3d82bdce33d411be7120 /Alc/winmm.c
parent61315d4dfb8f42d4b6fc74ba8cf8344653cd6598 (diff)
Add a basic WaveOut device
It can still use more work, but it seems to work
Diffstat (limited to 'Alc/winmm.c')
-rw-r--r--Alc/winmm.c362
1 files changed, 343 insertions, 19 deletions
diff --git a/Alc/winmm.c b/Alc/winmm.c
index 54101058..8688b7b0 100644
--- a/Alc/winmm.c
+++ b/Alc/winmm.c
@@ -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;
@@ -95,6 +142,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
Posts a message to 'CaptureThreadProc' everytime a WaveIn Buffer is completed and
@@ -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++)