Fix retrieved update size from pulseaudio
[openal-soft.git] / Alc / winmm.c
blobbf3c18871264895f13fb603102a48365c15ccf55
1 /**
2 * OpenAL cross platform audio library
3 * Copyright (C) 1999-2007 by authors.
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
18 * Or go to http://www.gnu.org/copyleft/lgpl.html
21 #include "config.h"
23 #define _WIN32_WINNT 0x0500
24 #include <stdlib.h>
25 #include <stdio.h>
26 #include <memory.h>
28 #include <windows.h>
29 #include <mmsystem.h>
31 #include "alMain.h"
32 #include "AL/al.h"
33 #include "AL/alc.h"
36 typedef struct {
37 // MMSYSTEM Capture Device
38 ALboolean bWaveInShutdown;
39 HANDLE hWaveInHdrEvent;
40 HANDLE hWaveInThreadEvent;
41 HANDLE hWaveInThread;
42 DWORD ulWaveInThreadID;
43 ALint lWaveInBuffersCommitted;
44 HWAVEIN hWaveInHandle;
45 WAVEHDR WaveInBuffer[4];
46 ALCchar *pCapturedSampleData;
47 ALuint ulCapturedDataSize;
48 ALuint ulReadCapturedDataPos;
49 ALuint ulWriteCapturedDataPos;
50 } WinMMData;
53 static ALCchar **CaptureDeviceList;
54 static ALuint NumCaptureDevices;
57 WaveInProc
59 Posts a message to 'CaptureThreadProc' everytime a WaveIn Buffer is completed and
60 returns to the application (with more data)
62 static void CALLBACK WaveInProc(HWAVEIN hDevice,UINT uMsg,DWORD_PTR dwInstance,DWORD_PTR dwParam1,DWORD_PTR dwParam2)
64 ALCdevice *pDevice = (ALCdevice *)dwInstance;
65 WinMMData *pData = pDevice->ExtraData;
67 (void)hDevice;
68 (void)dwParam2;
70 if ((uMsg==WIM_DATA))
72 // Decrement number of buffers in use
73 pData->lWaveInBuffersCommitted--;
75 if (pData->bWaveInShutdown == AL_FALSE)
77 // Notify Wave Processor Thread that a Wave Header has returned
78 PostThreadMessage(pData->ulWaveInThreadID,uMsg,0,dwParam1);
80 else
82 if (pData->lWaveInBuffersCommitted == 0)
84 // Signal Wave Buffers Returned event
85 if (pData->hWaveInHdrEvent)
86 SetEvent(pData->hWaveInHdrEvent);
88 // Post 'Quit' Message to WaveIn Processor Thread
89 PostThreadMessage(pData->ulWaveInThreadID,WM_QUIT,0,0);
96 CaptureThreadProc
98 Used by "MMSYSTEM" Device. Called when a WaveIn buffer had been filled with new
99 audio data.
101 DWORD WINAPI CaptureThreadProc(LPVOID lpParameter)
103 ALCdevice *pDevice = (ALCdevice*)lpParameter;
104 WinMMData *pData = pDevice->ExtraData;
105 ALuint ulOffset, ulMaxSize, ulSection;
106 LPWAVEHDR pWaveHdr;
107 MSG msg;
109 while (GetMessage(&msg, NULL, 0, 0))
111 if ((msg.message==WIM_DATA)&&(!pData->bWaveInShutdown))
113 SuspendContext(NULL);
115 pWaveHdr = ((LPWAVEHDR)msg.lParam);
117 // Calculate offset in local buffer to write data to
118 ulOffset = pData->ulWriteCapturedDataPos % pData->ulCapturedDataSize;
120 if ((ulOffset + pWaveHdr->dwBytesRecorded) > pData->ulCapturedDataSize)
122 ulSection = pData->ulCapturedDataSize - ulOffset;
123 memcpy(pData->pCapturedSampleData + ulOffset, pWaveHdr->lpData, ulSection);
124 memcpy(pData->pCapturedSampleData, pWaveHdr->lpData + ulSection, pWaveHdr->dwBytesRecorded - ulSection);
126 else
128 memcpy(pData->pCapturedSampleData + ulOffset, pWaveHdr->lpData, pWaveHdr->dwBytesRecorded);
131 pData->ulWriteCapturedDataPos += pWaveHdr->dwBytesRecorded;
133 if (pData->ulWriteCapturedDataPos > (pData->ulReadCapturedDataPos + pData->ulCapturedDataSize))
135 // Application has not read enough audio data from the capture buffer so data has been
136 // overwritten. Reset ReadPosition.
137 pData->ulReadCapturedDataPos = pData->ulWriteCapturedDataPos - pData->ulCapturedDataSize;
140 // To prevent an over-flow prevent the offset values from getting too large
141 ulMaxSize = pData->ulCapturedDataSize << 4;
142 if ((pData->ulReadCapturedDataPos > ulMaxSize) && (pData->ulWriteCapturedDataPos > ulMaxSize))
144 pData->ulReadCapturedDataPos -= ulMaxSize;
145 pData->ulWriteCapturedDataPos -= ulMaxSize;
148 // Send buffer back to capture more data
149 waveInAddBuffer(pData->hWaveInHandle,pWaveHdr,sizeof(WAVEHDR));
150 pData->lWaveInBuffersCommitted++;
152 ProcessContext(NULL);
156 // Signal Wave Thread completed event
157 if (pData->hWaveInThreadEvent)
158 SetEvent(pData->hWaveInThreadEvent);
160 ExitThread(0);
162 return 0;
166 static ALCboolean WinMMOpenPlayback(ALCdevice *device, const ALCchar *deviceName)
168 (void)device;
169 (void)deviceName;
170 return ALC_FALSE;
173 static void WinMMClosePlayback(ALCdevice *device)
175 (void)device;
179 static ALCboolean WinMMOpenCapture(ALCdevice *pDevice, const ALCchar *deviceName)
181 WAVEFORMATEX wfexCaptureFormat;
182 WinMMData *pData = NULL;
183 ALint lDeviceID = 0;
184 ALint lBufferSize;
185 ALuint i;
187 // Find the Device ID matching the deviceName if valid
188 if (deviceName)
190 for(i = 0;i < NumCaptureDevices;i++)
192 if (!strcmp(deviceName, CaptureDeviceList[i]))
194 lDeviceID = i;
195 break;
198 if(i == NumCaptureDevices)
199 return ALC_FALSE;
202 pData = calloc(1, sizeof(*pData));
203 if(!pData)
205 alcSetError(ALC_OUT_OF_MEMORY);
206 return ALC_FALSE;
209 memset(&wfexCaptureFormat, 0, sizeof(WAVEFORMATEX));
210 wfexCaptureFormat.wFormatTag = WAVE_FORMAT_PCM;
211 wfexCaptureFormat.nChannels = aluChannelsFromFormat(pDevice->Format);
212 wfexCaptureFormat.wBitsPerSample = aluBytesFromFormat(pDevice->Format) * 8;
213 wfexCaptureFormat.nBlockAlign = wfexCaptureFormat.wBitsPerSample *
214 wfexCaptureFormat.nChannels / 8;
215 wfexCaptureFormat.nSamplesPerSec = pDevice->Frequency;
216 wfexCaptureFormat.nAvgBytesPerSec = wfexCaptureFormat.nSamplesPerSec *
217 wfexCaptureFormat.nBlockAlign;
218 wfexCaptureFormat.cbSize = 0;
220 if (waveInOpen(&pData->hWaveInHandle, lDeviceID, &wfexCaptureFormat, (DWORD_PTR)&WaveInProc, (DWORD_PTR)pDevice, CALLBACK_FUNCTION) != MMSYSERR_NOERROR)
221 goto failure;
223 pData->hWaveInHdrEvent = CreateEvent(NULL, AL_TRUE, AL_FALSE, "WaveInAllHeadersReturned");
224 if (pData->hWaveInHdrEvent == NULL)
225 goto failure;
227 pData->hWaveInThreadEvent = CreateEvent(NULL, AL_TRUE, AL_FALSE, "WaveInThreadDestroyed");
228 if (pData->hWaveInThreadEvent == NULL)
229 goto failure;
231 // Allocate circular memory buffer for the captured audio
232 pData->ulCapturedDataSize = pDevice->UpdateSize*pDevice->NumUpdates *
233 wfexCaptureFormat.nBlockAlign;
235 // Make sure circular buffer is at least 100ms in size (and an exact multiple of
236 // the block alignment
237 if (pData->ulCapturedDataSize < (wfexCaptureFormat.nAvgBytesPerSec / 10))
239 pData->ulCapturedDataSize = wfexCaptureFormat.nAvgBytesPerSec / 10;
240 pData->ulCapturedDataSize -= (pData->ulCapturedDataSize % wfexCaptureFormat.nBlockAlign);
243 pData->pCapturedSampleData = (ALCchar*)malloc(pData->ulCapturedDataSize);
244 pData->lWaveInBuffersCommitted=0;
246 // Create 4 Buffers of 50ms each
247 lBufferSize = wfexCaptureFormat.nAvgBytesPerSec / 20;
248 lBufferSize -= (lBufferSize % wfexCaptureFormat.nBlockAlign);
250 for (i=0;i<4;i++)
252 memset(&pData->WaveInBuffer[i], 0, sizeof(WAVEHDR));
253 pData->WaveInBuffer[i].dwBufferLength = lBufferSize;
254 pData->WaveInBuffer[i].lpData = calloc(1,pData->WaveInBuffer[i].dwBufferLength);
255 pData->WaveInBuffer[i].dwFlags = 0;
256 pData->WaveInBuffer[i].dwLoops = 0;
257 waveInPrepareHeader(pData->hWaveInHandle, &pData->WaveInBuffer[i], sizeof(WAVEHDR));
258 waveInAddBuffer(pData->hWaveInHandle, &pData->WaveInBuffer[i], sizeof(WAVEHDR));
259 pData->lWaveInBuffersCommitted++;
262 pData->ulReadCapturedDataPos = 0;
263 pData->ulWriteCapturedDataPos = 0;
265 pDevice->ExtraData = pData;
267 pData->hWaveInThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)CaptureThreadProc, (LPVOID)pDevice, 0, &pData->ulWaveInThreadID);
268 if (pData->hWaveInThread == NULL)
269 goto failure;
271 pDevice->szDeviceName = strdup(CaptureDeviceList[lDeviceID]);
272 return ALC_TRUE;
274 failure:
275 for (i=0;i<4;i++)
277 if(pData->WaveInBuffer[i].lpData)
279 waveInUnprepareHeader(pData->hWaveInHandle, &pData->WaveInBuffer[i], sizeof(WAVEHDR));
280 free(pData->WaveInBuffer[i].lpData);
284 free(pData->pCapturedSampleData);
285 if(pData->hWaveInHandle)
286 waveInClose(pData->hWaveInHandle);
287 if(pData->hWaveInThread)
288 CloseHandle(pData->hWaveInThread);
289 if (pData->hWaveInHdrEvent)
290 CloseHandle(pData->hWaveInHdrEvent);
291 if (pData->hWaveInThreadEvent)
292 CloseHandle(pData->hWaveInThreadEvent);
294 free(pData);
295 pDevice->ExtraData = NULL;
296 return ALC_FALSE;
299 static void WinMMCloseCapture(ALCdevice *pDevice)
301 WinMMData *pData = (WinMMData*)pDevice->ExtraData;
302 int i;
304 // Call waveOutReset to shutdown wave device
305 pData->bWaveInShutdown = AL_TRUE;
306 waveInReset(pData->hWaveInHandle);
308 // Wait for signal that all Wave Buffers have returned
309 WaitForSingleObjectEx(pData->hWaveInHdrEvent, 5000, FALSE);
311 // Wait for signal that Wave Thread has been destroyed
312 WaitForSingleObjectEx(pData->hWaveInThreadEvent, 5000, FALSE);
314 // Release the wave buffers
315 for (i=0;i<4;i++)
317 waveInUnprepareHeader(pData->hWaveInHandle, &pData->WaveInBuffer[i], sizeof(WAVEHDR));
318 free(pData->WaveInBuffer[i].lpData);
321 // Free Audio Buffer data
322 free(pData->pCapturedSampleData);
323 pData->pCapturedSampleData = NULL;
325 // Close the Wave device
326 waveInClose(pData->hWaveInHandle);
327 pData->hWaveInHandle = 0;
329 CloseHandle(pData->hWaveInThread);
330 pData->hWaveInThread = 0;
332 if (pData->hWaveInHdrEvent)
334 CloseHandle(pData->hWaveInHdrEvent);
335 pData->hWaveInHdrEvent = 0;
338 if (pData->hWaveInThreadEvent)
340 CloseHandle(pData->hWaveInThreadEvent);
341 pData->hWaveInThreadEvent = 0;
344 free(pData);
345 pDevice->ExtraData = NULL;
348 static void WinMMStartCapture(ALCdevice *pDevice)
350 WinMMData *pData = (WinMMData*)pDevice->ExtraData;
351 waveInStart(pData->hWaveInHandle);
354 static void WinMMStopCapture(ALCdevice *pDevice)
356 WinMMData *pData = (WinMMData*)pDevice->ExtraData;
357 waveInStop(pData->hWaveInHandle);
360 static void WinMMCaptureSamples(ALCdevice *pDevice, ALCvoid *pBuffer, ALCuint lSamples)
362 WinMMData *pData = (WinMMData*)pDevice->ExtraData;
363 ALuint ulSamples = (unsigned long)lSamples;
364 ALuint ulBytes, ulBytesToCopy;
365 ALuint ulCapturedSamples;
366 ALuint ulReadOffset;
367 ALuint frameSize = aluBytesFromFormat(pDevice->Format) *
368 aluChannelsFromFormat(pDevice->Format);
370 // Check that we have the requested numbers of Samples
371 ulCapturedSamples = (pData->ulWriteCapturedDataPos -
372 pData->ulReadCapturedDataPos) /
373 frameSize;
374 if(ulSamples > ulCapturedSamples)
376 alcSetError(ALC_INVALID_VALUE);
377 return;
380 ulBytes = ulSamples * frameSize;
382 // Get Read Offset
383 ulReadOffset = (pData->ulReadCapturedDataPos % pData->ulCapturedDataSize);
385 // Check for wrap-around condition
386 if ((ulReadOffset + ulBytes) > pData->ulCapturedDataSize)
388 // Copy data from last Read position to end of data
389 ulBytesToCopy = pData->ulCapturedDataSize - ulReadOffset;
390 memcpy(pBuffer, pData->pCapturedSampleData + ulReadOffset, ulBytesToCopy);
392 // Copy rest of the data from the start of the captured data
393 memcpy(((char *)pBuffer) + ulBytesToCopy, pData->pCapturedSampleData, ulBytes - ulBytesToCopy);
395 else
397 // Copy data from the read position in the captured data
398 memcpy(pBuffer, pData->pCapturedSampleData + ulReadOffset, ulBytes);
401 // Update Read Position
402 pData->ulReadCapturedDataPos += ulBytes;
405 static ALCuint WinMMAvailableSamples(ALCdevice *pDevice)
407 WinMMData *pData = (WinMMData*)pDevice->ExtraData;
408 ALCuint lCapturedBytes = (pData->ulWriteCapturedDataPos - pData->ulReadCapturedDataPos);
409 return lCapturedBytes / (aluBytesFromFormat(pDevice->Format) *
410 aluChannelsFromFormat(pDevice->Format));
414 BackendFuncs WinMMFuncs = {
415 WinMMOpenPlayback,
416 WinMMClosePlayback,
417 NULL,
418 NULL,
419 WinMMOpenCapture,
420 WinMMCloseCapture,
421 WinMMStartCapture,
422 WinMMStopCapture,
423 WinMMCaptureSamples,
424 WinMMAvailableSamples
427 void alcWinMMInit(BackendFuncs *FuncList)
429 *FuncList = WinMMFuncs;
432 void alcWinMMDeinit()
434 ALuint lLoop;
436 for(lLoop = 0; lLoop < NumCaptureDevices; lLoop++)
437 free(CaptureDeviceList[lLoop]);
438 free(CaptureDeviceList);
439 CaptureDeviceList = NULL;
441 NumCaptureDevices = 0;
444 void alcWinMMProbe(int type)
446 ALuint lLoop;
448 if(type != CAPTURE_DEVICE_PROBE)
449 return;
451 for(lLoop = 0; lLoop < NumCaptureDevices; lLoop++)
452 free(CaptureDeviceList[lLoop]);
454 NumCaptureDevices = waveInGetNumDevs();
455 CaptureDeviceList = realloc(CaptureDeviceList, sizeof(ALCchar*) * NumCaptureDevices);
456 for(lLoop = 0; lLoop < NumCaptureDevices; lLoop++)
458 WAVEINCAPS WaveInCaps;
460 if(waveInGetDevCaps(lLoop, &WaveInCaps, sizeof(WAVEINCAPS)) == MMSYSERR_NOERROR)
462 char name[128];
463 snprintf(name, sizeof(name), "WaveIn on %s", WaveInCaps.szPname);
464 AppendCaptureDeviceList(name);
465 CaptureDeviceList[lLoop] = strdup(name);
467 else
468 CaptureDeviceList[lLoop] = strdup("");