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
37 volatile ALboolean bWaveShutdown
;
38 HANDLE hWaveThreadEvent
;
41 LONG lWaveBuffersCommitted
;
42 WAVEHDR WaveBuffer
[4];
55 static const ALCchar woDefault
[] = "WaveOut Default";
57 static ALCchar
**PlaybackDeviceList
;
58 static ALuint NumPlaybackDevices
;
59 static ALCchar
**CaptureDeviceList
;
60 static ALuint NumCaptureDevices
;
63 static void ProbePlaybackDevices(void)
67 for(i
= 0;i
< NumPlaybackDevices
;i
++)
68 free(PlaybackDeviceList
[i
]);
70 NumPlaybackDevices
= waveOutGetNumDevs();
71 PlaybackDeviceList
= realloc(PlaybackDeviceList
, sizeof(ALCchar
*) * NumPlaybackDevices
);
72 for(i
= 0;i
< NumPlaybackDevices
;i
++)
76 PlaybackDeviceList
[i
] = NULL
;
77 if(waveOutGetDevCaps(i
, &WaveCaps
, sizeof(WaveCaps
)) == MMSYSERR_NOERROR
)
85 snprintf(name
, sizeof(name
), "%s", WaveCaps
.szPname
);
87 snprintf(name
, sizeof(name
), "%s #%d", WaveCaps
.szPname
, count
+1);
92 if(strcmp(name
, PlaybackDeviceList
[j
]) == 0)
97 PlaybackDeviceList
[i
] = strdup(name
);
102 static void ProbeCaptureDevices(void)
106 for(i
= 0;i
< NumCaptureDevices
;i
++)
107 free(CaptureDeviceList
[i
]);
109 NumCaptureDevices
= waveInGetNumDevs();
110 CaptureDeviceList
= realloc(CaptureDeviceList
, sizeof(ALCchar
*) * NumCaptureDevices
);
111 for(i
= 0;i
< NumCaptureDevices
;i
++)
113 WAVEINCAPS WaveInCaps
;
115 CaptureDeviceList
[i
] = NULL
;
116 if(waveInGetDevCaps(i
, &WaveInCaps
, sizeof(WAVEINCAPS
)) == MMSYSERR_NOERROR
)
124 snprintf(name
, sizeof(name
), "%s", WaveInCaps
.szPname
);
126 snprintf(name
, sizeof(name
), "%s #%d", WaveInCaps
.szPname
, count
+1);
131 if(strcmp(name
, CaptureDeviceList
[j
]) == 0)
136 CaptureDeviceList
[i
] = strdup(name
);
145 Posts a message to 'PlaybackThreadProc' everytime a WaveOut Buffer is completed and
146 returns to the application (for more data)
148 static void CALLBACK
WaveOutProc(HWAVEOUT hDevice
,UINT uMsg
,DWORD_PTR dwInstance
,DWORD_PTR dwParam1
,DWORD_PTR dwParam2
)
150 ALCdevice
*pDevice
= (ALCdevice
*)dwInstance
;
151 WinMMData
*pData
= pDevice
->ExtraData
;
159 // Decrement number of buffers in use
160 InterlockedDecrement(&pData
->lWaveBuffersCommitted
);
162 if(pData
->bWaveShutdown
== AL_FALSE
)
164 // Notify Wave Processor Thread that a Wave Header has returned
165 PostThreadMessage(pData
->ulWaveThreadID
, uMsg
, 0, dwParam1
);
169 if(pData
->lWaveBuffersCommitted
== 0)
171 // Post 'Quit' Message to WaveOut Processor Thread
172 PostThreadMessage(pData
->ulWaveThreadID
, WM_QUIT
, 0, 0);
180 Used by "MMSYSTEM" Device. Called when a WaveOut buffer has used up its
183 static DWORD WINAPI
PlaybackThreadProc(LPVOID lpParameter
)
185 ALCdevice
*pDevice
= (ALCdevice
*)lpParameter
;
186 WinMMData
*pData
= pDevice
->ExtraData
;
191 FrameSize
= FrameSizeFromDevFmt(pDevice
->FmtChans
, pDevice
->FmtType
);
195 while(GetMessage(&msg
, NULL
, 0, 0))
197 if(msg
.message
!= WOM_DONE
|| pData
->bWaveShutdown
)
200 pWaveHdr
= ((LPWAVEHDR
)msg
.lParam
);
202 aluMixData(pDevice
, pWaveHdr
->lpData
, pWaveHdr
->dwBufferLength
/FrameSize
);
204 // Send buffer back to play more data
205 waveOutWrite(pData
->hWaveHandle
.Out
, pWaveHdr
, sizeof(WAVEHDR
));
206 InterlockedIncrement(&pData
->lWaveBuffersCommitted
);
209 // Signal Wave Thread completed event
210 if(pData
->hWaveThreadEvent
)
211 SetEvent(pData
->hWaveThreadEvent
);
221 Posts a message to 'CaptureThreadProc' everytime a WaveIn Buffer is completed and
222 returns to the application (with more data)
224 static void CALLBACK
WaveInProc(HWAVEIN hDevice
,UINT uMsg
,DWORD_PTR dwInstance
,DWORD_PTR dwParam1
,DWORD_PTR dwParam2
)
226 ALCdevice
*pDevice
= (ALCdevice
*)dwInstance
;
227 WinMMData
*pData
= pDevice
->ExtraData
;
235 // Decrement number of buffers in use
236 InterlockedDecrement(&pData
->lWaveBuffersCommitted
);
238 if(pData
->bWaveShutdown
== AL_FALSE
)
240 // Notify Wave Processor Thread that a Wave Header has returned
241 PostThreadMessage(pData
->ulWaveThreadID
,uMsg
,0,dwParam1
);
245 if(pData
->lWaveBuffersCommitted
== 0)
247 // Post 'Quit' Message to WaveIn Processor Thread
248 PostThreadMessage(pData
->ulWaveThreadID
,WM_QUIT
,0,0);
256 Used by "MMSYSTEM" Device. Called when a WaveIn buffer had been filled with new
259 static DWORD WINAPI
CaptureThreadProc(LPVOID lpParameter
)
261 ALCdevice
*pDevice
= (ALCdevice
*)lpParameter
;
262 WinMMData
*pData
= pDevice
->ExtraData
;
267 FrameSize
= FrameSizeFromDevFmt(pDevice
->FmtChans
, pDevice
->FmtType
);
269 while(GetMessage(&msg
, NULL
, 0, 0))
271 if(msg
.message
!= WIM_DATA
|| pData
->bWaveShutdown
)
274 pWaveHdr
= ((LPWAVEHDR
)msg
.lParam
);
276 WriteRingBuffer(pData
->pRing
, (ALubyte
*)pWaveHdr
->lpData
,
277 pWaveHdr
->dwBytesRecorded
/FrameSize
);
279 // Send buffer back to capture more data
280 waveInAddBuffer(pData
->hWaveHandle
.In
,pWaveHdr
,sizeof(WAVEHDR
));
281 InterlockedIncrement(&pData
->lWaveBuffersCommitted
);
284 // Signal Wave Thread completed event
285 if(pData
->hWaveThreadEvent
)
286 SetEvent(pData
->hWaveThreadEvent
);
294 static ALCenum
WinMMOpenPlayback(ALCdevice
*pDevice
, const ALCchar
*deviceName
)
296 WAVEFORMATEX wfexFormat
;
297 WinMMData
*pData
= NULL
;
302 // Find the Device ID matching the deviceName if valid
303 if(!deviceName
|| strcmp(deviceName
, woDefault
) == 0)
304 lDeviceID
= WAVE_MAPPER
;
307 if(!PlaybackDeviceList
)
308 ProbePlaybackDevices();
310 for(i
= 0;i
< NumPlaybackDevices
;i
++)
312 if(PlaybackDeviceList
[i
] &&
313 strcmp(deviceName
, PlaybackDeviceList
[i
]) == 0)
319 if(i
== NumPlaybackDevices
)
320 return ALC_INVALID_VALUE
;
323 pData
= calloc(1, sizeof(*pData
));
325 return ALC_OUT_OF_MEMORY
;
326 pDevice
->ExtraData
= pData
;
328 if(pDevice
->FmtChans
!= DevFmtMono
)
330 if((pDevice
->Flags
&DEVICE_CHANNELS_REQUEST
) &&
331 pDevice
->FmtChans
!= DevFmtStereo
)
333 ERR("Failed to set %s, got Stereo instead\n", DevFmtChannelsString(pDevice
->FmtChans
));
334 pDevice
->Flags
&= ~DEVICE_CHANNELS_REQUEST
;
336 pDevice
->FmtChans
= DevFmtStereo
;
338 switch(pDevice
->FmtType
)
341 pDevice
->FmtType
= DevFmtUByte
;
345 pDevice
->FmtType
= DevFmtShort
;
352 memset(&wfexFormat
, 0, sizeof(WAVEFORMATEX
));
353 wfexFormat
.wFormatTag
= WAVE_FORMAT_PCM
;
354 wfexFormat
.nChannels
= ChannelsFromDevFmt(pDevice
->FmtChans
);
355 wfexFormat
.wBitsPerSample
= BytesFromDevFmt(pDevice
->FmtType
) * 8;
356 wfexFormat
.nBlockAlign
= wfexFormat
.wBitsPerSample
*
357 wfexFormat
.nChannels
/ 8;
358 wfexFormat
.nSamplesPerSec
= pDevice
->Frequency
;
359 wfexFormat
.nAvgBytesPerSec
= wfexFormat
.nSamplesPerSec
*
360 wfexFormat
.nBlockAlign
;
361 wfexFormat
.cbSize
= 0;
363 if((res
=waveOutOpen(&pData
->hWaveHandle
.Out
, lDeviceID
, &wfexFormat
, (DWORD_PTR
)&WaveOutProc
, (DWORD_PTR
)pDevice
, CALLBACK_FUNCTION
)) != MMSYSERR_NOERROR
)
365 ERR("waveOutOpen failed: %u\n", res
);
369 pData
->hWaveThreadEvent
= CreateEvent(NULL
, FALSE
, FALSE
, NULL
);
370 if(pData
->hWaveThreadEvent
== NULL
)
372 ERR("CreateEvent failed: %lu\n", GetLastError());
376 pData
->Frequency
= pDevice
->Frequency
;
378 pDevice
->szDeviceName
= strdup((lDeviceID
==WAVE_MAPPER
) ? woDefault
:
379 PlaybackDeviceList
[lDeviceID
]);
383 if(pData
->hWaveThreadEvent
)
384 CloseHandle(pData
->hWaveThreadEvent
);
386 if(pData
->hWaveHandle
.Out
)
387 waveOutClose(pData
->hWaveHandle
.Out
);
390 pDevice
->ExtraData
= NULL
;
391 return ALC_INVALID_VALUE
;
394 static void WinMMClosePlayback(ALCdevice
*device
)
396 WinMMData
*pData
= (WinMMData
*)device
->ExtraData
;
398 // Close the Wave device
399 CloseHandle(pData
->hWaveThreadEvent
);
400 pData
->hWaveThreadEvent
= 0;
402 waveOutClose(pData
->hWaveHandle
.Out
);
403 pData
->hWaveHandle
.Out
= 0;
406 device
->ExtraData
= NULL
;
409 static ALCboolean
WinMMResetPlayback(ALCdevice
*device
)
411 WinMMData
*pData
= (WinMMData
*)device
->ExtraData
;
416 pData
->hWaveThread
= CreateThread(NULL
, 0, (LPTHREAD_START_ROUTINE
)PlaybackThreadProc
, (LPVOID
)device
, 0, &pData
->ulWaveThreadID
);
417 if(pData
->hWaveThread
== NULL
)
420 device
->UpdateSize
= (ALuint
)((ALuint64
)device
->UpdateSize
*
421 pData
->Frequency
/ device
->Frequency
);
422 if(device
->Frequency
!= pData
->Frequency
)
424 if((device
->Flags
&DEVICE_FREQUENCY_REQUEST
))
425 ERR("WinMM does not support changing sample rates (wanted %dhz, got %dhz)\n", device
->Frequency
, pData
->Frequency
);
426 device
->Flags
&= ~DEVICE_FREQUENCY_REQUEST
;
427 device
->Frequency
= pData
->Frequency
;
430 SetDefaultWFXChannelOrder(device
);
432 pData
->lWaveBuffersCommitted
= 0;
435 lBufferSize
= device
->UpdateSize
*device
->NumUpdates
/ 4;
436 lBufferSize
*= FrameSizeFromDevFmt(device
->FmtChans
, device
->FmtType
);
438 BufferData
= calloc(4, lBufferSize
);
441 memset(&pData
->WaveBuffer
[i
], 0, sizeof(WAVEHDR
));
442 pData
->WaveBuffer
[i
].dwBufferLength
= lBufferSize
;
443 pData
->WaveBuffer
[i
].lpData
= ((i
==0) ? (LPSTR
)BufferData
:
444 (pData
->WaveBuffer
[i
-1].lpData
+
445 pData
->WaveBuffer
[i
-1].dwBufferLength
));
446 waveOutPrepareHeader(pData
->hWaveHandle
.Out
, &pData
->WaveBuffer
[i
], sizeof(WAVEHDR
));
447 waveOutWrite(pData
->hWaveHandle
.Out
, &pData
->WaveBuffer
[i
], sizeof(WAVEHDR
));
448 InterlockedIncrement(&pData
->lWaveBuffersCommitted
);
454 static void WinMMStopPlayback(ALCdevice
*device
)
456 WinMMData
*pData
= (WinMMData
*)device
->ExtraData
;
460 if(pData
->hWaveThread
== NULL
)
463 // Set flag to stop processing headers
464 pData
->bWaveShutdown
= AL_TRUE
;
466 // Wait for signal that Wave Thread has been destroyed
467 WaitForSingleObjectEx(pData
->hWaveThreadEvent
, 5000, FALSE
);
469 CloseHandle(pData
->hWaveThread
);
470 pData
->hWaveThread
= 0;
472 pData
->bWaveShutdown
= AL_FALSE
;
474 // Release the wave buffers
477 waveOutUnprepareHeader(pData
->hWaveHandle
.Out
, &pData
->WaveBuffer
[i
], sizeof(WAVEHDR
));
478 if(i
== 0) buffer
= pData
->WaveBuffer
[i
].lpData
;
479 pData
->WaveBuffer
[i
].lpData
= NULL
;
485 static ALCenum
WinMMOpenCapture(ALCdevice
*pDevice
, const ALCchar
*deviceName
)
487 WAVEFORMATEX wfexCaptureFormat
;
488 ALbyte
*BufferData
= NULL
;
489 DWORD ulCapturedDataSize
;
490 WinMMData
*pData
= NULL
;
496 if(!CaptureDeviceList
)
497 ProbeCaptureDevices();
499 // Find the Device ID matching the deviceName if valid
502 for(i
= 0;i
< NumCaptureDevices
;i
++)
504 if(CaptureDeviceList
[i
] &&
505 strcmp(deviceName
, CaptureDeviceList
[i
]) == 0)
514 for(i
= 0;i
< NumCaptureDevices
;i
++)
516 if(CaptureDeviceList
[i
])
523 if(i
== NumCaptureDevices
)
524 return ALC_INVALID_VALUE
;
526 pData
= calloc(1, sizeof(*pData
));
528 return ALC_OUT_OF_MEMORY
;
529 pDevice
->ExtraData
= pData
;
531 if((pDevice
->FmtChans
!= DevFmtMono
&& pDevice
->FmtChans
!= DevFmtStereo
) ||
532 (pDevice
->FmtType
!= DevFmtUByte
&& pDevice
->FmtType
!= DevFmtShort
))
535 memset(&wfexCaptureFormat
, 0, sizeof(WAVEFORMATEX
));
536 wfexCaptureFormat
.wFormatTag
= WAVE_FORMAT_PCM
;
537 wfexCaptureFormat
.nChannels
= ChannelsFromDevFmt(pDevice
->FmtChans
);
538 wfexCaptureFormat
.wBitsPerSample
= BytesFromDevFmt(pDevice
->FmtType
) * 8;
539 wfexCaptureFormat
.nBlockAlign
= wfexCaptureFormat
.wBitsPerSample
*
540 wfexCaptureFormat
.nChannels
/ 8;
541 wfexCaptureFormat
.nSamplesPerSec
= pDevice
->Frequency
;
542 wfexCaptureFormat
.nAvgBytesPerSec
= wfexCaptureFormat
.nSamplesPerSec
*
543 wfexCaptureFormat
.nBlockAlign
;
544 wfexCaptureFormat
.cbSize
= 0;
546 if((res
=waveInOpen(&pData
->hWaveHandle
.In
, lDeviceID
, &wfexCaptureFormat
, (DWORD_PTR
)&WaveInProc
, (DWORD_PTR
)pDevice
, CALLBACK_FUNCTION
)) != MMSYSERR_NOERROR
)
548 ERR("waveInOpen failed: %u\n", res
);
552 pData
->hWaveThreadEvent
= CreateEvent(NULL
, FALSE
, FALSE
, NULL
);
553 if(pData
->hWaveThreadEvent
== NULL
)
555 ERR("CreateEvent failed: %lu\n", GetLastError());
559 pData
->Frequency
= pDevice
->Frequency
;
561 // Allocate circular memory buffer for the captured audio
562 ulCapturedDataSize
= pDevice
->UpdateSize
*pDevice
->NumUpdates
;
564 // Make sure circular buffer is at least 100ms in size
565 if(ulCapturedDataSize
< (wfexCaptureFormat
.nSamplesPerSec
/ 10))
566 ulCapturedDataSize
= wfexCaptureFormat
.nSamplesPerSec
/ 10;
568 pData
->pRing
= CreateRingBuffer(wfexCaptureFormat
.nBlockAlign
, ulCapturedDataSize
);
572 pData
->lWaveBuffersCommitted
= 0;
574 // Create 4 Buffers of 50ms each
575 lBufferSize
= wfexCaptureFormat
.nAvgBytesPerSec
/ 20;
576 lBufferSize
-= (lBufferSize
% wfexCaptureFormat
.nBlockAlign
);
578 BufferData
= calloc(4, lBufferSize
);
584 memset(&pData
->WaveBuffer
[i
], 0, sizeof(WAVEHDR
));
585 pData
->WaveBuffer
[i
].dwBufferLength
= lBufferSize
;
586 pData
->WaveBuffer
[i
].lpData
= ((i
==0) ? (LPSTR
)BufferData
:
587 (pData
->WaveBuffer
[i
-1].lpData
+
588 pData
->WaveBuffer
[i
-1].dwBufferLength
));
589 pData
->WaveBuffer
[i
].dwFlags
= 0;
590 pData
->WaveBuffer
[i
].dwLoops
= 0;
591 waveInPrepareHeader(pData
->hWaveHandle
.In
, &pData
->WaveBuffer
[i
], sizeof(WAVEHDR
));
592 waveInAddBuffer(pData
->hWaveHandle
.In
, &pData
->WaveBuffer
[i
], sizeof(WAVEHDR
));
593 InterlockedIncrement(&pData
->lWaveBuffersCommitted
);
596 pData
->hWaveThread
= CreateThread(NULL
, 0, (LPTHREAD_START_ROUTINE
)CaptureThreadProc
, (LPVOID
)pDevice
, 0, &pData
->ulWaveThreadID
);
597 if (pData
->hWaveThread
== NULL
)
600 pDevice
->szDeviceName
= strdup(CaptureDeviceList
[lDeviceID
]);
604 if(pData
->hWaveThread
)
605 CloseHandle(pData
->hWaveThread
);
610 waveInUnprepareHeader(pData
->hWaveHandle
.In
, &pData
->WaveBuffer
[i
], sizeof(WAVEHDR
));
615 DestroyRingBuffer(pData
->pRing
);
617 if(pData
->hWaveThreadEvent
)
618 CloseHandle(pData
->hWaveThreadEvent
);
620 if(pData
->hWaveHandle
.In
)
621 waveInClose(pData
->hWaveHandle
.In
);
624 pDevice
->ExtraData
= NULL
;
625 return ALC_INVALID_VALUE
;
628 static void WinMMCloseCapture(ALCdevice
*pDevice
)
630 WinMMData
*pData
= (WinMMData
*)pDevice
->ExtraData
;
634 // Call waveOutReset to shutdown wave device
635 pData
->bWaveShutdown
= AL_TRUE
;
636 waveInReset(pData
->hWaveHandle
.In
);
638 // Wait for signal that Wave Thread has been destroyed
639 WaitForSingleObjectEx(pData
->hWaveThreadEvent
, 5000, FALSE
);
641 CloseHandle(pData
->hWaveThread
);
642 pData
->hWaveThread
= 0;
644 // Release the wave buffers
647 waveInUnprepareHeader(pData
->hWaveHandle
.In
, &pData
->WaveBuffer
[i
], sizeof(WAVEHDR
));
648 if(i
== 0) buffer
= pData
->WaveBuffer
[i
].lpData
;
649 pData
->WaveBuffer
[i
].lpData
= NULL
;
653 DestroyRingBuffer(pData
->pRing
);
656 // Close the Wave device
657 CloseHandle(pData
->hWaveThreadEvent
);
658 pData
->hWaveThreadEvent
= 0;
660 waveInClose(pData
->hWaveHandle
.In
);
661 pData
->hWaveHandle
.In
= 0;
664 pDevice
->ExtraData
= NULL
;
667 static void WinMMStartCapture(ALCdevice
*pDevice
)
669 WinMMData
*pData
= (WinMMData
*)pDevice
->ExtraData
;
670 waveInStart(pData
->hWaveHandle
.In
);
673 static void WinMMStopCapture(ALCdevice
*pDevice
)
675 WinMMData
*pData
= (WinMMData
*)pDevice
->ExtraData
;
676 waveInStop(pData
->hWaveHandle
.In
);
679 static ALCenum
WinMMCaptureSamples(ALCdevice
*pDevice
, ALCvoid
*pBuffer
, ALCuint lSamples
)
681 WinMMData
*pData
= (WinMMData
*)pDevice
->ExtraData
;
682 ReadRingBuffer(pData
->pRing
, pBuffer
, lSamples
);
686 static ALCuint
WinMMAvailableSamples(ALCdevice
*pDevice
)
688 WinMMData
*pData
= (WinMMData
*)pDevice
->ExtraData
;
689 return RingBufferSize(pData
->pRing
);
693 static const BackendFuncs WinMMFuncs
= {
703 WinMMAvailableSamples
706 ALCboolean
alcWinMMInit(BackendFuncs
*FuncList
)
708 *FuncList
= WinMMFuncs
;
712 void alcWinMMDeinit()
716 for(lLoop
= 0;lLoop
< NumPlaybackDevices
;lLoop
++)
717 free(PlaybackDeviceList
[lLoop
]);
718 free(PlaybackDeviceList
);
719 PlaybackDeviceList
= NULL
;
721 NumPlaybackDevices
= 0;
724 for(lLoop
= 0; lLoop
< NumCaptureDevices
; lLoop
++)
725 free(CaptureDeviceList
[lLoop
]);
726 free(CaptureDeviceList
);
727 CaptureDeviceList
= NULL
;
729 NumCaptureDevices
= 0;
732 void alcWinMMProbe(enum DevProbe type
)
739 ProbePlaybackDevices();
740 if(NumPlaybackDevices
> 0)
741 AppendDeviceList(woDefault
);
744 case ALL_DEVICE_PROBE
:
745 ProbePlaybackDevices();
746 if(NumPlaybackDevices
> 0)
747 AppendAllDeviceList(woDefault
);
748 for(i
= 0;i
< NumPlaybackDevices
;i
++)
750 if(PlaybackDeviceList
[i
])
751 AppendAllDeviceList(PlaybackDeviceList
[i
]);
755 case CAPTURE_DEVICE_PROBE
:
756 ProbeCaptureDevices();
757 for(i
= 0;i
< NumCaptureDevices
;i
++)
759 if(CaptureDeviceList
[i
])
760 AppendCaptureDeviceList(CaptureDeviceList
[i
]);