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 const ALCchar woDefault
[] = "WaveOut Default";
61 static ALCchar
**PlaybackDeviceList
;
62 static ALuint NumPlaybackDevices
;
63 static ALCchar
**CaptureDeviceList
;
64 static ALuint NumCaptureDevices
;
67 static void ProbePlaybackDevices(void)
71 for(i
= 0;i
< NumPlaybackDevices
;i
++)
72 free(PlaybackDeviceList
[i
]);
74 NumPlaybackDevices
= waveOutGetNumDevs();
75 PlaybackDeviceList
= realloc(PlaybackDeviceList
, sizeof(ALCchar
*) * NumPlaybackDevices
);
76 for(i
= 0;i
< NumPlaybackDevices
;i
++)
80 PlaybackDeviceList
[i
] = NULL
;
81 if(waveOutGetDevCaps(i
, &WaveCaps
, sizeof(WaveCaps
)) == MMSYSERR_NOERROR
)
89 snprintf(name
, sizeof(name
), "%s", WaveCaps
.szPname
);
91 snprintf(name
, sizeof(name
), "%s #%d", WaveCaps
.szPname
, count
+1);
96 if(strcmp(name
, PlaybackDeviceList
[j
]) == 0)
101 PlaybackDeviceList
[i
] = strdup(name
);
106 static void ProbeCaptureDevices(void)
110 for(i
= 0;i
< NumCaptureDevices
;i
++)
111 free(CaptureDeviceList
[i
]);
113 NumCaptureDevices
= waveInGetNumDevs();
114 CaptureDeviceList
= realloc(CaptureDeviceList
, sizeof(ALCchar
*) * NumCaptureDevices
);
115 for(i
= 0;i
< NumCaptureDevices
;i
++)
117 WAVEINCAPS WaveInCaps
;
119 CaptureDeviceList
[i
] = NULL
;
120 if(waveInGetDevCaps(i
, &WaveInCaps
, sizeof(WAVEINCAPS
)) == MMSYSERR_NOERROR
)
128 snprintf(name
, sizeof(name
), "%s", WaveInCaps
.szPname
);
130 snprintf(name
, sizeof(name
), "%s #%d", WaveInCaps
.szPname
, count
+1);
135 if(strcmp(name
, CaptureDeviceList
[j
]) == 0)
140 CaptureDeviceList
[i
] = strdup(name
);
149 Posts a message to 'PlaybackThreadProc' everytime a WaveOut Buffer is completed and
150 returns to the application (for more data)
152 static void CALLBACK
WaveOutProc(HWAVEOUT hDevice
,UINT uMsg
,DWORD_PTR dwInstance
,DWORD_PTR dwParam1
,DWORD_PTR dwParam2
)
154 ALCdevice
*pDevice
= (ALCdevice
*)dwInstance
;
155 WinMMData
*pData
= pDevice
->ExtraData
;
163 InterlockedDecrement(&pData
->lWaveBuffersCommitted
);
164 PostThreadMessage(pData
->ulWaveThreadID
, uMsg
, 0, dwParam1
);
170 Used by "MMSYSTEM" Device. Called when a WaveOut buffer has used up its
173 static DWORD WINAPI
PlaybackThreadProc(LPVOID lpParameter
)
175 ALCdevice
*pDevice
= (ALCdevice
*)lpParameter
;
176 WinMMData
*pData
= pDevice
->ExtraData
;
181 FrameSize
= FrameSizeFromDevFmt(pDevice
->FmtChans
, pDevice
->FmtType
);
185 while(GetMessage(&msg
, NULL
, 0, 0))
187 if(msg
.message
!= WOM_DONE
)
190 if(pData
->bWaveShutdown
)
192 if(pData
->lWaveBuffersCommitted
== 0)
197 pWaveHdr
= ((LPWAVEHDR
)msg
.lParam
);
199 aluMixData(pDevice
, pWaveHdr
->lpData
, pWaveHdr
->dwBufferLength
/FrameSize
);
201 // Send buffer back to play more data
202 waveOutWrite(pData
->hWaveHandle
.Out
, pWaveHdr
, sizeof(WAVEHDR
));
203 InterlockedIncrement(&pData
->lWaveBuffersCommitted
);
206 // Signal Wave Thread completed event
207 if(pData
->hWaveThreadEvent
)
208 SetEvent(pData
->hWaveThreadEvent
);
218 Posts a message to 'CaptureThreadProc' everytime a WaveIn Buffer is completed and
219 returns to the application (with more data)
221 static void CALLBACK
WaveInProc(HWAVEIN hDevice
,UINT uMsg
,DWORD_PTR dwInstance
,DWORD_PTR dwParam1
,DWORD_PTR dwParam2
)
223 ALCdevice
*pDevice
= (ALCdevice
*)dwInstance
;
224 WinMMData
*pData
= pDevice
->ExtraData
;
232 InterlockedDecrement(&pData
->lWaveBuffersCommitted
);
233 PostThreadMessage(pData
->ulWaveThreadID
,uMsg
,0,dwParam1
);
239 Used by "MMSYSTEM" Device. Called when a WaveIn buffer had been filled with new
242 static DWORD WINAPI
CaptureThreadProc(LPVOID lpParameter
)
244 ALCdevice
*pDevice
= (ALCdevice
*)lpParameter
;
245 WinMMData
*pData
= pDevice
->ExtraData
;
250 FrameSize
= FrameSizeFromDevFmt(pDevice
->FmtChans
, pDevice
->FmtType
);
252 while(GetMessage(&msg
, NULL
, 0, 0))
254 if(msg
.message
!= WIM_DATA
)
256 /* Don't wait for other buffers to finish before quitting. We're
257 * closing so we don't need them. */
258 if(pData
->bWaveShutdown
)
261 pWaveHdr
= ((LPWAVEHDR
)msg
.lParam
);
263 WriteRingBuffer(pData
->pRing
, (ALubyte
*)pWaveHdr
->lpData
,
264 pWaveHdr
->dwBytesRecorded
/FrameSize
);
266 // Send buffer back to capture more data
267 waveInAddBuffer(pData
->hWaveHandle
.In
,pWaveHdr
,sizeof(WAVEHDR
));
268 InterlockedIncrement(&pData
->lWaveBuffersCommitted
);
271 // Signal Wave Thread completed event
272 if(pData
->hWaveThreadEvent
)
273 SetEvent(pData
->hWaveThreadEvent
);
281 static ALCenum
WinMMOpenPlayback(ALCdevice
*pDevice
, const ALCchar
*deviceName
)
283 WAVEFORMATEX wfexFormat
;
284 WinMMData
*pData
= NULL
;
289 // Find the Device ID matching the deviceName if valid
290 if(!deviceName
|| strcmp(deviceName
, woDefault
) == 0)
291 lDeviceID
= WAVE_MAPPER
;
294 if(!PlaybackDeviceList
)
295 ProbePlaybackDevices();
297 for(i
= 0;i
< NumPlaybackDevices
;i
++)
299 if(PlaybackDeviceList
[i
] &&
300 strcmp(deviceName
, PlaybackDeviceList
[i
]) == 0)
306 if(i
== NumPlaybackDevices
)
307 return ALC_INVALID_VALUE
;
310 pData
= calloc(1, sizeof(*pData
));
312 return ALC_OUT_OF_MEMORY
;
313 pDevice
->ExtraData
= pData
;
315 if(pDevice
->FmtChans
!= DevFmtMono
)
317 if((pDevice
->Flags
&DEVICE_CHANNELS_REQUEST
) &&
318 pDevice
->FmtChans
!= DevFmtStereo
)
320 ERR("Failed to set %s, got Stereo instead\n", DevFmtChannelsString(pDevice
->FmtChans
));
321 pDevice
->Flags
&= ~DEVICE_CHANNELS_REQUEST
;
323 pDevice
->FmtChans
= DevFmtStereo
;
325 switch(pDevice
->FmtType
)
328 pDevice
->FmtType
= DevFmtUByte
;
333 pDevice
->FmtType
= DevFmtShort
;
342 memset(&wfexFormat
, 0, sizeof(WAVEFORMATEX
));
343 wfexFormat
.wFormatTag
= ((pDevice
->FmtType
== DevFmtFloat
) ?
344 WAVE_FORMAT_IEEE_FLOAT
: WAVE_FORMAT_PCM
);
345 wfexFormat
.nChannels
= ChannelsFromDevFmt(pDevice
->FmtChans
);
346 wfexFormat
.wBitsPerSample
= BytesFromDevFmt(pDevice
->FmtType
) * 8;
347 wfexFormat
.nBlockAlign
= wfexFormat
.wBitsPerSample
*
348 wfexFormat
.nChannels
/ 8;
349 wfexFormat
.nSamplesPerSec
= pDevice
->Frequency
;
350 wfexFormat
.nAvgBytesPerSec
= wfexFormat
.nSamplesPerSec
*
351 wfexFormat
.nBlockAlign
;
352 wfexFormat
.cbSize
= 0;
354 if((res
=waveOutOpen(&pData
->hWaveHandle
.Out
, lDeviceID
, &wfexFormat
, (DWORD_PTR
)&WaveOutProc
, (DWORD_PTR
)pDevice
, CALLBACK_FUNCTION
)) != MMSYSERR_NOERROR
)
356 if(pDevice
->FmtType
== DevFmtFloat
)
358 pDevice
->FmtType
= DevFmtShort
;
361 ERR("waveOutOpen failed: %u\n", res
);
365 pData
->hWaveThreadEvent
= CreateEvent(NULL
, FALSE
, FALSE
, NULL
);
366 if(pData
->hWaveThreadEvent
== NULL
)
368 ERR("CreateEvent failed: %lu\n", GetLastError());
372 pData
->Frequency
= pDevice
->Frequency
;
374 pDevice
->szDeviceName
= strdup((lDeviceID
==WAVE_MAPPER
) ? woDefault
:
375 PlaybackDeviceList
[lDeviceID
]);
379 if(pData
->hWaveThreadEvent
)
380 CloseHandle(pData
->hWaveThreadEvent
);
382 if(pData
->hWaveHandle
.Out
)
383 waveOutClose(pData
->hWaveHandle
.Out
);
386 pDevice
->ExtraData
= NULL
;
387 return ALC_INVALID_VALUE
;
390 static void WinMMClosePlayback(ALCdevice
*device
)
392 WinMMData
*pData
= (WinMMData
*)device
->ExtraData
;
394 // Close the Wave device
395 CloseHandle(pData
->hWaveThreadEvent
);
396 pData
->hWaveThreadEvent
= 0;
398 waveOutClose(pData
->hWaveHandle
.Out
);
399 pData
->hWaveHandle
.Out
= 0;
402 device
->ExtraData
= NULL
;
405 static ALCboolean
WinMMResetPlayback(ALCdevice
*device
)
407 WinMMData
*pData
= (WinMMData
*)device
->ExtraData
;
412 pData
->hWaveThread
= CreateThread(NULL
, 0, (LPTHREAD_START_ROUTINE
)PlaybackThreadProc
, (LPVOID
)device
, 0, &pData
->ulWaveThreadID
);
413 if(pData
->hWaveThread
== NULL
)
416 device
->UpdateSize
= (ALuint
)((ALuint64
)device
->UpdateSize
*
417 pData
->Frequency
/ device
->Frequency
);
418 device
->Frequency
= pData
->Frequency
;
420 SetDefaultWFXChannelOrder(device
);
422 pData
->lWaveBuffersCommitted
= 0;
425 lBufferSize
= device
->UpdateSize
*device
->NumUpdates
/ 4;
426 lBufferSize
*= FrameSizeFromDevFmt(device
->FmtChans
, device
->FmtType
);
428 BufferData
= calloc(4, lBufferSize
);
431 memset(&pData
->WaveBuffer
[i
], 0, sizeof(WAVEHDR
));
432 pData
->WaveBuffer
[i
].dwBufferLength
= lBufferSize
;
433 pData
->WaveBuffer
[i
].lpData
= ((i
==0) ? (LPSTR
)BufferData
:
434 (pData
->WaveBuffer
[i
-1].lpData
+
435 pData
->WaveBuffer
[i
-1].dwBufferLength
));
436 waveOutPrepareHeader(pData
->hWaveHandle
.Out
, &pData
->WaveBuffer
[i
], sizeof(WAVEHDR
));
437 waveOutWrite(pData
->hWaveHandle
.Out
, &pData
->WaveBuffer
[i
], sizeof(WAVEHDR
));
438 InterlockedIncrement(&pData
->lWaveBuffersCommitted
);
444 static void WinMMStopPlayback(ALCdevice
*device
)
446 WinMMData
*pData
= (WinMMData
*)device
->ExtraData
;
450 if(pData
->hWaveThread
== NULL
)
453 // Set flag to stop processing headers
454 pData
->bWaveShutdown
= AL_TRUE
;
456 // Wait for signal that Wave Thread has been destroyed
457 WaitForSingleObjectEx(pData
->hWaveThreadEvent
, 5000, FALSE
);
459 CloseHandle(pData
->hWaveThread
);
460 pData
->hWaveThread
= 0;
462 pData
->bWaveShutdown
= AL_FALSE
;
464 // Release the wave buffers
467 waveOutUnprepareHeader(pData
->hWaveHandle
.Out
, &pData
->WaveBuffer
[i
], sizeof(WAVEHDR
));
468 if(i
== 0) buffer
= pData
->WaveBuffer
[i
].lpData
;
469 pData
->WaveBuffer
[i
].lpData
= NULL
;
475 static ALCenum
WinMMOpenCapture(ALCdevice
*pDevice
, const ALCchar
*deviceName
)
477 WAVEFORMATEX wfexCaptureFormat
;
478 ALbyte
*BufferData
= NULL
;
479 DWORD ulCapturedDataSize
;
480 WinMMData
*pData
= NULL
;
486 if(!CaptureDeviceList
)
487 ProbeCaptureDevices();
489 // Find the Device ID matching the deviceName if valid
492 for(i
= 0;i
< NumCaptureDevices
;i
++)
494 if(CaptureDeviceList
[i
] &&
495 strcmp(deviceName
, CaptureDeviceList
[i
]) == 0)
504 for(i
= 0;i
< NumCaptureDevices
;i
++)
506 if(CaptureDeviceList
[i
])
513 if(i
== NumCaptureDevices
)
514 return ALC_INVALID_VALUE
;
516 switch(pDevice
->FmtChans
)
527 return ALC_INVALID_ENUM
;
530 switch(pDevice
->FmtType
)
541 return ALC_INVALID_ENUM
;
544 pData
= calloc(1, sizeof(*pData
));
546 return ALC_OUT_OF_MEMORY
;
547 pDevice
->ExtraData
= pData
;
549 memset(&wfexCaptureFormat
, 0, sizeof(WAVEFORMATEX
));
550 wfexCaptureFormat
.wFormatTag
= ((pDevice
->FmtType
== DevFmtFloat
) ?
551 WAVE_FORMAT_IEEE_FLOAT
: WAVE_FORMAT_PCM
);
552 wfexCaptureFormat
.nChannels
= ChannelsFromDevFmt(pDevice
->FmtChans
);
553 wfexCaptureFormat
.wBitsPerSample
= BytesFromDevFmt(pDevice
->FmtType
) * 8;
554 wfexCaptureFormat
.nBlockAlign
= wfexCaptureFormat
.wBitsPerSample
*
555 wfexCaptureFormat
.nChannels
/ 8;
556 wfexCaptureFormat
.nSamplesPerSec
= pDevice
->Frequency
;
557 wfexCaptureFormat
.nAvgBytesPerSec
= wfexCaptureFormat
.nSamplesPerSec
*
558 wfexCaptureFormat
.nBlockAlign
;
559 wfexCaptureFormat
.cbSize
= 0;
561 if((res
=waveInOpen(&pData
->hWaveHandle
.In
, lDeviceID
, &wfexCaptureFormat
, (DWORD_PTR
)&WaveInProc
, (DWORD_PTR
)pDevice
, CALLBACK_FUNCTION
)) != MMSYSERR_NOERROR
)
563 ERR("waveInOpen failed: %u\n", res
);
567 pData
->hWaveThreadEvent
= CreateEvent(NULL
, FALSE
, FALSE
, NULL
);
568 if(pData
->hWaveThreadEvent
== NULL
)
570 ERR("CreateEvent failed: %lu\n", GetLastError());
574 pData
->Frequency
= pDevice
->Frequency
;
576 // Allocate circular memory buffer for the captured audio
577 ulCapturedDataSize
= pDevice
->UpdateSize
*pDevice
->NumUpdates
;
579 // Make sure circular buffer is at least 100ms in size
580 if(ulCapturedDataSize
< (wfexCaptureFormat
.nSamplesPerSec
/ 10))
581 ulCapturedDataSize
= wfexCaptureFormat
.nSamplesPerSec
/ 10;
583 pData
->pRing
= CreateRingBuffer(wfexCaptureFormat
.nBlockAlign
, ulCapturedDataSize
);
587 pData
->lWaveBuffersCommitted
= 0;
589 // Create 4 Buffers of 50ms each
590 lBufferSize
= wfexCaptureFormat
.nAvgBytesPerSec
/ 20;
591 lBufferSize
-= (lBufferSize
% wfexCaptureFormat
.nBlockAlign
);
593 BufferData
= calloc(4, lBufferSize
);
599 memset(&pData
->WaveBuffer
[i
], 0, sizeof(WAVEHDR
));
600 pData
->WaveBuffer
[i
].dwBufferLength
= lBufferSize
;
601 pData
->WaveBuffer
[i
].lpData
= ((i
==0) ? (LPSTR
)BufferData
:
602 (pData
->WaveBuffer
[i
-1].lpData
+
603 pData
->WaveBuffer
[i
-1].dwBufferLength
));
604 pData
->WaveBuffer
[i
].dwFlags
= 0;
605 pData
->WaveBuffer
[i
].dwLoops
= 0;
606 waveInPrepareHeader(pData
->hWaveHandle
.In
, &pData
->WaveBuffer
[i
], sizeof(WAVEHDR
));
607 waveInAddBuffer(pData
->hWaveHandle
.In
, &pData
->WaveBuffer
[i
], sizeof(WAVEHDR
));
608 InterlockedIncrement(&pData
->lWaveBuffersCommitted
);
611 pData
->hWaveThread
= CreateThread(NULL
, 0, (LPTHREAD_START_ROUTINE
)CaptureThreadProc
, (LPVOID
)pDevice
, 0, &pData
->ulWaveThreadID
);
612 if (pData
->hWaveThread
== NULL
)
615 pDevice
->szDeviceName
= strdup(CaptureDeviceList
[lDeviceID
]);
619 if(pData
->hWaveThread
)
620 CloseHandle(pData
->hWaveThread
);
625 waveInUnprepareHeader(pData
->hWaveHandle
.In
, &pData
->WaveBuffer
[i
], sizeof(WAVEHDR
));
630 DestroyRingBuffer(pData
->pRing
);
632 if(pData
->hWaveThreadEvent
)
633 CloseHandle(pData
->hWaveThreadEvent
);
635 if(pData
->hWaveHandle
.In
)
636 waveInClose(pData
->hWaveHandle
.In
);
639 pDevice
->ExtraData
= NULL
;
640 return ALC_INVALID_VALUE
;
643 static void WinMMCloseCapture(ALCdevice
*pDevice
)
645 WinMMData
*pData
= (WinMMData
*)pDevice
->ExtraData
;
649 /* Tell the processing thread to quit and wait for it to do so. */
650 pData
->bWaveShutdown
= AL_TRUE
;
651 PostThreadMessage(pData
->ulWaveThreadID
, WM_QUIT
, 0, 0);
653 WaitForSingleObjectEx(pData
->hWaveThreadEvent
, 5000, FALSE
);
655 /* Make sure capture is stopped and all pending buffers are flushed. */
656 waveInReset(pData
->hWaveHandle
.In
);
658 CloseHandle(pData
->hWaveThread
);
659 pData
->hWaveThread
= 0;
661 // Release the wave buffers
664 waveInUnprepareHeader(pData
->hWaveHandle
.In
, &pData
->WaveBuffer
[i
], sizeof(WAVEHDR
));
665 if(i
== 0) buffer
= pData
->WaveBuffer
[i
].lpData
;
666 pData
->WaveBuffer
[i
].lpData
= NULL
;
670 DestroyRingBuffer(pData
->pRing
);
673 // Close the Wave device
674 CloseHandle(pData
->hWaveThreadEvent
);
675 pData
->hWaveThreadEvent
= 0;
677 waveInClose(pData
->hWaveHandle
.In
);
678 pData
->hWaveHandle
.In
= 0;
681 pDevice
->ExtraData
= NULL
;
684 static void WinMMStartCapture(ALCdevice
*pDevice
)
686 WinMMData
*pData
= (WinMMData
*)pDevice
->ExtraData
;
687 waveInStart(pData
->hWaveHandle
.In
);
690 static void WinMMStopCapture(ALCdevice
*pDevice
)
692 WinMMData
*pData
= (WinMMData
*)pDevice
->ExtraData
;
693 waveInStop(pData
->hWaveHandle
.In
);
696 static ALCenum
WinMMCaptureSamples(ALCdevice
*pDevice
, ALCvoid
*pBuffer
, ALCuint lSamples
)
698 WinMMData
*pData
= (WinMMData
*)pDevice
->ExtraData
;
699 ReadRingBuffer(pData
->pRing
, pBuffer
, lSamples
);
703 static ALCuint
WinMMAvailableSamples(ALCdevice
*pDevice
)
705 WinMMData
*pData
= (WinMMData
*)pDevice
->ExtraData
;
706 return RingBufferSize(pData
->pRing
);
710 static const BackendFuncs WinMMFuncs
= {
720 WinMMAvailableSamples
723 ALCboolean
alcWinMMInit(BackendFuncs
*FuncList
)
725 *FuncList
= WinMMFuncs
;
729 void alcWinMMDeinit()
733 for(lLoop
= 0;lLoop
< NumPlaybackDevices
;lLoop
++)
734 free(PlaybackDeviceList
[lLoop
]);
735 free(PlaybackDeviceList
);
736 PlaybackDeviceList
= NULL
;
738 NumPlaybackDevices
= 0;
741 for(lLoop
= 0; lLoop
< NumCaptureDevices
; lLoop
++)
742 free(CaptureDeviceList
[lLoop
]);
743 free(CaptureDeviceList
);
744 CaptureDeviceList
= NULL
;
746 NumCaptureDevices
= 0;
749 void alcWinMMProbe(enum DevProbe type
)
755 case ALL_DEVICE_PROBE
:
756 ProbePlaybackDevices();
757 if(NumPlaybackDevices
> 0)
758 AppendAllDeviceList(woDefault
);
759 for(i
= 0;i
< NumPlaybackDevices
;i
++)
761 if(PlaybackDeviceList
[i
])
762 AppendAllDeviceList(PlaybackDeviceList
[i
]);
766 case CAPTURE_DEVICE_PROBE
:
767 ProbeCaptureDevices();
768 for(i
= 0;i
< NumCaptureDevices
;i
++)
770 if(CaptureDeviceList
[i
])
771 AppendCaptureDeviceList(CaptureDeviceList
[i
]);