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
34 #ifndef WAVE_FORMAT_IEEE_FLOAT
35 #define WAVE_FORMAT_IEEE_FLOAT 0x0003
41 volatile ALboolean bWaveShutdown
;
42 HANDLE hWaveThreadEvent
;
45 volatile LONG lWaveBuffersCommitted
;
46 WAVEHDR WaveBuffer
[4];
59 static ALCchar
**PlaybackDeviceList
;
60 static ALuint NumPlaybackDevices
;
61 static ALCchar
**CaptureDeviceList
;
62 static ALuint NumCaptureDevices
;
65 static void ProbePlaybackDevices(void)
69 for(i
= 0;i
< NumPlaybackDevices
;i
++)
70 free(PlaybackDeviceList
[i
]);
72 NumPlaybackDevices
= waveOutGetNumDevs();
73 PlaybackDeviceList
= realloc(PlaybackDeviceList
, sizeof(ALCchar
*) * NumPlaybackDevices
);
74 for(i
= 0;i
< NumPlaybackDevices
;i
++)
78 PlaybackDeviceList
[i
] = NULL
;
79 if(waveOutGetDevCaps(i
, &WaveCaps
, sizeof(WaveCaps
)) == MMSYSERR_NOERROR
)
87 snprintf(name
, sizeof(name
), "%s", WaveCaps
.szPname
);
89 snprintf(name
, sizeof(name
), "%s #%d", WaveCaps
.szPname
, count
+1);
94 if(strcmp(name
, PlaybackDeviceList
[j
]) == 0)
99 PlaybackDeviceList
[i
] = strdup(name
);
104 static void ProbeCaptureDevices(void)
108 for(i
= 0;i
< NumCaptureDevices
;i
++)
109 free(CaptureDeviceList
[i
]);
111 NumCaptureDevices
= waveInGetNumDevs();
112 CaptureDeviceList
= realloc(CaptureDeviceList
, sizeof(ALCchar
*) * NumCaptureDevices
);
113 for(i
= 0;i
< NumCaptureDevices
;i
++)
115 WAVEINCAPS WaveInCaps
;
117 CaptureDeviceList
[i
] = NULL
;
118 if(waveInGetDevCaps(i
, &WaveInCaps
, sizeof(WAVEINCAPS
)) == MMSYSERR_NOERROR
)
126 snprintf(name
, sizeof(name
), "%s", WaveInCaps
.szPname
);
128 snprintf(name
, sizeof(name
), "%s #%d", WaveInCaps
.szPname
, count
+1);
133 if(strcmp(name
, CaptureDeviceList
[j
]) == 0)
138 CaptureDeviceList
[i
] = strdup(name
);
147 Posts a message to 'PlaybackThreadProc' everytime a WaveOut Buffer is completed and
148 returns to the application (for more data)
150 static void CALLBACK
WaveOutProc(HWAVEOUT hDevice
,UINT uMsg
,DWORD_PTR dwInstance
,DWORD_PTR dwParam1
,DWORD_PTR dwParam2
)
152 ALCdevice
*pDevice
= (ALCdevice
*)dwInstance
;
153 WinMMData
*pData
= pDevice
->ExtraData
;
161 InterlockedDecrement(&pData
->lWaveBuffersCommitted
);
162 PostThreadMessage(pData
->ulWaveThreadID
, uMsg
, 0, dwParam1
);
168 Used by "MMSYSTEM" Device. Called when a WaveOut buffer has used up its
171 static DWORD WINAPI
PlaybackThreadProc(LPVOID lpParameter
)
173 ALCdevice
*pDevice
= (ALCdevice
*)lpParameter
;
174 WinMMData
*pData
= pDevice
->ExtraData
;
179 FrameSize
= FrameSizeFromDevFmt(pDevice
->FmtChans
, pDevice
->FmtType
);
183 while(GetMessage(&msg
, NULL
, 0, 0))
185 if(msg
.message
!= WOM_DONE
)
188 if(pData
->bWaveShutdown
)
190 if(pData
->lWaveBuffersCommitted
== 0)
195 pWaveHdr
= ((LPWAVEHDR
)msg
.lParam
);
197 aluMixData(pDevice
, pWaveHdr
->lpData
, pWaveHdr
->dwBufferLength
/FrameSize
);
199 // Send buffer back to play more data
200 waveOutWrite(pData
->hWaveHandle
.Out
, pWaveHdr
, sizeof(WAVEHDR
));
201 InterlockedIncrement(&pData
->lWaveBuffersCommitted
);
204 // Signal Wave Thread completed event
205 if(pData
->hWaveThreadEvent
)
206 SetEvent(pData
->hWaveThreadEvent
);
216 Posts a message to 'CaptureThreadProc' everytime a WaveIn Buffer is completed and
217 returns to the application (with more data)
219 static void CALLBACK
WaveInProc(HWAVEIN hDevice
,UINT uMsg
,DWORD_PTR dwInstance
,DWORD_PTR dwParam1
,DWORD_PTR dwParam2
)
221 ALCdevice
*pDevice
= (ALCdevice
*)dwInstance
;
222 WinMMData
*pData
= pDevice
->ExtraData
;
230 InterlockedDecrement(&pData
->lWaveBuffersCommitted
);
231 PostThreadMessage(pData
->ulWaveThreadID
,uMsg
,0,dwParam1
);
237 Used by "MMSYSTEM" Device. Called when a WaveIn buffer had been filled with new
240 static DWORD WINAPI
CaptureThreadProc(LPVOID lpParameter
)
242 ALCdevice
*pDevice
= (ALCdevice
*)lpParameter
;
243 WinMMData
*pData
= pDevice
->ExtraData
;
248 FrameSize
= FrameSizeFromDevFmt(pDevice
->FmtChans
, pDevice
->FmtType
);
250 while(GetMessage(&msg
, NULL
, 0, 0))
252 if(msg
.message
!= WIM_DATA
)
254 /* Don't wait for other buffers to finish before quitting. We're
255 * closing so we don't need them. */
256 if(pData
->bWaveShutdown
)
259 pWaveHdr
= ((LPWAVEHDR
)msg
.lParam
);
261 WriteRingBuffer(pData
->pRing
, (ALubyte
*)pWaveHdr
->lpData
,
262 pWaveHdr
->dwBytesRecorded
/FrameSize
);
264 // Send buffer back to capture more data
265 waveInAddBuffer(pData
->hWaveHandle
.In
,pWaveHdr
,sizeof(WAVEHDR
));
266 InterlockedIncrement(&pData
->lWaveBuffersCommitted
);
269 // Signal Wave Thread completed event
270 if(pData
->hWaveThreadEvent
)
271 SetEvent(pData
->hWaveThreadEvent
);
279 static ALCenum
WinMMOpenPlayback(ALCdevice
*pDevice
, const ALCchar
*deviceName
)
281 WAVEFORMATEX wfexFormat
;
282 WinMMData
*pData
= NULL
;
287 if(!PlaybackDeviceList
)
288 ProbePlaybackDevices();
290 // Find the Device ID matching the deviceName if valid
291 for(i
= 0;i
< NumPlaybackDevices
;i
++)
293 if(PlaybackDeviceList
[i
] &&
294 (!deviceName
|| strcmp(deviceName
, PlaybackDeviceList
[i
]) == 0))
300 if(i
== NumPlaybackDevices
)
301 return ALC_INVALID_VALUE
;
303 pData
= calloc(1, sizeof(*pData
));
305 return ALC_OUT_OF_MEMORY
;
306 pDevice
->ExtraData
= pData
;
308 if(pDevice
->FmtChans
!= DevFmtMono
)
310 if((pDevice
->Flags
&DEVICE_CHANNELS_REQUEST
) &&
311 pDevice
->FmtChans
!= DevFmtStereo
)
313 ERR("Failed to set %s, got Stereo instead\n", DevFmtChannelsString(pDevice
->FmtChans
));
314 pDevice
->Flags
&= ~DEVICE_CHANNELS_REQUEST
;
316 pDevice
->FmtChans
= DevFmtStereo
;
318 switch(pDevice
->FmtType
)
321 pDevice
->FmtType
= DevFmtUByte
;
326 pDevice
->FmtType
= DevFmtShort
;
335 memset(&wfexFormat
, 0, sizeof(WAVEFORMATEX
));
336 wfexFormat
.wFormatTag
= ((pDevice
->FmtType
== DevFmtFloat
) ?
337 WAVE_FORMAT_IEEE_FLOAT
: WAVE_FORMAT_PCM
);
338 wfexFormat
.nChannels
= ChannelsFromDevFmt(pDevice
->FmtChans
);
339 wfexFormat
.wBitsPerSample
= BytesFromDevFmt(pDevice
->FmtType
) * 8;
340 wfexFormat
.nBlockAlign
= wfexFormat
.wBitsPerSample
*
341 wfexFormat
.nChannels
/ 8;
342 wfexFormat
.nSamplesPerSec
= pDevice
->Frequency
;
343 wfexFormat
.nAvgBytesPerSec
= wfexFormat
.nSamplesPerSec
*
344 wfexFormat
.nBlockAlign
;
345 wfexFormat
.cbSize
= 0;
347 if((res
=waveOutOpen(&pData
->hWaveHandle
.Out
, lDeviceID
, &wfexFormat
, (DWORD_PTR
)&WaveOutProc
, (DWORD_PTR
)pDevice
, CALLBACK_FUNCTION
)) != MMSYSERR_NOERROR
)
349 if(pDevice
->FmtType
== DevFmtFloat
)
351 pDevice
->FmtType
= DevFmtShort
;
354 ERR("waveOutOpen failed: %u\n", res
);
358 pData
->hWaveThreadEvent
= CreateEvent(NULL
, FALSE
, FALSE
, NULL
);
359 if(pData
->hWaveThreadEvent
== NULL
)
361 ERR("CreateEvent failed: %lu\n", GetLastError());
365 pData
->Frequency
= pDevice
->Frequency
;
367 pDevice
->szDeviceName
= strdup(PlaybackDeviceList
[lDeviceID
]);
371 if(pData
->hWaveThreadEvent
)
372 CloseHandle(pData
->hWaveThreadEvent
);
374 if(pData
->hWaveHandle
.Out
)
375 waveOutClose(pData
->hWaveHandle
.Out
);
378 pDevice
->ExtraData
= NULL
;
379 return ALC_INVALID_VALUE
;
382 static void WinMMClosePlayback(ALCdevice
*device
)
384 WinMMData
*pData
= (WinMMData
*)device
->ExtraData
;
386 // Close the Wave device
387 CloseHandle(pData
->hWaveThreadEvent
);
388 pData
->hWaveThreadEvent
= 0;
390 waveOutClose(pData
->hWaveHandle
.Out
);
391 pData
->hWaveHandle
.Out
= 0;
394 device
->ExtraData
= NULL
;
397 static ALCboolean
WinMMResetPlayback(ALCdevice
*device
)
399 WinMMData
*pData
= (WinMMData
*)device
->ExtraData
;
401 device
->UpdateSize
= (ALuint
)((ALuint64
)device
->UpdateSize
*
402 pData
->Frequency
/ device
->Frequency
);
403 device
->UpdateSize
= device
->UpdateSize
*device
->NumUpdates
/ 4;
404 device
->NumUpdates
= 4;
405 device
->Frequency
= pData
->Frequency
;
407 SetDefaultWFXChannelOrder(device
);
412 static ALCboolean
WinMMStartPlayback(ALCdevice
*device
)
414 WinMMData
*pData
= (WinMMData
*)device
->ExtraData
;
419 pData
->hWaveThread
= CreateThread(NULL
, 0, (LPTHREAD_START_ROUTINE
)PlaybackThreadProc
, (LPVOID
)device
, 0, &pData
->ulWaveThreadID
);
420 if(pData
->hWaveThread
== NULL
)
423 pData
->lWaveBuffersCommitted
= 0;
426 lBufferSize
= device
->UpdateSize
*device
->NumUpdates
/ 4;
427 lBufferSize
*= FrameSizeFromDevFmt(device
->FmtChans
, device
->FmtType
);
429 BufferData
= calloc(4, lBufferSize
);
432 memset(&pData
->WaveBuffer
[i
], 0, sizeof(WAVEHDR
));
433 pData
->WaveBuffer
[i
].dwBufferLength
= lBufferSize
;
434 pData
->WaveBuffer
[i
].lpData
= ((i
==0) ? (LPSTR
)BufferData
:
435 (pData
->WaveBuffer
[i
-1].lpData
+
436 pData
->WaveBuffer
[i
-1].dwBufferLength
));
437 waveOutPrepareHeader(pData
->hWaveHandle
.Out
, &pData
->WaveBuffer
[i
], sizeof(WAVEHDR
));
438 waveOutWrite(pData
->hWaveHandle
.Out
, &pData
->WaveBuffer
[i
], sizeof(WAVEHDR
));
439 InterlockedIncrement(&pData
->lWaveBuffersCommitted
);
445 static void WinMMStopPlayback(ALCdevice
*device
)
447 WinMMData
*pData
= (WinMMData
*)device
->ExtraData
;
451 if(pData
->hWaveThread
== NULL
)
454 // Set flag to stop processing headers
455 pData
->bWaveShutdown
= AL_TRUE
;
457 // Wait for signal that Wave Thread has been destroyed
458 WaitForSingleObjectEx(pData
->hWaveThreadEvent
, 5000, FALSE
);
460 CloseHandle(pData
->hWaveThread
);
461 pData
->hWaveThread
= 0;
463 pData
->bWaveShutdown
= AL_FALSE
;
465 // Release the wave buffers
468 waveOutUnprepareHeader(pData
->hWaveHandle
.Out
, &pData
->WaveBuffer
[i
], sizeof(WAVEHDR
));
469 if(i
== 0) buffer
= pData
->WaveBuffer
[i
].lpData
;
470 pData
->WaveBuffer
[i
].lpData
= NULL
;
476 static ALCenum
WinMMOpenCapture(ALCdevice
*pDevice
, const ALCchar
*deviceName
)
478 WAVEFORMATEX wfexCaptureFormat
;
479 ALbyte
*BufferData
= NULL
;
480 DWORD ulCapturedDataSize
;
481 WinMMData
*pData
= NULL
;
487 if(!CaptureDeviceList
)
488 ProbeCaptureDevices();
490 // Find the Device ID matching the deviceName if valid
491 for(i
= 0;i
< NumCaptureDevices
;i
++)
493 if(CaptureDeviceList
[i
] &&
494 (!deviceName
|| strcmp(deviceName
, CaptureDeviceList
[i
]) == 0))
500 if(i
== NumCaptureDevices
)
501 return ALC_INVALID_VALUE
;
503 switch(pDevice
->FmtChans
)
514 return ALC_INVALID_ENUM
;
517 switch(pDevice
->FmtType
)
528 return ALC_INVALID_ENUM
;
531 pData
= calloc(1, sizeof(*pData
));
533 return ALC_OUT_OF_MEMORY
;
534 pDevice
->ExtraData
= pData
;
536 memset(&wfexCaptureFormat
, 0, sizeof(WAVEFORMATEX
));
537 wfexCaptureFormat
.wFormatTag
= ((pDevice
->FmtType
== DevFmtFloat
) ?
538 WAVE_FORMAT_IEEE_FLOAT
: WAVE_FORMAT_PCM
);
539 wfexCaptureFormat
.nChannels
= ChannelsFromDevFmt(pDevice
->FmtChans
);
540 wfexCaptureFormat
.wBitsPerSample
= BytesFromDevFmt(pDevice
->FmtType
) * 8;
541 wfexCaptureFormat
.nBlockAlign
= wfexCaptureFormat
.wBitsPerSample
*
542 wfexCaptureFormat
.nChannels
/ 8;
543 wfexCaptureFormat
.nSamplesPerSec
= pDevice
->Frequency
;
544 wfexCaptureFormat
.nAvgBytesPerSec
= wfexCaptureFormat
.nSamplesPerSec
*
545 wfexCaptureFormat
.nBlockAlign
;
546 wfexCaptureFormat
.cbSize
= 0;
548 if((res
=waveInOpen(&pData
->hWaveHandle
.In
, lDeviceID
, &wfexCaptureFormat
, (DWORD_PTR
)&WaveInProc
, (DWORD_PTR
)pDevice
, CALLBACK_FUNCTION
)) != MMSYSERR_NOERROR
)
550 ERR("waveInOpen failed: %u\n", res
);
554 pData
->hWaveThreadEvent
= CreateEvent(NULL
, FALSE
, FALSE
, NULL
);
555 if(pData
->hWaveThreadEvent
== NULL
)
557 ERR("CreateEvent failed: %lu\n", GetLastError());
561 pData
->Frequency
= pDevice
->Frequency
;
563 // Allocate circular memory buffer for the captured audio
564 ulCapturedDataSize
= pDevice
->UpdateSize
*pDevice
->NumUpdates
;
566 // Make sure circular buffer is at least 100ms in size
567 if(ulCapturedDataSize
< (wfexCaptureFormat
.nSamplesPerSec
/ 10))
568 ulCapturedDataSize
= wfexCaptureFormat
.nSamplesPerSec
/ 10;
570 pData
->pRing
= CreateRingBuffer(wfexCaptureFormat
.nBlockAlign
, ulCapturedDataSize
);
574 pData
->lWaveBuffersCommitted
= 0;
576 // Create 4 Buffers of 50ms each
577 lBufferSize
= wfexCaptureFormat
.nAvgBytesPerSec
/ 20;
578 lBufferSize
-= (lBufferSize
% wfexCaptureFormat
.nBlockAlign
);
580 BufferData
= calloc(4, lBufferSize
);
586 memset(&pData
->WaveBuffer
[i
], 0, sizeof(WAVEHDR
));
587 pData
->WaveBuffer
[i
].dwBufferLength
= lBufferSize
;
588 pData
->WaveBuffer
[i
].lpData
= ((i
==0) ? (LPSTR
)BufferData
:
589 (pData
->WaveBuffer
[i
-1].lpData
+
590 pData
->WaveBuffer
[i
-1].dwBufferLength
));
591 pData
->WaveBuffer
[i
].dwFlags
= 0;
592 pData
->WaveBuffer
[i
].dwLoops
= 0;
593 waveInPrepareHeader(pData
->hWaveHandle
.In
, &pData
->WaveBuffer
[i
], sizeof(WAVEHDR
));
594 waveInAddBuffer(pData
->hWaveHandle
.In
, &pData
->WaveBuffer
[i
], sizeof(WAVEHDR
));
595 InterlockedIncrement(&pData
->lWaveBuffersCommitted
);
598 pData
->hWaveThread
= CreateThread(NULL
, 0, (LPTHREAD_START_ROUTINE
)CaptureThreadProc
, (LPVOID
)pDevice
, 0, &pData
->ulWaveThreadID
);
599 if (pData
->hWaveThread
== NULL
)
602 pDevice
->szDeviceName
= strdup(CaptureDeviceList
[lDeviceID
]);
606 if(pData
->hWaveThread
)
607 CloseHandle(pData
->hWaveThread
);
612 waveInUnprepareHeader(pData
->hWaveHandle
.In
, &pData
->WaveBuffer
[i
], sizeof(WAVEHDR
));
617 DestroyRingBuffer(pData
->pRing
);
619 if(pData
->hWaveThreadEvent
)
620 CloseHandle(pData
->hWaveThreadEvent
);
622 if(pData
->hWaveHandle
.In
)
623 waveInClose(pData
->hWaveHandle
.In
);
626 pDevice
->ExtraData
= NULL
;
627 return ALC_INVALID_VALUE
;
630 static void WinMMCloseCapture(ALCdevice
*pDevice
)
632 WinMMData
*pData
= (WinMMData
*)pDevice
->ExtraData
;
636 /* Tell the processing thread to quit and wait for it to do so. */
637 pData
->bWaveShutdown
= AL_TRUE
;
638 PostThreadMessage(pData
->ulWaveThreadID
, WM_QUIT
, 0, 0);
640 WaitForSingleObjectEx(pData
->hWaveThreadEvent
, 5000, FALSE
);
642 /* Make sure capture is stopped and all pending buffers are flushed. */
643 waveInReset(pData
->hWaveHandle
.In
);
645 CloseHandle(pData
->hWaveThread
);
646 pData
->hWaveThread
= 0;
648 // Release the wave buffers
651 waveInUnprepareHeader(pData
->hWaveHandle
.In
, &pData
->WaveBuffer
[i
], sizeof(WAVEHDR
));
652 if(i
== 0) buffer
= pData
->WaveBuffer
[i
].lpData
;
653 pData
->WaveBuffer
[i
].lpData
= NULL
;
657 DestroyRingBuffer(pData
->pRing
);
660 // Close the Wave device
661 CloseHandle(pData
->hWaveThreadEvent
);
662 pData
->hWaveThreadEvent
= 0;
664 waveInClose(pData
->hWaveHandle
.In
);
665 pData
->hWaveHandle
.In
= 0;
668 pDevice
->ExtraData
= NULL
;
671 static void WinMMStartCapture(ALCdevice
*pDevice
)
673 WinMMData
*pData
= (WinMMData
*)pDevice
->ExtraData
;
674 waveInStart(pData
->hWaveHandle
.In
);
677 static void WinMMStopCapture(ALCdevice
*pDevice
)
679 WinMMData
*pData
= (WinMMData
*)pDevice
->ExtraData
;
680 waveInStop(pData
->hWaveHandle
.In
);
683 static ALCenum
WinMMCaptureSamples(ALCdevice
*pDevice
, ALCvoid
*pBuffer
, ALCuint lSamples
)
685 WinMMData
*pData
= (WinMMData
*)pDevice
->ExtraData
;
686 ReadRingBuffer(pData
->pRing
, pBuffer
, lSamples
);
690 static ALCuint
WinMMAvailableSamples(ALCdevice
*pDevice
)
692 WinMMData
*pData
= (WinMMData
*)pDevice
->ExtraData
;
693 return RingBufferSize(pData
->pRing
);
697 static const BackendFuncs WinMMFuncs
= {
708 WinMMAvailableSamples
711 ALCboolean
alcWinMMInit(BackendFuncs
*FuncList
)
713 *FuncList
= WinMMFuncs
;
717 void alcWinMMDeinit()
721 for(lLoop
= 0;lLoop
< NumPlaybackDevices
;lLoop
++)
722 free(PlaybackDeviceList
[lLoop
]);
723 free(PlaybackDeviceList
);
724 PlaybackDeviceList
= NULL
;
726 NumPlaybackDevices
= 0;
729 for(lLoop
= 0; lLoop
< NumCaptureDevices
; lLoop
++)
730 free(CaptureDeviceList
[lLoop
]);
731 free(CaptureDeviceList
);
732 CaptureDeviceList
= NULL
;
734 NumCaptureDevices
= 0;
737 void alcWinMMProbe(enum DevProbe type
)
743 case ALL_DEVICE_PROBE
:
744 ProbePlaybackDevices();
745 for(i
= 0;i
< NumPlaybackDevices
;i
++)
747 if(PlaybackDeviceList
[i
])
748 AppendAllDeviceList(PlaybackDeviceList
[i
]);
752 case CAPTURE_DEVICE_PROBE
:
753 ProbeCaptureDevices();
754 for(i
= 0;i
< NumCaptureDevices
;i
++)
756 if(CaptureDeviceList
[i
])
757 AppendCaptureDeviceList(CaptureDeviceList
[i
]);