rlm@0: /** rlm@0: * OpenAL cross platform audio library rlm@0: * Copyright (C) 1999-2007 by authors. rlm@0: * This library is free software; you can redistribute it and/or rlm@0: * modify it under the terms of the GNU Library General Public rlm@0: * License as published by the Free Software Foundation; either rlm@0: * version 2 of the License, or (at your option) any later version. rlm@0: * rlm@0: * This library is distributed in the hope that it will be useful, rlm@0: * but WITHOUT ANY WARRANTY; without even the implied warranty of rlm@0: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU rlm@0: * Library General Public License for more details. rlm@0: * rlm@0: * You should have received a copy of the GNU Library General Public rlm@0: * License along with this library; if not, write to the rlm@0: * Free Software Foundation, Inc., 59 Temple Place - Suite 330, rlm@0: * Boston, MA 02111-1307, USA. rlm@0: * Or go to http://www.gnu.org/copyleft/lgpl.html rlm@0: */ rlm@0: rlm@0: #include "config.h" rlm@0: rlm@0: #define _WIN32_WINNT 0x0500 rlm@0: #include rlm@0: #include rlm@0: #include rlm@0: rlm@0: #include rlm@0: #include rlm@0: rlm@0: #include "alMain.h" rlm@0: #include "AL/al.h" rlm@0: #include "AL/alc.h" rlm@0: rlm@0: rlm@0: typedef struct { rlm@0: // MMSYSTEM Device rlm@0: volatile ALboolean bWaveShutdown; rlm@0: HANDLE hWaveThreadEvent; rlm@0: HANDLE hWaveThread; rlm@0: DWORD ulWaveThreadID; rlm@0: LONG lWaveBuffersCommitted; rlm@0: WAVEHDR WaveBuffer[4]; rlm@0: rlm@0: union { rlm@0: HWAVEIN In; rlm@0: HWAVEOUT Out; rlm@0: } hWaveHandle; rlm@0: rlm@0: ALuint Frequency; rlm@0: rlm@0: RingBuffer *pRing; rlm@0: } WinMMData; rlm@0: rlm@0: rlm@0: static const ALCchar woDefault[] = "WaveOut Default"; rlm@0: rlm@0: static ALCchar **PlaybackDeviceList; rlm@0: static ALuint NumPlaybackDevices; rlm@0: static ALCchar **CaptureDeviceList; rlm@0: static ALuint NumCaptureDevices; rlm@0: rlm@0: rlm@0: static void ProbePlaybackDevices(void) rlm@0: { rlm@0: ALuint i; rlm@0: rlm@0: for(i = 0;i < NumPlaybackDevices;i++) rlm@0: free(PlaybackDeviceList[i]); rlm@0: rlm@0: NumPlaybackDevices = waveOutGetNumDevs(); rlm@0: PlaybackDeviceList = realloc(PlaybackDeviceList, sizeof(ALCchar*) * NumPlaybackDevices); rlm@0: for(i = 0;i < NumPlaybackDevices;i++) rlm@0: { rlm@0: WAVEOUTCAPS WaveCaps; rlm@0: rlm@0: PlaybackDeviceList[i] = NULL; rlm@0: if(waveOutGetDevCaps(i, &WaveCaps, sizeof(WaveCaps)) == MMSYSERR_NOERROR) rlm@0: { rlm@0: char name[1024]; rlm@0: ALuint count, j; rlm@0: rlm@0: count = 0; rlm@0: do { rlm@0: if(count == 0) rlm@0: snprintf(name, sizeof(name), "%s", WaveCaps.szPname); rlm@0: else rlm@0: snprintf(name, sizeof(name), "%s #%d", WaveCaps.szPname, count+1); rlm@0: count++; rlm@0: rlm@0: for(j = 0;j < i;j++) rlm@0: { rlm@0: if(strcmp(name, PlaybackDeviceList[j]) == 0) rlm@0: break; rlm@0: } rlm@0: } while(j != i); rlm@0: rlm@0: PlaybackDeviceList[i] = strdup(name); rlm@0: } rlm@0: } rlm@0: } rlm@0: rlm@0: static void ProbeCaptureDevices(void) rlm@0: { rlm@0: ALuint i; rlm@0: rlm@0: for(i = 0;i < NumCaptureDevices;i++) rlm@0: free(CaptureDeviceList[i]); rlm@0: rlm@0: NumCaptureDevices = waveInGetNumDevs(); rlm@0: CaptureDeviceList = realloc(CaptureDeviceList, sizeof(ALCchar*) * NumCaptureDevices); rlm@0: for(i = 0;i < NumCaptureDevices;i++) rlm@0: { rlm@0: WAVEINCAPS WaveInCaps; rlm@0: rlm@0: CaptureDeviceList[i] = NULL; rlm@0: if(waveInGetDevCaps(i, &WaveInCaps, sizeof(WAVEINCAPS)) == MMSYSERR_NOERROR) rlm@0: { rlm@0: char name[1024]; rlm@0: ALuint count, j; rlm@0: rlm@0: count = 0; rlm@0: do { rlm@0: if(count == 0) rlm@0: snprintf(name, sizeof(name), "%s", WaveInCaps.szPname); rlm@0: else rlm@0: snprintf(name, sizeof(name), "%s #%d", WaveInCaps.szPname, count+1); rlm@0: count++; rlm@0: rlm@0: for(j = 0;j < i;j++) rlm@0: { rlm@0: if(strcmp(name, CaptureDeviceList[j]) == 0) rlm@0: break; rlm@0: } rlm@0: } while(j != i); rlm@0: rlm@0: CaptureDeviceList[i] = strdup(name); rlm@0: } rlm@0: } rlm@0: } rlm@0: rlm@0: rlm@0: /* rlm@0: WaveOutProc rlm@0: rlm@0: Posts a message to 'PlaybackThreadProc' everytime a WaveOut Buffer is completed and rlm@0: returns to the application (for more data) rlm@0: */ rlm@0: static void CALLBACK WaveOutProc(HWAVEOUT hDevice,UINT uMsg,DWORD_PTR dwInstance,DWORD_PTR dwParam1,DWORD_PTR dwParam2) rlm@0: { rlm@0: ALCdevice *pDevice = (ALCdevice*)dwInstance; rlm@0: WinMMData *pData = pDevice->ExtraData; rlm@0: rlm@0: (void)hDevice; rlm@0: (void)dwParam2; rlm@0: rlm@0: if(uMsg != WOM_DONE) rlm@0: return; rlm@0: rlm@0: // Decrement number of buffers in use rlm@0: InterlockedDecrement(&pData->lWaveBuffersCommitted); rlm@0: rlm@0: if(pData->bWaveShutdown == AL_FALSE) rlm@0: { rlm@0: // Notify Wave Processor Thread that a Wave Header has returned rlm@0: PostThreadMessage(pData->ulWaveThreadID, uMsg, 0, dwParam1); rlm@0: } rlm@0: else rlm@0: { rlm@0: if(pData->lWaveBuffersCommitted == 0) rlm@0: { rlm@0: // Post 'Quit' Message to WaveOut Processor Thread rlm@0: PostThreadMessage(pData->ulWaveThreadID, WM_QUIT, 0, 0); rlm@0: } rlm@0: } rlm@0: } rlm@0: rlm@0: /* rlm@0: PlaybackThreadProc rlm@0: rlm@0: Used by "MMSYSTEM" Device. Called when a WaveOut buffer has used up its rlm@0: audio data. rlm@0: */ rlm@0: static DWORD WINAPI PlaybackThreadProc(LPVOID lpParameter) rlm@0: { rlm@0: ALCdevice *pDevice = (ALCdevice*)lpParameter; rlm@0: WinMMData *pData = pDevice->ExtraData; rlm@0: LPWAVEHDR pWaveHdr; rlm@0: ALuint FrameSize; rlm@0: MSG msg; rlm@0: rlm@0: FrameSize = FrameSizeFromDevFmt(pDevice->FmtChans, pDevice->FmtType); rlm@0: rlm@0: SetRTPriority(); rlm@0: rlm@0: while(GetMessage(&msg, NULL, 0, 0)) rlm@0: { rlm@0: if(msg.message != WOM_DONE || pData->bWaveShutdown) rlm@0: continue; rlm@0: rlm@0: pWaveHdr = ((LPWAVEHDR)msg.lParam); rlm@0: rlm@0: aluMixData(pDevice, pWaveHdr->lpData, pWaveHdr->dwBufferLength/FrameSize); rlm@0: rlm@0: // Send buffer back to play more data rlm@0: waveOutWrite(pData->hWaveHandle.Out, pWaveHdr, sizeof(WAVEHDR)); rlm@0: InterlockedIncrement(&pData->lWaveBuffersCommitted); rlm@0: } rlm@0: rlm@0: // Signal Wave Thread completed event rlm@0: if(pData->hWaveThreadEvent) rlm@0: SetEvent(pData->hWaveThreadEvent); rlm@0: rlm@0: ExitThread(0); rlm@0: rlm@0: return 0; rlm@0: } rlm@0: rlm@0: /* rlm@0: WaveInProc rlm@0: rlm@0: Posts a message to 'CaptureThreadProc' everytime a WaveIn Buffer is completed and rlm@0: returns to the application (with more data) rlm@0: */ rlm@0: static void CALLBACK WaveInProc(HWAVEIN hDevice,UINT uMsg,DWORD_PTR dwInstance,DWORD_PTR dwParam1,DWORD_PTR dwParam2) rlm@0: { rlm@0: ALCdevice *pDevice = (ALCdevice*)dwInstance; rlm@0: WinMMData *pData = pDevice->ExtraData; rlm@0: rlm@0: (void)hDevice; rlm@0: (void)dwParam2; rlm@0: rlm@0: if(uMsg != WIM_DATA) rlm@0: return; rlm@0: rlm@0: // Decrement number of buffers in use rlm@0: InterlockedDecrement(&pData->lWaveBuffersCommitted); rlm@0: rlm@0: if(pData->bWaveShutdown == AL_FALSE) rlm@0: { rlm@0: // Notify Wave Processor Thread that a Wave Header has returned rlm@0: PostThreadMessage(pData->ulWaveThreadID,uMsg,0,dwParam1); rlm@0: } rlm@0: else rlm@0: { rlm@0: if(pData->lWaveBuffersCommitted == 0) rlm@0: { rlm@0: // Post 'Quit' Message to WaveIn Processor Thread rlm@0: PostThreadMessage(pData->ulWaveThreadID,WM_QUIT,0,0); rlm@0: } rlm@0: } rlm@0: } rlm@0: rlm@0: /* rlm@0: CaptureThreadProc rlm@0: rlm@0: Used by "MMSYSTEM" Device. Called when a WaveIn buffer had been filled with new rlm@0: audio data. rlm@0: */ rlm@0: static DWORD WINAPI CaptureThreadProc(LPVOID lpParameter) rlm@0: { rlm@0: ALCdevice *pDevice = (ALCdevice*)lpParameter; rlm@0: WinMMData *pData = pDevice->ExtraData; rlm@0: LPWAVEHDR pWaveHdr; rlm@0: ALuint FrameSize; rlm@0: MSG msg; rlm@0: rlm@0: FrameSize = FrameSizeFromDevFmt(pDevice->FmtChans, pDevice->FmtType); rlm@0: rlm@0: while(GetMessage(&msg, NULL, 0, 0)) rlm@0: { rlm@0: if(msg.message != WIM_DATA || pData->bWaveShutdown) rlm@0: continue; rlm@0: rlm@0: pWaveHdr = ((LPWAVEHDR)msg.lParam); rlm@0: rlm@0: WriteRingBuffer(pData->pRing, (ALubyte*)pWaveHdr->lpData, rlm@0: pWaveHdr->dwBytesRecorded/FrameSize); rlm@0: rlm@0: // Send buffer back to capture more data rlm@0: waveInAddBuffer(pData->hWaveHandle.In,pWaveHdr,sizeof(WAVEHDR)); rlm@0: InterlockedIncrement(&pData->lWaveBuffersCommitted); rlm@0: } rlm@0: rlm@0: // Signal Wave Thread completed event rlm@0: if(pData->hWaveThreadEvent) rlm@0: SetEvent(pData->hWaveThreadEvent); rlm@0: rlm@0: ExitThread(0); rlm@0: rlm@0: return 0; rlm@0: } rlm@0: rlm@0: rlm@0: static ALCboolean WinMMOpenPlayback(ALCdevice *pDevice, const ALCchar *deviceName) rlm@0: { rlm@0: WAVEFORMATEX wfexFormat; rlm@0: WinMMData *pData = NULL; rlm@0: UINT lDeviceID = 0; rlm@0: MMRESULT res; rlm@0: ALuint i = 0; rlm@0: rlm@0: // Find the Device ID matching the deviceName if valid rlm@0: if(!deviceName || strcmp(deviceName, woDefault) == 0) rlm@0: lDeviceID = WAVE_MAPPER; rlm@0: else rlm@0: { rlm@0: if(!PlaybackDeviceList) rlm@0: ProbePlaybackDevices(); rlm@0: rlm@0: for(i = 0;i < NumPlaybackDevices;i++) rlm@0: { rlm@0: if(PlaybackDeviceList[i] && rlm@0: strcmp(deviceName, PlaybackDeviceList[i]) == 0) rlm@0: { rlm@0: lDeviceID = i; rlm@0: break; rlm@0: } rlm@0: } rlm@0: if(i == NumPlaybackDevices) rlm@0: return ALC_FALSE; rlm@0: } rlm@0: rlm@0: pData = calloc(1, sizeof(*pData)); rlm@0: if(!pData) rlm@0: { rlm@0: alcSetError(pDevice, ALC_OUT_OF_MEMORY); rlm@0: return ALC_FALSE; rlm@0: } rlm@0: pDevice->ExtraData = pData; rlm@0: rlm@0: if(pDevice->FmtChans != DevFmtMono) rlm@0: { rlm@0: if((pDevice->Flags&DEVICE_CHANNELS_REQUEST) && rlm@0: pDevice->FmtChans != DevFmtStereo) rlm@0: { rlm@0: ERR("Failed to set %s, got Stereo instead\n", DevFmtChannelsString(pDevice->FmtChans)); rlm@0: pDevice->Flags &= ~DEVICE_CHANNELS_REQUEST; rlm@0: } rlm@0: pDevice->FmtChans = DevFmtStereo; rlm@0: } rlm@0: switch(pDevice->FmtType) rlm@0: { rlm@0: case DevFmtByte: rlm@0: pDevice->FmtType = DevFmtUByte; rlm@0: break; rlm@0: case DevFmtUShort: rlm@0: case DevFmtFloat: rlm@0: pDevice->FmtType = DevFmtShort; rlm@0: break; rlm@0: case DevFmtUByte: rlm@0: case DevFmtShort: rlm@0: break; rlm@0: } rlm@0: rlm@0: memset(&wfexFormat, 0, sizeof(WAVEFORMATEX)); rlm@0: wfexFormat.wFormatTag = WAVE_FORMAT_PCM; rlm@0: wfexFormat.nChannels = ChannelsFromDevFmt(pDevice->FmtChans); rlm@0: wfexFormat.wBitsPerSample = BytesFromDevFmt(pDevice->FmtType) * 8; rlm@0: wfexFormat.nBlockAlign = wfexFormat.wBitsPerSample * rlm@0: wfexFormat.nChannels / 8; rlm@0: wfexFormat.nSamplesPerSec = pDevice->Frequency; rlm@0: wfexFormat.nAvgBytesPerSec = wfexFormat.nSamplesPerSec * rlm@0: wfexFormat.nBlockAlign; rlm@0: wfexFormat.cbSize = 0; rlm@0: rlm@0: if((res=waveOutOpen(&pData->hWaveHandle.Out, lDeviceID, &wfexFormat, (DWORD_PTR)&WaveOutProc, (DWORD_PTR)pDevice, CALLBACK_FUNCTION)) != MMSYSERR_NOERROR) rlm@0: { rlm@0: ERR("waveOutOpen failed: %u\n", res); rlm@0: goto failure; rlm@0: } rlm@0: rlm@0: pData->hWaveThreadEvent = CreateEvent(NULL, FALSE, FALSE, NULL); rlm@0: if(pData->hWaveThreadEvent == NULL) rlm@0: { rlm@0: ERR("CreateEvent failed: %lu\n", GetLastError()); rlm@0: goto failure; rlm@0: } rlm@0: rlm@0: pData->Frequency = pDevice->Frequency; rlm@0: rlm@0: pDevice->szDeviceName = strdup((lDeviceID==WAVE_MAPPER) ? woDefault : rlm@0: PlaybackDeviceList[lDeviceID]); rlm@0: return ALC_TRUE; rlm@0: rlm@0: failure: rlm@0: if(pData->hWaveThreadEvent) rlm@0: CloseHandle(pData->hWaveThreadEvent); rlm@0: rlm@0: if(pData->hWaveHandle.Out) rlm@0: waveOutClose(pData->hWaveHandle.Out); rlm@0: rlm@0: free(pData); rlm@0: pDevice->ExtraData = NULL; rlm@0: return ALC_FALSE; rlm@0: } rlm@0: rlm@0: static void WinMMClosePlayback(ALCdevice *device) rlm@0: { rlm@0: WinMMData *pData = (WinMMData*)device->ExtraData; rlm@0: rlm@0: // Close the Wave device rlm@0: CloseHandle(pData->hWaveThreadEvent); rlm@0: pData->hWaveThreadEvent = 0; rlm@0: rlm@0: waveOutClose(pData->hWaveHandle.Out); rlm@0: pData->hWaveHandle.Out = 0; rlm@0: rlm@0: free(pData); rlm@0: device->ExtraData = NULL; rlm@0: } rlm@0: rlm@0: static ALCboolean WinMMResetPlayback(ALCdevice *device) rlm@0: { rlm@0: WinMMData *pData = (WinMMData*)device->ExtraData; rlm@0: ALbyte *BufferData; rlm@0: ALint lBufferSize; rlm@0: ALuint i; rlm@0: rlm@0: pData->hWaveThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)PlaybackThreadProc, (LPVOID)device, 0, &pData->ulWaveThreadID); rlm@0: if(pData->hWaveThread == NULL) rlm@0: return ALC_FALSE; rlm@0: rlm@0: device->UpdateSize = (ALuint)((ALuint64)device->UpdateSize * rlm@0: pData->Frequency / device->Frequency); rlm@0: if(device->Frequency != pData->Frequency) rlm@0: { rlm@0: if((device->Flags&DEVICE_FREQUENCY_REQUEST)) rlm@0: ERR("WinMM does not support changing sample rates (wanted %dhz, got %dhz)\n", device->Frequency, pData->Frequency); rlm@0: device->Flags &= ~DEVICE_FREQUENCY_REQUEST; rlm@0: device->Frequency = pData->Frequency; rlm@0: } rlm@0: rlm@0: SetDefaultWFXChannelOrder(device); rlm@0: rlm@0: pData->lWaveBuffersCommitted = 0; rlm@0: rlm@0: // Create 4 Buffers rlm@0: lBufferSize = device->UpdateSize*device->NumUpdates / 4; rlm@0: lBufferSize *= FrameSizeFromDevFmt(device->FmtChans, device->FmtType); rlm@0: rlm@0: BufferData = calloc(4, lBufferSize); rlm@0: for(i = 0;i < 4;i++) rlm@0: { rlm@0: memset(&pData->WaveBuffer[i], 0, sizeof(WAVEHDR)); rlm@0: pData->WaveBuffer[i].dwBufferLength = lBufferSize; rlm@0: pData->WaveBuffer[i].lpData = ((i==0) ? (LPSTR)BufferData : rlm@0: (pData->WaveBuffer[i-1].lpData + rlm@0: pData->WaveBuffer[i-1].dwBufferLength)); rlm@0: waveOutPrepareHeader(pData->hWaveHandle.Out, &pData->WaveBuffer[i], sizeof(WAVEHDR)); rlm@0: waveOutWrite(pData->hWaveHandle.Out, &pData->WaveBuffer[i], sizeof(WAVEHDR)); rlm@0: InterlockedIncrement(&pData->lWaveBuffersCommitted); rlm@0: } rlm@0: rlm@0: return ALC_TRUE; rlm@0: } rlm@0: rlm@0: static void WinMMStopPlayback(ALCdevice *device) rlm@0: { rlm@0: WinMMData *pData = (WinMMData*)device->ExtraData; rlm@0: void *buffer = NULL; rlm@0: int i; rlm@0: rlm@0: if(pData->hWaveThread == NULL) rlm@0: return; rlm@0: rlm@0: // Set flag to stop processing headers rlm@0: pData->bWaveShutdown = AL_TRUE; rlm@0: rlm@0: // Wait for signal that Wave Thread has been destroyed rlm@0: WaitForSingleObjectEx(pData->hWaveThreadEvent, 5000, FALSE); rlm@0: rlm@0: CloseHandle(pData->hWaveThread); rlm@0: pData->hWaveThread = 0; rlm@0: rlm@0: pData->bWaveShutdown = AL_FALSE; rlm@0: rlm@0: // Release the wave buffers rlm@0: for(i = 0;i < 4;i++) rlm@0: { rlm@0: waveOutUnprepareHeader(pData->hWaveHandle.Out, &pData->WaveBuffer[i], sizeof(WAVEHDR)); rlm@0: if(i == 0) buffer = pData->WaveBuffer[i].lpData; rlm@0: pData->WaveBuffer[i].lpData = NULL; rlm@0: } rlm@0: free(buffer); rlm@0: } rlm@0: rlm@0: rlm@0: static ALCboolean WinMMOpenCapture(ALCdevice *pDevice, const ALCchar *deviceName) rlm@0: { rlm@0: WAVEFORMATEX wfexCaptureFormat; rlm@0: DWORD ulCapturedDataSize; rlm@0: WinMMData *pData = NULL; rlm@0: UINT lDeviceID = 0; rlm@0: ALbyte *BufferData; rlm@0: ALint lBufferSize; rlm@0: MMRESULT res; rlm@0: ALuint i; rlm@0: rlm@0: if(!CaptureDeviceList) rlm@0: ProbeCaptureDevices(); rlm@0: rlm@0: // Find the Device ID matching the deviceName if valid rlm@0: if(deviceName) rlm@0: { rlm@0: for(i = 0;i < NumCaptureDevices;i++) rlm@0: { rlm@0: if(CaptureDeviceList[i] && rlm@0: strcmp(deviceName, CaptureDeviceList[i]) == 0) rlm@0: { rlm@0: lDeviceID = i; rlm@0: break; rlm@0: } rlm@0: } rlm@0: } rlm@0: else rlm@0: { rlm@0: for(i = 0;i < NumCaptureDevices;i++) rlm@0: { rlm@0: if(CaptureDeviceList[i]) rlm@0: { rlm@0: lDeviceID = i; rlm@0: break; rlm@0: } rlm@0: } rlm@0: } rlm@0: if(i == NumCaptureDevices) rlm@0: return ALC_FALSE; rlm@0: rlm@0: pData = calloc(1, sizeof(*pData)); rlm@0: if(!pData) rlm@0: { rlm@0: alcSetError(pDevice, ALC_OUT_OF_MEMORY); rlm@0: return ALC_FALSE; rlm@0: } rlm@0: pDevice->ExtraData = pData; rlm@0: rlm@0: if((pDevice->FmtChans != DevFmtMono && pDevice->FmtChans != DevFmtStereo) || rlm@0: (pDevice->FmtType != DevFmtUByte && pDevice->FmtType != DevFmtShort)) rlm@0: { rlm@0: alcSetError(pDevice, ALC_INVALID_ENUM); rlm@0: goto failure; rlm@0: } rlm@0: rlm@0: memset(&wfexCaptureFormat, 0, sizeof(WAVEFORMATEX)); rlm@0: wfexCaptureFormat.wFormatTag = WAVE_FORMAT_PCM; rlm@0: wfexCaptureFormat.nChannels = ChannelsFromDevFmt(pDevice->FmtChans); rlm@0: wfexCaptureFormat.wBitsPerSample = BytesFromDevFmt(pDevice->FmtType) * 8; rlm@0: wfexCaptureFormat.nBlockAlign = wfexCaptureFormat.wBitsPerSample * rlm@0: wfexCaptureFormat.nChannels / 8; rlm@0: wfexCaptureFormat.nSamplesPerSec = pDevice->Frequency; rlm@0: wfexCaptureFormat.nAvgBytesPerSec = wfexCaptureFormat.nSamplesPerSec * rlm@0: wfexCaptureFormat.nBlockAlign; rlm@0: wfexCaptureFormat.cbSize = 0; rlm@0: rlm@0: if((res=waveInOpen(&pData->hWaveHandle.In, lDeviceID, &wfexCaptureFormat, (DWORD_PTR)&WaveInProc, (DWORD_PTR)pDevice, CALLBACK_FUNCTION)) != MMSYSERR_NOERROR) rlm@0: { rlm@0: ERR("waveInOpen failed: %u\n", res); rlm@0: goto failure; rlm@0: } rlm@0: rlm@0: pData->hWaveThreadEvent = CreateEvent(NULL, FALSE, FALSE, NULL); rlm@0: if(pData->hWaveThreadEvent == NULL) rlm@0: { rlm@0: ERR("CreateEvent failed: %lu\n", GetLastError()); rlm@0: goto failure; rlm@0: } rlm@0: rlm@0: pData->Frequency = pDevice->Frequency; rlm@0: rlm@0: // Allocate circular memory buffer for the captured audio rlm@0: ulCapturedDataSize = pDevice->UpdateSize*pDevice->NumUpdates; rlm@0: rlm@0: // Make sure circular buffer is at least 100ms in size rlm@0: if(ulCapturedDataSize < (wfexCaptureFormat.nSamplesPerSec / 10)) rlm@0: ulCapturedDataSize = wfexCaptureFormat.nSamplesPerSec / 10; rlm@0: rlm@0: pData->pRing = CreateRingBuffer(wfexCaptureFormat.nBlockAlign, ulCapturedDataSize); rlm@0: if(!pData->pRing) rlm@0: goto failure; rlm@0: rlm@0: pData->lWaveBuffersCommitted = 0; rlm@0: rlm@0: // Create 4 Buffers of 50ms each rlm@0: lBufferSize = wfexCaptureFormat.nAvgBytesPerSec / 20; rlm@0: lBufferSize -= (lBufferSize % wfexCaptureFormat.nBlockAlign); rlm@0: rlm@0: BufferData = calloc(4, lBufferSize); rlm@0: if(!BufferData) rlm@0: goto failure; rlm@0: rlm@0: for(i = 0;i < 4;i++) rlm@0: { rlm@0: memset(&pData->WaveBuffer[i], 0, sizeof(WAVEHDR)); rlm@0: pData->WaveBuffer[i].dwBufferLength = lBufferSize; rlm@0: pData->WaveBuffer[i].lpData = ((i==0) ? (LPSTR)BufferData : rlm@0: (pData->WaveBuffer[i-1].lpData + rlm@0: pData->WaveBuffer[i-1].dwBufferLength)); rlm@0: pData->WaveBuffer[i].dwFlags = 0; rlm@0: pData->WaveBuffer[i].dwLoops = 0; rlm@0: waveInPrepareHeader(pData->hWaveHandle.In, &pData->WaveBuffer[i], sizeof(WAVEHDR)); rlm@0: waveInAddBuffer(pData->hWaveHandle.In, &pData->WaveBuffer[i], sizeof(WAVEHDR)); rlm@0: InterlockedIncrement(&pData->lWaveBuffersCommitted); rlm@0: } rlm@0: rlm@0: pData->hWaveThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)CaptureThreadProc, (LPVOID)pDevice, 0, &pData->ulWaveThreadID); rlm@0: if (pData->hWaveThread == NULL) rlm@0: goto failure; rlm@0: rlm@0: pDevice->szDeviceName = strdup(CaptureDeviceList[lDeviceID]); rlm@0: return ALC_TRUE; rlm@0: rlm@0: failure: rlm@0: if(pData->hWaveThread) rlm@0: CloseHandle(pData->hWaveThread); rlm@0: rlm@0: for(i = 0;i < 4;i++) rlm@0: { rlm@0: if(pData->WaveBuffer[i].lpData) rlm@0: { rlm@0: waveInUnprepareHeader(pData->hWaveHandle.In, &pData->WaveBuffer[i], sizeof(WAVEHDR)); rlm@0: if(i == 0) rlm@0: free(pData->WaveBuffer[i].lpData); rlm@0: } rlm@0: } rlm@0: rlm@0: if(pData->pRing) rlm@0: DestroyRingBuffer(pData->pRing); rlm@0: rlm@0: if(pData->hWaveThreadEvent) rlm@0: CloseHandle(pData->hWaveThreadEvent); rlm@0: rlm@0: if(pData->hWaveHandle.In) rlm@0: waveInClose(pData->hWaveHandle.In); rlm@0: rlm@0: free(pData); rlm@0: pDevice->ExtraData = NULL; rlm@0: return ALC_FALSE; rlm@0: } rlm@0: rlm@0: static void WinMMCloseCapture(ALCdevice *pDevice) rlm@0: { rlm@0: WinMMData *pData = (WinMMData*)pDevice->ExtraData; rlm@0: void *buffer = NULL; rlm@0: int i; rlm@0: rlm@0: // Call waveOutReset to shutdown wave device rlm@0: pData->bWaveShutdown = AL_TRUE; rlm@0: waveInReset(pData->hWaveHandle.In); rlm@0: rlm@0: // Wait for signal that Wave Thread has been destroyed rlm@0: WaitForSingleObjectEx(pData->hWaveThreadEvent, 5000, FALSE); rlm@0: rlm@0: CloseHandle(pData->hWaveThread); rlm@0: pData->hWaveThread = 0; rlm@0: rlm@0: // Release the wave buffers rlm@0: for(i = 0;i < 4;i++) rlm@0: { rlm@0: waveInUnprepareHeader(pData->hWaveHandle.In, &pData->WaveBuffer[i], sizeof(WAVEHDR)); rlm@0: if(i == 0) buffer = pData->WaveBuffer[i].lpData; rlm@0: pData->WaveBuffer[i].lpData = NULL; rlm@0: } rlm@0: free(buffer); rlm@0: rlm@0: DestroyRingBuffer(pData->pRing); rlm@0: pData->pRing = NULL; rlm@0: rlm@0: // Close the Wave device rlm@0: CloseHandle(pData->hWaveThreadEvent); rlm@0: pData->hWaveThreadEvent = 0; rlm@0: rlm@0: waveInClose(pData->hWaveHandle.In); rlm@0: pData->hWaveHandle.In = 0; rlm@0: rlm@0: free(pData); rlm@0: pDevice->ExtraData = NULL; rlm@0: } rlm@0: rlm@0: static void WinMMStartCapture(ALCdevice *pDevice) rlm@0: { rlm@0: WinMMData *pData = (WinMMData*)pDevice->ExtraData; rlm@0: waveInStart(pData->hWaveHandle.In); rlm@0: } rlm@0: rlm@0: static void WinMMStopCapture(ALCdevice *pDevice) rlm@0: { rlm@0: WinMMData *pData = (WinMMData*)pDevice->ExtraData; rlm@0: waveInStop(pData->hWaveHandle.In); rlm@0: } rlm@0: rlm@0: static ALCuint WinMMAvailableSamples(ALCdevice *pDevice) rlm@0: { rlm@0: WinMMData *pData = (WinMMData*)pDevice->ExtraData; rlm@0: return RingBufferSize(pData->pRing); rlm@0: } rlm@0: rlm@0: static void WinMMCaptureSamples(ALCdevice *pDevice, ALCvoid *pBuffer, ALCuint lSamples) rlm@0: { rlm@0: WinMMData *pData = (WinMMData*)pDevice->ExtraData; rlm@0: rlm@0: if(WinMMAvailableSamples(pDevice) >= lSamples) rlm@0: ReadRingBuffer(pData->pRing, pBuffer, lSamples); rlm@0: else rlm@0: alcSetError(pDevice, ALC_INVALID_VALUE); rlm@0: } rlm@0: rlm@0: rlm@0: static const BackendFuncs WinMMFuncs = { rlm@0: WinMMOpenPlayback, rlm@0: WinMMClosePlayback, rlm@0: WinMMResetPlayback, rlm@0: WinMMStopPlayback, rlm@0: WinMMOpenCapture, rlm@0: WinMMCloseCapture, rlm@0: WinMMStartCapture, rlm@0: WinMMStopCapture, rlm@0: WinMMCaptureSamples, rlm@0: WinMMAvailableSamples rlm@0: }; rlm@0: rlm@0: ALCboolean alcWinMMInit(BackendFuncs *FuncList) rlm@0: { rlm@0: *FuncList = WinMMFuncs; rlm@0: return ALC_TRUE; rlm@0: } rlm@0: rlm@0: void alcWinMMDeinit() rlm@0: { rlm@0: ALuint lLoop; rlm@0: rlm@0: for(lLoop = 0;lLoop < NumPlaybackDevices;lLoop++) rlm@0: free(PlaybackDeviceList[lLoop]); rlm@0: free(PlaybackDeviceList); rlm@0: PlaybackDeviceList = NULL; rlm@0: rlm@0: NumPlaybackDevices = 0; rlm@0: rlm@0: rlm@0: for(lLoop = 0; lLoop < NumCaptureDevices; lLoop++) rlm@0: free(CaptureDeviceList[lLoop]); rlm@0: free(CaptureDeviceList); rlm@0: CaptureDeviceList = NULL; rlm@0: rlm@0: NumCaptureDevices = 0; rlm@0: } rlm@0: rlm@0: void alcWinMMProbe(enum DevProbe type) rlm@0: { rlm@0: ALuint i; rlm@0: rlm@0: switch(type) rlm@0: { rlm@0: case DEVICE_PROBE: rlm@0: ProbePlaybackDevices(); rlm@0: if(NumPlaybackDevices > 0) rlm@0: AppendDeviceList(woDefault); rlm@0: break; rlm@0: rlm@0: case ALL_DEVICE_PROBE: rlm@0: ProbePlaybackDevices(); rlm@0: if(NumPlaybackDevices > 0) rlm@0: AppendAllDeviceList(woDefault); rlm@0: for(i = 0;i < NumPlaybackDevices;i++) rlm@0: { rlm@0: if(PlaybackDeviceList[i]) rlm@0: AppendAllDeviceList(PlaybackDeviceList[i]); rlm@0: } rlm@0: break; rlm@0: rlm@0: case CAPTURE_DEVICE_PROBE: rlm@0: ProbeCaptureDevices(); rlm@0: for(i = 0;i < NumCaptureDevices;i++) rlm@0: { rlm@0: if(CaptureDeviceList[i]) rlm@0: AppendCaptureDeviceList(CaptureDeviceList[i]); rlm@0: } rlm@0: break; rlm@0: } rlm@0: }