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 *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
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
pDevice->ExtraData = pData;
308
memset(&pData->wfexFormat, 0, sizeof(WAVEFORMATEX));
309
if(pDevice->FmtType == DevFmtFloat)
311
pData->wfexFormat.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
312
pData->wfexFormat.wBitsPerSample = 32;
316
pData->wfexFormat.wFormatTag = WAVE_FORMAT_PCM;
317
if(pDevice->FmtType == DevFmtUByte || pDevice->FmtType == DevFmtByte)
318
pData->wfexFormat.wBitsPerSample = 8;
320
pData->wfexFormat.wBitsPerSample = 16;
322
pData->wfexFormat.nChannels = ((pDevice->FmtChans == DevFmtMono) ? 1 : 2);
323
pData->wfexFormat.nBlockAlign = pData->wfexFormat.wBitsPerSample *
324
pData->wfexFormat.nChannels / 8;
325
pData->wfexFormat.nSamplesPerSec = pDevice->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)pDevice, CALLBACK_FUNCTION)) != MMSYSERR_NOERROR)
332
if(pDevice->FmtType == DevFmtFloat)
334
pDevice->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
pDevice->szDeviceName = strdup(PlaybackDeviceList[lDeviceID]);
352
if(pData->hWaveThreadEvent)
353
CloseHandle(pData->hWaveThreadEvent);
355
if(pData->hWaveHandle.Out)
356
waveOutClose(pData->hWaveHandle.Out);
359
pDevice->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 *pDevice, 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(pDevice->FmtChans)
532
return ALC_INVALID_ENUM;
535
switch(pDevice->FmtType)
546
return ALC_INVALID_ENUM;
549
pData = calloc(1, sizeof(*pData));
551
return ALC_OUT_OF_MEMORY;
552
pDevice->ExtraData = pData;
554
memset(&pData->wfexFormat, 0, sizeof(WAVEFORMATEX));
555
pData->wfexFormat.wFormatTag = ((pDevice->FmtType == DevFmtFloat) ?
556
WAVE_FORMAT_IEEE_FLOAT : WAVE_FORMAT_PCM);
557
pData->wfexFormat.nChannels = ChannelsFromDevFmt(pDevice->FmtChans);
558
pData->wfexFormat.wBitsPerSample = BytesFromDevFmt(pDevice->FmtType) * 8;
559
pData->wfexFormat.nBlockAlign = pData->wfexFormat.wBitsPerSample *
560
pData->wfexFormat.nChannels / 8;
561
pData->wfexFormat.nSamplesPerSec = pDevice->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)pDevice, 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 = pDevice->UpdateSize*pDevice->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)pDevice, 0, &pData->ulWaveThreadID);
615
if (pData->hWaveThread == NULL)
618
pDevice->szDeviceName = 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
pDevice->ExtraData = NULL;
643
return ALC_INVALID_VALUE;
646
static void WinMMCloseCapture(ALCdevice *pDevice)
648
WinMMData *pData = (WinMMData*)pDevice->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
pDevice->ExtraData = NULL;
687
static void WinMMStartCapture(ALCdevice *pDevice)
689
WinMMData *pData = (WinMMData*)pDevice->ExtraData;
690
waveInStart(pData->hWaveHandle.In);
693
static void WinMMStopCapture(ALCdevice *pDevice)
695
WinMMData *pData = (WinMMData*)pDevice->ExtraData;
696
waveInStop(pData->hWaveHandle.In);
699
static ALCenum WinMMCaptureSamples(ALCdevice *pDevice, ALCvoid *pBuffer, ALCuint lSamples)
701
WinMMData *pData = (WinMMData*)pDevice->ExtraData;
702
ReadRingBuffer(pData->pRing, pBuffer, lSamples);
706
static ALCuint WinMMAvailableSamples(ALCdevice *pDevice)
708
WinMMData *pData = (WinMMData*)pDevice->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]);