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 killNow
;
42 HANDLE WaveThreadEvent
;
45 volatile LONG WaveBuffersCommitted
;
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 device
, UINT msg
, DWORD_PTR instance
, DWORD_PTR param1
, DWORD_PTR param2
)
152 ALCdevice
*Device
= (ALCdevice
*)instance
;
153 WinMMData
*data
= Device
->ExtraData
;
161 InterlockedDecrement(&data
->WaveBuffersCommitted
);
162 PostThreadMessage(data
->WaveThreadID
, msg
, 0, param1
);
168 Used by "MMSYSTEM" Device. Called when a WaveOut buffer has used up its
171 static DWORD WINAPI
PlaybackThreadProc(LPVOID param
)
173 ALCdevice
*Device
= (ALCdevice
*)param
;
174 WinMMData
*data
= Device
->ExtraData
;
179 FrameSize
= FrameSizeFromDevFmt(Device
->FmtChans
, Device
->FmtType
);
183 while(GetMessage(&msg
, NULL
, 0, 0))
185 if(msg
.message
!= WOM_DONE
)
190 if(data
->WaveBuffersCommitted
== 0)
195 WaveHdr
= ((LPWAVEHDR
)msg
.lParam
);
196 aluMixData(Device
, WaveHdr
->lpData
, WaveHdr
->dwBufferLength
/FrameSize
);
198 // Send buffer back to play more data
199 waveOutWrite(data
->WaveHandle
.Out
, WaveHdr
, sizeof(WAVEHDR
));
200 InterlockedIncrement(&data
->WaveBuffersCommitted
);
203 // Signal Wave Thread completed event
204 if(data
->WaveThreadEvent
)
205 SetEvent(data
->WaveThreadEvent
);
214 Posts a message to 'CaptureThreadProc' everytime a WaveIn Buffer is completed and
215 returns to the application (with more data)
217 static void CALLBACK
WaveInProc(HWAVEIN device
, UINT msg
, DWORD_PTR instance
, DWORD_PTR param1
, DWORD_PTR param2
)
219 ALCdevice
*Device
= (ALCdevice
*)instance
;
220 WinMMData
*data
= Device
->ExtraData
;
228 InterlockedDecrement(&data
->WaveBuffersCommitted
);
229 PostThreadMessage(data
->WaveThreadID
, msg
, 0, param1
);
235 Used by "MMSYSTEM" Device. Called when a WaveIn buffer had been filled with new
238 static DWORD WINAPI
CaptureThreadProc(LPVOID param
)
240 ALCdevice
*Device
= (ALCdevice
*)param
;
241 WinMMData
*data
= Device
->ExtraData
;
246 FrameSize
= FrameSizeFromDevFmt(Device
->FmtChans
, Device
->FmtType
);
248 while(GetMessage(&msg
, NULL
, 0, 0))
250 if(msg
.message
!= WIM_DATA
)
252 /* Don't wait for other buffers to finish before quitting. We're
253 * closing so we don't need them. */
257 WaveHdr
= ((LPWAVEHDR
)msg
.lParam
);
258 WriteRingBuffer(data
->Ring
, (ALubyte
*)WaveHdr
->lpData
, WaveHdr
->dwBytesRecorded
/FrameSize
);
260 // Send buffer back to capture more data
261 waveInAddBuffer(data
->WaveHandle
.In
, WaveHdr
, sizeof(WAVEHDR
));
262 InterlockedIncrement(&data
->WaveBuffersCommitted
);
265 // Signal Wave Thread completed event
266 if(data
->WaveThreadEvent
)
267 SetEvent(data
->WaveThreadEvent
);
274 static ALCenum
WinMMOpenPlayback(ALCdevice
*Device
, const ALCchar
*deviceName
)
276 WinMMData
*data
= NULL
;
281 if(!PlaybackDeviceList
)
282 ProbePlaybackDevices();
284 // Find the Device ID matching the deviceName if valid
285 for(i
= 0;i
< NumPlaybackDevices
;i
++)
287 if(PlaybackDeviceList
[i
] &&
288 (!deviceName
|| strcmp(deviceName
, PlaybackDeviceList
[i
]) == 0))
294 if(i
== NumPlaybackDevices
)
295 return ALC_INVALID_VALUE
;
297 data
= calloc(1, sizeof(*data
));
299 return ALC_OUT_OF_MEMORY
;
300 Device
->ExtraData
= data
;
303 memset(&data
->Format
, 0, sizeof(WAVEFORMATEX
));
304 if(Device
->FmtType
== DevFmtFloat
)
306 data
->Format
.wFormatTag
= WAVE_FORMAT_IEEE_FLOAT
;
307 data
->Format
.wBitsPerSample
= 32;
311 data
->Format
.wFormatTag
= WAVE_FORMAT_PCM
;
312 if(Device
->FmtType
== DevFmtUByte
|| Device
->FmtType
== DevFmtByte
)
313 data
->Format
.wBitsPerSample
= 8;
315 data
->Format
.wBitsPerSample
= 16;
317 data
->Format
.nChannels
= ((Device
->FmtChans
== DevFmtMono
) ? 1 : 2);
318 data
->Format
.nBlockAlign
= data
->Format
.wBitsPerSample
*
319 data
->Format
.nChannels
/ 8;
320 data
->Format
.nSamplesPerSec
= Device
->Frequency
;
321 data
->Format
.nAvgBytesPerSec
= data
->Format
.nSamplesPerSec
*
322 data
->Format
.nBlockAlign
;
323 data
->Format
.cbSize
= 0;
325 if((res
=waveOutOpen(&data
->WaveHandle
.Out
, DeviceID
, &data
->Format
, (DWORD_PTR
)&WaveOutProc
, (DWORD_PTR
)Device
, CALLBACK_FUNCTION
)) != MMSYSERR_NOERROR
)
327 if(Device
->FmtType
== DevFmtFloat
)
329 Device
->FmtType
= DevFmtShort
;
332 ERR("waveOutOpen failed: %u\n", res
);
336 data
->WaveThreadEvent
= CreateEvent(NULL
, FALSE
, FALSE
, NULL
);
337 if(data
->WaveThreadEvent
== NULL
)
339 ERR("CreateEvent failed: %lu\n", GetLastError());
343 Device
->DeviceName
= strdup(PlaybackDeviceList
[DeviceID
]);
347 if(data
->WaveThreadEvent
)
348 CloseHandle(data
->WaveThreadEvent
);
350 if(data
->WaveHandle
.Out
)
351 waveOutClose(data
->WaveHandle
.Out
);
354 Device
->ExtraData
= NULL
;
355 return ALC_INVALID_VALUE
;
358 static void WinMMClosePlayback(ALCdevice
*device
)
360 WinMMData
*data
= (WinMMData
*)device
->ExtraData
;
362 // Close the Wave device
363 CloseHandle(data
->WaveThreadEvent
);
364 data
->WaveThreadEvent
= 0;
366 waveOutClose(data
->WaveHandle
.Out
);
367 data
->WaveHandle
.Out
= 0;
370 device
->ExtraData
= NULL
;
373 static ALCboolean
WinMMResetPlayback(ALCdevice
*device
)
375 WinMMData
*data
= (WinMMData
*)device
->ExtraData
;
377 device
->UpdateSize
= (ALuint
)((ALuint64
)device
->UpdateSize
*
378 data
->Format
.nSamplesPerSec
/
380 device
->UpdateSize
= (device
->UpdateSize
*device
->NumUpdates
+ 3) / 4;
381 device
->NumUpdates
= 4;
382 device
->Frequency
= data
->Format
.nSamplesPerSec
;
384 if(data
->Format
.wFormatTag
== WAVE_FORMAT_IEEE_FLOAT
)
386 if(data
->Format
.wBitsPerSample
== 32)
387 device
->FmtType
= DevFmtFloat
;
390 ERR("Unhandled IEEE float sample depth: %d\n", data
->Format
.wBitsPerSample
);
394 else if(data
->Format
.wFormatTag
== WAVE_FORMAT_PCM
)
396 if(data
->Format
.wBitsPerSample
== 16)
397 device
->FmtType
= DevFmtShort
;
398 else if(data
->Format
.wBitsPerSample
== 8)
399 device
->FmtType
= DevFmtUByte
;
402 ERR("Unhandled PCM sample depth: %d\n", data
->Format
.wBitsPerSample
);
408 ERR("Unhandled format tag: 0x%04x\n", data
->Format
.wFormatTag
);
412 if(data
->Format
.nChannels
== 2)
413 device
->FmtChans
= DevFmtStereo
;
414 else if(data
->Format
.nChannels
== 1)
415 device
->FmtChans
= DevFmtMono
;
418 ERR("Unhandled channel count: %d\n", data
->Format
.nChannels
);
421 SetDefaultWFXChannelOrder(device
);
426 static ALCboolean
WinMMStartPlayback(ALCdevice
*device
)
428 WinMMData
*data
= (WinMMData
*)device
->ExtraData
;
433 data
->WaveThread
= CreateThread(NULL
, 0, (LPTHREAD_START_ROUTINE
)PlaybackThreadProc
, (LPVOID
)device
, 0, &data
->WaveThreadID
);
434 if(data
->WaveThread
== NULL
)
437 data
->WaveBuffersCommitted
= 0;
440 BufferSize
= device
->UpdateSize
*device
->NumUpdates
/ 4;
441 BufferSize
*= FrameSizeFromDevFmt(device
->FmtChans
, device
->FmtType
);
443 BufferData
= calloc(4, BufferSize
);
446 memset(&data
->WaveBuffer
[i
], 0, sizeof(WAVEHDR
));
447 data
->WaveBuffer
[i
].dwBufferLength
= BufferSize
;
448 data
->WaveBuffer
[i
].lpData
= ((i
==0) ? (LPSTR
)BufferData
:
449 (data
->WaveBuffer
[i
-1].lpData
+
450 data
->WaveBuffer
[i
-1].dwBufferLength
));
451 waveOutPrepareHeader(data
->WaveHandle
.Out
, &data
->WaveBuffer
[i
], sizeof(WAVEHDR
));
452 waveOutWrite(data
->WaveHandle
.Out
, &data
->WaveBuffer
[i
], sizeof(WAVEHDR
));
453 InterlockedIncrement(&data
->WaveBuffersCommitted
);
459 static void WinMMStopPlayback(ALCdevice
*device
)
461 WinMMData
*data
= (WinMMData
*)device
->ExtraData
;
465 if(data
->WaveThread
== NULL
)
468 // Set flag to stop processing headers
469 data
->killNow
= AL_TRUE
;
471 // Wait for signal that Wave Thread has been destroyed
472 WaitForSingleObjectEx(data
->WaveThreadEvent
, 5000, FALSE
);
474 CloseHandle(data
->WaveThread
);
475 data
->WaveThread
= 0;
477 data
->killNow
= AL_FALSE
;
479 // Release the wave buffers
482 waveOutUnprepareHeader(data
->WaveHandle
.Out
, &data
->WaveBuffer
[i
], sizeof(WAVEHDR
));
483 if(i
== 0) buffer
= data
->WaveBuffer
[i
].lpData
;
484 data
->WaveBuffer
[i
].lpData
= NULL
;
490 static ALCenum
WinMMOpenCapture(ALCdevice
*Device
, const ALCchar
*deviceName
)
492 ALbyte
*BufferData
= NULL
;
493 DWORD CapturedDataSize
;
494 WinMMData
*data
= NULL
;
500 if(!CaptureDeviceList
)
501 ProbeCaptureDevices();
503 // Find the Device ID matching the deviceName if valid
504 for(i
= 0;i
< NumCaptureDevices
;i
++)
506 if(CaptureDeviceList
[i
] &&
507 (!deviceName
|| strcmp(deviceName
, CaptureDeviceList
[i
]) == 0))
513 if(i
== NumCaptureDevices
)
514 return ALC_INVALID_VALUE
;
516 switch(Device
->FmtChans
)
527 return ALC_INVALID_ENUM
;
530 switch(Device
->FmtType
)
541 return ALC_INVALID_ENUM
;
544 data
= calloc(1, sizeof(*data
));
546 return ALC_OUT_OF_MEMORY
;
547 Device
->ExtraData
= data
;
549 memset(&data
->Format
, 0, sizeof(WAVEFORMATEX
));
550 data
->Format
.wFormatTag
= ((Device
->FmtType
== DevFmtFloat
) ?
551 WAVE_FORMAT_IEEE_FLOAT
: WAVE_FORMAT_PCM
);
552 data
->Format
.nChannels
= ChannelsFromDevFmt(Device
->FmtChans
);
553 data
->Format
.wBitsPerSample
= BytesFromDevFmt(Device
->FmtType
) * 8;
554 data
->Format
.nBlockAlign
= data
->Format
.wBitsPerSample
*
555 data
->Format
.nChannels
/ 8;
556 data
->Format
.nSamplesPerSec
= Device
->Frequency
;
557 data
->Format
.nAvgBytesPerSec
= data
->Format
.nSamplesPerSec
*
558 data
->Format
.nBlockAlign
;
559 data
->Format
.cbSize
= 0;
561 if((res
=waveInOpen(&data
->WaveHandle
.In
, DeviceID
, &data
->Format
, (DWORD_PTR
)&WaveInProc
, (DWORD_PTR
)Device
, CALLBACK_FUNCTION
)) != MMSYSERR_NOERROR
)
563 ERR("waveInOpen failed: %u\n", res
);
567 data
->WaveThreadEvent
= CreateEvent(NULL
, FALSE
, FALSE
, NULL
);
568 if(data
->WaveThreadEvent
== NULL
)
570 ERR("CreateEvent failed: %lu\n", GetLastError());
574 // Allocate circular memory buffer for the captured audio
575 CapturedDataSize
= Device
->UpdateSize
*Device
->NumUpdates
;
577 // Make sure circular buffer is at least 100ms in size
578 if(CapturedDataSize
< (data
->Format
.nSamplesPerSec
/ 10))
579 CapturedDataSize
= data
->Format
.nSamplesPerSec
/ 10;
581 data
->Ring
= CreateRingBuffer(data
->Format
.nBlockAlign
, CapturedDataSize
);
585 data
->WaveBuffersCommitted
= 0;
587 // Create 4 Buffers of 50ms each
588 BufferSize
= data
->Format
.nAvgBytesPerSec
/ 20;
589 BufferSize
-= (BufferSize
% data
->Format
.nBlockAlign
);
591 BufferData
= calloc(4, BufferSize
);
597 memset(&data
->WaveBuffer
[i
], 0, sizeof(WAVEHDR
));
598 data
->WaveBuffer
[i
].dwBufferLength
= BufferSize
;
599 data
->WaveBuffer
[i
].lpData
= ((i
==0) ? (LPSTR
)BufferData
:
600 (data
->WaveBuffer
[i
-1].lpData
+
601 data
->WaveBuffer
[i
-1].dwBufferLength
));
602 data
->WaveBuffer
[i
].dwFlags
= 0;
603 data
->WaveBuffer
[i
].dwLoops
= 0;
604 waveInPrepareHeader(data
->WaveHandle
.In
, &data
->WaveBuffer
[i
], sizeof(WAVEHDR
));
605 waveInAddBuffer(data
->WaveHandle
.In
, &data
->WaveBuffer
[i
], sizeof(WAVEHDR
));
606 InterlockedIncrement(&data
->WaveBuffersCommitted
);
609 data
->WaveThread
= CreateThread(NULL
, 0, (LPTHREAD_START_ROUTINE
)CaptureThreadProc
, (LPVOID
)Device
, 0, &data
->WaveThreadID
);
610 if (data
->WaveThread
== NULL
)
613 Device
->DeviceName
= strdup(CaptureDeviceList
[DeviceID
]);
618 CloseHandle(data
->WaveThread
);
623 waveInUnprepareHeader(data
->WaveHandle
.In
, &data
->WaveBuffer
[i
], sizeof(WAVEHDR
));
628 DestroyRingBuffer(data
->Ring
);
630 if(data
->WaveThreadEvent
)
631 CloseHandle(data
->WaveThreadEvent
);
633 if(data
->WaveHandle
.In
)
634 waveInClose(data
->WaveHandle
.In
);
637 Device
->ExtraData
= NULL
;
638 return ALC_INVALID_VALUE
;
641 static void WinMMCloseCapture(ALCdevice
*Device
)
643 WinMMData
*data
= (WinMMData
*)Device
->ExtraData
;
647 /* Tell the processing thread to quit and wait for it to do so. */
648 data
->killNow
= AL_TRUE
;
649 PostThreadMessage(data
->WaveThreadID
, WM_QUIT
, 0, 0);
651 WaitForSingleObjectEx(data
->WaveThreadEvent
, 5000, FALSE
);
653 /* Make sure capture is stopped and all pending buffers are flushed. */
654 waveInReset(data
->WaveHandle
.In
);
656 CloseHandle(data
->WaveThread
);
657 data
->WaveThread
= 0;
659 // Release the wave buffers
662 waveInUnprepareHeader(data
->WaveHandle
.In
, &data
->WaveBuffer
[i
], sizeof(WAVEHDR
));
663 if(i
== 0) buffer
= data
->WaveBuffer
[i
].lpData
;
664 data
->WaveBuffer
[i
].lpData
= NULL
;
668 DestroyRingBuffer(data
->Ring
);
671 // Close the Wave device
672 CloseHandle(data
->WaveThreadEvent
);
673 data
->WaveThreadEvent
= 0;
675 waveInClose(data
->WaveHandle
.In
);
676 data
->WaveHandle
.In
= 0;
679 Device
->ExtraData
= NULL
;
682 static void WinMMStartCapture(ALCdevice
*Device
)
684 WinMMData
*data
= (WinMMData
*)Device
->ExtraData
;
685 waveInStart(data
->WaveHandle
.In
);
688 static void WinMMStopCapture(ALCdevice
*Device
)
690 WinMMData
*data
= (WinMMData
*)Device
->ExtraData
;
691 waveInStop(data
->WaveHandle
.In
);
694 static ALCenum
WinMMCaptureSamples(ALCdevice
*Device
, ALCvoid
*Buffer
, ALCuint Samples
)
696 WinMMData
*data
= (WinMMData
*)Device
->ExtraData
;
697 ReadRingBuffer(data
->Ring
, Buffer
, Samples
);
701 static ALCuint
WinMMAvailableSamples(ALCdevice
*Device
)
703 WinMMData
*data
= (WinMMData
*)Device
->ExtraData
;
704 return RingBufferSize(data
->Ring
);
708 static const BackendFuncs WinMMFuncs
= {
719 WinMMAvailableSamples
,
720 ALCdevice_LockDefault
,
721 ALCdevice_UnlockDefault
,
722 ALCdevice_GetLatencyDefault
725 ALCboolean
alcWinMMInit(BackendFuncs
*FuncList
)
727 *FuncList
= WinMMFuncs
;
731 void alcWinMMDeinit()
735 for(i
= 0;i
< NumPlaybackDevices
;i
++)
736 free(PlaybackDeviceList
[i
]);
737 free(PlaybackDeviceList
);
738 PlaybackDeviceList
= NULL
;
740 NumPlaybackDevices
= 0;
743 for(i
= 0;i
< NumCaptureDevices
;i
++)
744 free(CaptureDeviceList
[i
]);
745 free(CaptureDeviceList
);
746 CaptureDeviceList
= NULL
;
748 NumCaptureDevices
= 0;
751 void alcWinMMProbe(enum DevProbe type
)
757 case ALL_DEVICE_PROBE
:
758 ProbePlaybackDevices();
759 for(i
= 0;i
< NumPlaybackDevices
;i
++)
761 if(PlaybackDeviceList
[i
])
762 AppendAllDevicesList(PlaybackDeviceList
[i
]);
766 case CAPTURE_DEVICE_PROBE
:
767 ProbeCaptureDevices();
768 for(i
= 0;i
< NumCaptureDevices
;i
++)
770 if(CaptureDeviceList
[i
])
771 AppendCaptureDeviceList(CaptureDeviceList
[i
]);