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];
53 WAVEFORMATEX wfexFormat
;
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
*Device
= (ALCdevice
*)dwInstance
;
153 WinMMData
*pData
= Device
->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
*Device
= (ALCdevice
*)lpParameter
;
174 WinMMData
*pData
= Device
->ExtraData
;
179 FrameSize
= FrameSizeFromDevFmt(Device
->FmtChans
, Device
->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(Device
, 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
*Device
= (ALCdevice
*)dwInstance
;
222 WinMMData
*pData
= Device
->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
*Device
= (ALCdevice
*)lpParameter
;
243 WinMMData
*pData
= Device
->ExtraData
;
248 FrameSize
= FrameSizeFromDevFmt(Device
->FmtChans
, Device
->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
*Device
, const ALCchar
*deviceName
)
281 WinMMData
*pData
= NULL
;
286 if(!PlaybackDeviceList
)
287 ProbePlaybackDevices();
289 // Find the Device ID matching the deviceName if valid
290 for(i
= 0;i
< NumPlaybackDevices
;i
++)
292 if(PlaybackDeviceList
[i
] &&
293 (!deviceName
|| strcmp(deviceName
, PlaybackDeviceList
[i
]) == 0))
299 if(i
== NumPlaybackDevices
)
300 return ALC_INVALID_VALUE
;
302 pData
= calloc(1, sizeof(*pData
));
304 return ALC_OUT_OF_MEMORY
;
305 Device
->ExtraData
= pData
;
308 memset(&pData
->wfexFormat
, 0, sizeof(WAVEFORMATEX
));
309 if(Device
->FmtType
== DevFmtFloat
)
311 pData
->wfexFormat
.wFormatTag
= WAVE_FORMAT_IEEE_FLOAT
;
312 pData
->wfexFormat
.wBitsPerSample
= 32;
316 pData
->wfexFormat
.wFormatTag
= WAVE_FORMAT_PCM
;
317 if(Device
->FmtType
== DevFmtUByte
|| Device
->FmtType
== DevFmtByte
)
318 pData
->wfexFormat
.wBitsPerSample
= 8;
320 pData
->wfexFormat
.wBitsPerSample
= 16;
322 pData
->wfexFormat
.nChannels
= ((Device
->FmtChans
== DevFmtMono
) ? 1 : 2);
323 pData
->wfexFormat
.nBlockAlign
= pData
->wfexFormat
.wBitsPerSample
*
324 pData
->wfexFormat
.nChannels
/ 8;
325 pData
->wfexFormat
.nSamplesPerSec
= Device
->Frequency
;
326 pData
->wfexFormat
.nAvgBytesPerSec
= pData
->wfexFormat
.nSamplesPerSec
*
327 pData
->wfexFormat
.nBlockAlign
;
328 pData
->wfexFormat
.cbSize
= 0;
330 if((res
=waveOutOpen(&pData
->hWaveHandle
.Out
, lDeviceID
, &pData
->wfexFormat
, (DWORD_PTR
)&WaveOutProc
, (DWORD_PTR
)Device
, CALLBACK_FUNCTION
)) != MMSYSERR_NOERROR
)
332 if(Device
->FmtType
== DevFmtFloat
)
334 Device
->FmtType
= DevFmtShort
;
337 ERR("waveOutOpen failed: %u\n", res
);
341 pData
->hWaveThreadEvent
= CreateEvent(NULL
, FALSE
, FALSE
, NULL
);
342 if(pData
->hWaveThreadEvent
== NULL
)
344 ERR("CreateEvent failed: %lu\n", GetLastError());
348 Device
->DeviceName
= strdup(PlaybackDeviceList
[lDeviceID
]);
352 if(pData
->hWaveThreadEvent
)
353 CloseHandle(pData
->hWaveThreadEvent
);
355 if(pData
->hWaveHandle
.Out
)
356 waveOutClose(pData
->hWaveHandle
.Out
);
359 Device
->ExtraData
= NULL
;
360 return ALC_INVALID_VALUE
;
363 static void WinMMClosePlayback(ALCdevice
*device
)
365 WinMMData
*pData
= (WinMMData
*)device
->ExtraData
;
367 // Close the Wave device
368 CloseHandle(pData
->hWaveThreadEvent
);
369 pData
->hWaveThreadEvent
= 0;
371 waveOutClose(pData
->hWaveHandle
.Out
);
372 pData
->hWaveHandle
.Out
= 0;
375 device
->ExtraData
= NULL
;
378 static ALCboolean
WinMMResetPlayback(ALCdevice
*device
)
380 WinMMData
*data
= (WinMMData
*)device
->ExtraData
;
382 device
->UpdateSize
= (ALuint
)((ALuint64
)device
->UpdateSize
*
383 data
->wfexFormat
.nSamplesPerSec
/
385 device
->UpdateSize
= (device
->UpdateSize
*device
->NumUpdates
+ 3) / 4;
386 device
->NumUpdates
= 4;
387 device
->Frequency
= data
->wfexFormat
.nSamplesPerSec
;
389 if(data
->wfexFormat
.wFormatTag
== WAVE_FORMAT_IEEE_FLOAT
)
391 if(data
->wfexFormat
.wBitsPerSample
== 32)
392 device
->FmtType
= DevFmtFloat
;
395 ERR("Unhandled IEEE float sample depth: %d\n", data
->wfexFormat
.wBitsPerSample
);
399 else if(data
->wfexFormat
.wFormatTag
== WAVE_FORMAT_PCM
)
401 if(data
->wfexFormat
.wBitsPerSample
== 16)
402 device
->FmtType
= DevFmtShort
;
403 else if(data
->wfexFormat
.wBitsPerSample
== 8)
404 device
->FmtType
= DevFmtUByte
;
407 ERR("Unhandled PCM sample depth: %d\n", data
->wfexFormat
.wBitsPerSample
);
413 ERR("Unhandled format tag: 0x%04x\n", data
->wfexFormat
.wFormatTag
);
417 if(data
->wfexFormat
.nChannels
== 2)
418 device
->FmtChans
= DevFmtStereo
;
419 else if(data
->wfexFormat
.nChannels
== 1)
420 device
->FmtChans
= DevFmtMono
;
423 ERR("Unhandled channel count: %d\n", data
->wfexFormat
.nChannels
);
426 SetDefaultWFXChannelOrder(device
);
431 static ALCboolean
WinMMStartPlayback(ALCdevice
*device
)
433 WinMMData
*pData
= (WinMMData
*)device
->ExtraData
;
438 pData
->hWaveThread
= CreateThread(NULL
, 0, (LPTHREAD_START_ROUTINE
)PlaybackThreadProc
, (LPVOID
)device
, 0, &pData
->ulWaveThreadID
);
439 if(pData
->hWaveThread
== NULL
)
442 pData
->lWaveBuffersCommitted
= 0;
445 lBufferSize
= device
->UpdateSize
*device
->NumUpdates
/ 4;
446 lBufferSize
*= FrameSizeFromDevFmt(device
->FmtChans
, device
->FmtType
);
448 BufferData
= calloc(4, lBufferSize
);
451 memset(&pData
->WaveBuffer
[i
], 0, sizeof(WAVEHDR
));
452 pData
->WaveBuffer
[i
].dwBufferLength
= lBufferSize
;
453 pData
->WaveBuffer
[i
].lpData
= ((i
==0) ? (LPSTR
)BufferData
:
454 (pData
->WaveBuffer
[i
-1].lpData
+
455 pData
->WaveBuffer
[i
-1].dwBufferLength
));
456 waveOutPrepareHeader(pData
->hWaveHandle
.Out
, &pData
->WaveBuffer
[i
], sizeof(WAVEHDR
));
457 waveOutWrite(pData
->hWaveHandle
.Out
, &pData
->WaveBuffer
[i
], sizeof(WAVEHDR
));
458 InterlockedIncrement(&pData
->lWaveBuffersCommitted
);
464 static void WinMMStopPlayback(ALCdevice
*device
)
466 WinMMData
*pData
= (WinMMData
*)device
->ExtraData
;
470 if(pData
->hWaveThread
== NULL
)
473 // Set flag to stop processing headers
474 pData
->bWaveShutdown
= AL_TRUE
;
476 // Wait for signal that Wave Thread has been destroyed
477 WaitForSingleObjectEx(pData
->hWaveThreadEvent
, 5000, FALSE
);
479 CloseHandle(pData
->hWaveThread
);
480 pData
->hWaveThread
= 0;
482 pData
->bWaveShutdown
= AL_FALSE
;
484 // Release the wave buffers
487 waveOutUnprepareHeader(pData
->hWaveHandle
.Out
, &pData
->WaveBuffer
[i
], sizeof(WAVEHDR
));
488 if(i
== 0) buffer
= pData
->WaveBuffer
[i
].lpData
;
489 pData
->WaveBuffer
[i
].lpData
= NULL
;
495 static ALCenum
WinMMOpenCapture(ALCdevice
*Device
, const ALCchar
*deviceName
)
497 ALbyte
*BufferData
= NULL
;
498 DWORD ulCapturedDataSize
;
499 WinMMData
*pData
= NULL
;
505 if(!CaptureDeviceList
)
506 ProbeCaptureDevices();
508 // Find the Device ID matching the deviceName if valid
509 for(i
= 0;i
< NumCaptureDevices
;i
++)
511 if(CaptureDeviceList
[i
] &&
512 (!deviceName
|| strcmp(deviceName
, CaptureDeviceList
[i
]) == 0))
518 if(i
== NumCaptureDevices
)
519 return ALC_INVALID_VALUE
;
521 switch(Device
->FmtChans
)
532 return ALC_INVALID_ENUM
;
535 switch(Device
->FmtType
)
546 return ALC_INVALID_ENUM
;
549 pData
= calloc(1, sizeof(*pData
));
551 return ALC_OUT_OF_MEMORY
;
552 Device
->ExtraData
= pData
;
554 memset(&pData
->wfexFormat
, 0, sizeof(WAVEFORMATEX
));
555 pData
->wfexFormat
.wFormatTag
= ((Device
->FmtType
== DevFmtFloat
) ?
556 WAVE_FORMAT_IEEE_FLOAT
: WAVE_FORMAT_PCM
);
557 pData
->wfexFormat
.nChannels
= ChannelsFromDevFmt(Device
->FmtChans
);
558 pData
->wfexFormat
.wBitsPerSample
= BytesFromDevFmt(Device
->FmtType
) * 8;
559 pData
->wfexFormat
.nBlockAlign
= pData
->wfexFormat
.wBitsPerSample
*
560 pData
->wfexFormat
.nChannels
/ 8;
561 pData
->wfexFormat
.nSamplesPerSec
= Device
->Frequency
;
562 pData
->wfexFormat
.nAvgBytesPerSec
= pData
->wfexFormat
.nSamplesPerSec
*
563 pData
->wfexFormat
.nBlockAlign
;
564 pData
->wfexFormat
.cbSize
= 0;
566 if((res
=waveInOpen(&pData
->hWaveHandle
.In
, lDeviceID
, &pData
->wfexFormat
, (DWORD_PTR
)&WaveInProc
, (DWORD_PTR
)Device
, CALLBACK_FUNCTION
)) != MMSYSERR_NOERROR
)
568 ERR("waveInOpen failed: %u\n", res
);
572 pData
->hWaveThreadEvent
= CreateEvent(NULL
, FALSE
, FALSE
, NULL
);
573 if(pData
->hWaveThreadEvent
== NULL
)
575 ERR("CreateEvent failed: %lu\n", GetLastError());
579 // Allocate circular memory buffer for the captured audio
580 ulCapturedDataSize
= Device
->UpdateSize
*Device
->NumUpdates
;
582 // Make sure circular buffer is at least 100ms in size
583 if(ulCapturedDataSize
< (pData
->wfexFormat
.nSamplesPerSec
/ 10))
584 ulCapturedDataSize
= pData
->wfexFormat
.nSamplesPerSec
/ 10;
586 pData
->pRing
= CreateRingBuffer(pData
->wfexFormat
.nBlockAlign
, ulCapturedDataSize
);
590 pData
->lWaveBuffersCommitted
= 0;
592 // Create 4 Buffers of 50ms each
593 lBufferSize
= pData
->wfexFormat
.nAvgBytesPerSec
/ 20;
594 lBufferSize
-= (lBufferSize
% pData
->wfexFormat
.nBlockAlign
);
596 BufferData
= calloc(4, lBufferSize
);
602 memset(&pData
->WaveBuffer
[i
], 0, sizeof(WAVEHDR
));
603 pData
->WaveBuffer
[i
].dwBufferLength
= lBufferSize
;
604 pData
->WaveBuffer
[i
].lpData
= ((i
==0) ? (LPSTR
)BufferData
:
605 (pData
->WaveBuffer
[i
-1].lpData
+
606 pData
->WaveBuffer
[i
-1].dwBufferLength
));
607 pData
->WaveBuffer
[i
].dwFlags
= 0;
608 pData
->WaveBuffer
[i
].dwLoops
= 0;
609 waveInPrepareHeader(pData
->hWaveHandle
.In
, &pData
->WaveBuffer
[i
], sizeof(WAVEHDR
));
610 waveInAddBuffer(pData
->hWaveHandle
.In
, &pData
->WaveBuffer
[i
], sizeof(WAVEHDR
));
611 InterlockedIncrement(&pData
->lWaveBuffersCommitted
);
614 pData
->hWaveThread
= CreateThread(NULL
, 0, (LPTHREAD_START_ROUTINE
)CaptureThreadProc
, (LPVOID
)Device
, 0, &pData
->ulWaveThreadID
);
615 if (pData
->hWaveThread
== NULL
)
618 Device
->DeviceName
= strdup(CaptureDeviceList
[lDeviceID
]);
622 if(pData
->hWaveThread
)
623 CloseHandle(pData
->hWaveThread
);
628 waveInUnprepareHeader(pData
->hWaveHandle
.In
, &pData
->WaveBuffer
[i
], sizeof(WAVEHDR
));
633 DestroyRingBuffer(pData
->pRing
);
635 if(pData
->hWaveThreadEvent
)
636 CloseHandle(pData
->hWaveThreadEvent
);
638 if(pData
->hWaveHandle
.In
)
639 waveInClose(pData
->hWaveHandle
.In
);
642 Device
->ExtraData
= NULL
;
643 return ALC_INVALID_VALUE
;
646 static void WinMMCloseCapture(ALCdevice
*Device
)
648 WinMMData
*pData
= (WinMMData
*)Device
->ExtraData
;
652 /* Tell the processing thread to quit and wait for it to do so. */
653 pData
->bWaveShutdown
= AL_TRUE
;
654 PostThreadMessage(pData
->ulWaveThreadID
, WM_QUIT
, 0, 0);
656 WaitForSingleObjectEx(pData
->hWaveThreadEvent
, 5000, FALSE
);
658 /* Make sure capture is stopped and all pending buffers are flushed. */
659 waveInReset(pData
->hWaveHandle
.In
);
661 CloseHandle(pData
->hWaveThread
);
662 pData
->hWaveThread
= 0;
664 // Release the wave buffers
667 waveInUnprepareHeader(pData
->hWaveHandle
.In
, &pData
->WaveBuffer
[i
], sizeof(WAVEHDR
));
668 if(i
== 0) buffer
= pData
->WaveBuffer
[i
].lpData
;
669 pData
->WaveBuffer
[i
].lpData
= NULL
;
673 DestroyRingBuffer(pData
->pRing
);
676 // Close the Wave device
677 CloseHandle(pData
->hWaveThreadEvent
);
678 pData
->hWaveThreadEvent
= 0;
680 waveInClose(pData
->hWaveHandle
.In
);
681 pData
->hWaveHandle
.In
= 0;
684 Device
->ExtraData
= NULL
;
687 static void WinMMStartCapture(ALCdevice
*Device
)
689 WinMMData
*pData
= (WinMMData
*)Device
->ExtraData
;
690 waveInStart(pData
->hWaveHandle
.In
);
693 static void WinMMStopCapture(ALCdevice
*Device
)
695 WinMMData
*pData
= (WinMMData
*)Device
->ExtraData
;
696 waveInStop(pData
->hWaveHandle
.In
);
699 static ALCenum
WinMMCaptureSamples(ALCdevice
*Device
, ALCvoid
*Buffer
, ALCuint Samples
)
701 WinMMData
*pData
= (WinMMData
*)Device
->ExtraData
;
702 ReadRingBuffer(pData
->pRing
, Buffer
, Samples
);
706 static ALCuint
WinMMAvailableSamples(ALCdevice
*Device
)
708 WinMMData
*pData
= (WinMMData
*)Device
->ExtraData
;
709 return RingBufferSize(pData
->pRing
);
713 static const BackendFuncs WinMMFuncs
= {
724 WinMMAvailableSamples
727 ALCboolean
alcWinMMInit(BackendFuncs
*FuncList
)
729 *FuncList
= WinMMFuncs
;
733 void alcWinMMDeinit()
737 for(lLoop
= 0;lLoop
< NumPlaybackDevices
;lLoop
++)
738 free(PlaybackDeviceList
[lLoop
]);
739 free(PlaybackDeviceList
);
740 PlaybackDeviceList
= NULL
;
742 NumPlaybackDevices
= 0;
745 for(lLoop
= 0; lLoop
< NumCaptureDevices
; lLoop
++)
746 free(CaptureDeviceList
[lLoop
]);
747 free(CaptureDeviceList
);
748 CaptureDeviceList
= NULL
;
750 NumCaptureDevices
= 0;
753 void alcWinMMProbe(enum DevProbe type
)
759 case ALL_DEVICE_PROBE
:
760 ProbePlaybackDevices();
761 for(i
= 0;i
< NumPlaybackDevices
;i
++)
763 if(PlaybackDeviceList
[i
])
764 AppendAllDeviceList(PlaybackDeviceList
[i
]);
768 case CAPTURE_DEVICE_PROBE
:
769 ProbeCaptureDevices();
770 for(i
= 0;i
< NumCaptureDevices
;i
++)
772 if(CaptureDeviceList
[i
])
773 AppendCaptureDeviceList(CaptureDeviceList
[i
]);