annotate Alc/backends/winmm.c @ 0:f9476ff7637e

initial forking of open-al to create multiple listeners
author Robert McIntyre <rlm@mit.edu>
date Tue, 25 Oct 2011 13:02:31 -0700
parents
children
rev   line source
rlm@0 1 /**
rlm@0 2 * OpenAL cross platform audio library
rlm@0 3 * Copyright (C) 1999-2007 by authors.
rlm@0 4 * This library is free software; you can redistribute it and/or
rlm@0 5 * modify it under the terms of the GNU Library General Public
rlm@0 6 * License as published by the Free Software Foundation; either
rlm@0 7 * version 2 of the License, or (at your option) any later version.
rlm@0 8 *
rlm@0 9 * This library is distributed in the hope that it will be useful,
rlm@0 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
rlm@0 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
rlm@0 12 * Library General Public License for more details.
rlm@0 13 *
rlm@0 14 * You should have received a copy of the GNU Library General Public
rlm@0 15 * License along with this library; if not, write to the
rlm@0 16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
rlm@0 17 * Boston, MA 02111-1307, USA.
rlm@0 18 * Or go to http://www.gnu.org/copyleft/lgpl.html
rlm@0 19 */
rlm@0 20
rlm@0 21 #include "config.h"
rlm@0 22
rlm@0 23 #define _WIN32_WINNT 0x0500
rlm@0 24 #include <stdlib.h>
rlm@0 25 #include <stdio.h>
rlm@0 26 #include <memory.h>
rlm@0 27
rlm@0 28 #include <windows.h>
rlm@0 29 #include <mmsystem.h>
rlm@0 30
rlm@0 31 #include "alMain.h"
rlm@0 32 #include "AL/al.h"
rlm@0 33 #include "AL/alc.h"
rlm@0 34
rlm@0 35
rlm@0 36 typedef struct {
rlm@0 37 // MMSYSTEM Device
rlm@0 38 volatile ALboolean bWaveShutdown;
rlm@0 39 HANDLE hWaveThreadEvent;
rlm@0 40 HANDLE hWaveThread;
rlm@0 41 DWORD ulWaveThreadID;
rlm@0 42 LONG lWaveBuffersCommitted;
rlm@0 43 WAVEHDR WaveBuffer[4];
rlm@0 44
rlm@0 45 union {
rlm@0 46 HWAVEIN In;
rlm@0 47 HWAVEOUT Out;
rlm@0 48 } hWaveHandle;
rlm@0 49
rlm@0 50 ALuint Frequency;
rlm@0 51
rlm@0 52 RingBuffer *pRing;
rlm@0 53 } WinMMData;
rlm@0 54
rlm@0 55
rlm@0 56 static const ALCchar woDefault[] = "WaveOut Default";
rlm@0 57
rlm@0 58 static ALCchar **PlaybackDeviceList;
rlm@0 59 static ALuint NumPlaybackDevices;
rlm@0 60 static ALCchar **CaptureDeviceList;
rlm@0 61 static ALuint NumCaptureDevices;
rlm@0 62
rlm@0 63
rlm@0 64 static void ProbePlaybackDevices(void)
rlm@0 65 {
rlm@0 66 ALuint i;
rlm@0 67
rlm@0 68 for(i = 0;i < NumPlaybackDevices;i++)
rlm@0 69 free(PlaybackDeviceList[i]);
rlm@0 70
rlm@0 71 NumPlaybackDevices = waveOutGetNumDevs();
rlm@0 72 PlaybackDeviceList = realloc(PlaybackDeviceList, sizeof(ALCchar*) * NumPlaybackDevices);
rlm@0 73 for(i = 0;i < NumPlaybackDevices;i++)
rlm@0 74 {
rlm@0 75 WAVEOUTCAPS WaveCaps;
rlm@0 76
rlm@0 77 PlaybackDeviceList[i] = NULL;
rlm@0 78 if(waveOutGetDevCaps(i, &WaveCaps, sizeof(WaveCaps)) == MMSYSERR_NOERROR)
rlm@0 79 {
rlm@0 80 char name[1024];
rlm@0 81 ALuint count, j;
rlm@0 82
rlm@0 83 count = 0;
rlm@0 84 do {
rlm@0 85 if(count == 0)
rlm@0 86 snprintf(name, sizeof(name), "%s", WaveCaps.szPname);
rlm@0 87 else
rlm@0 88 snprintf(name, sizeof(name), "%s #%d", WaveCaps.szPname, count+1);
rlm@0 89 count++;
rlm@0 90
rlm@0 91 for(j = 0;j < i;j++)
rlm@0 92 {
rlm@0 93 if(strcmp(name, PlaybackDeviceList[j]) == 0)
rlm@0 94 break;
rlm@0 95 }
rlm@0 96 } while(j != i);
rlm@0 97
rlm@0 98 PlaybackDeviceList[i] = strdup(name);
rlm@0 99 }
rlm@0 100 }
rlm@0 101 }
rlm@0 102
rlm@0 103 static void ProbeCaptureDevices(void)
rlm@0 104 {
rlm@0 105 ALuint i;
rlm@0 106
rlm@0 107 for(i = 0;i < NumCaptureDevices;i++)
rlm@0 108 free(CaptureDeviceList[i]);
rlm@0 109
rlm@0 110 NumCaptureDevices = waveInGetNumDevs();
rlm@0 111 CaptureDeviceList = realloc(CaptureDeviceList, sizeof(ALCchar*) * NumCaptureDevices);
rlm@0 112 for(i = 0;i < NumCaptureDevices;i++)
rlm@0 113 {
rlm@0 114 WAVEINCAPS WaveInCaps;
rlm@0 115
rlm@0 116 CaptureDeviceList[i] = NULL;
rlm@0 117 if(waveInGetDevCaps(i, &WaveInCaps, sizeof(WAVEINCAPS)) == MMSYSERR_NOERROR)
rlm@0 118 {
rlm@0 119 char name[1024];
rlm@0 120 ALuint count, j;
rlm@0 121
rlm@0 122 count = 0;
rlm@0 123 do {
rlm@0 124 if(count == 0)
rlm@0 125 snprintf(name, sizeof(name), "%s", WaveInCaps.szPname);
rlm@0 126 else
rlm@0 127 snprintf(name, sizeof(name), "%s #%d", WaveInCaps.szPname, count+1);
rlm@0 128 count++;
rlm@0 129
rlm@0 130 for(j = 0;j < i;j++)
rlm@0 131 {
rlm@0 132 if(strcmp(name, CaptureDeviceList[j]) == 0)
rlm@0 133 break;
rlm@0 134 }
rlm@0 135 } while(j != i);
rlm@0 136
rlm@0 137 CaptureDeviceList[i] = strdup(name);
rlm@0 138 }
rlm@0 139 }
rlm@0 140 }
rlm@0 141
rlm@0 142
rlm@0 143 /*
rlm@0 144 WaveOutProc
rlm@0 145
rlm@0 146 Posts a message to 'PlaybackThreadProc' everytime a WaveOut Buffer is completed and
rlm@0 147 returns to the application (for more data)
rlm@0 148 */
rlm@0 149 static void CALLBACK WaveOutProc(HWAVEOUT hDevice,UINT uMsg,DWORD_PTR dwInstance,DWORD_PTR dwParam1,DWORD_PTR dwParam2)
rlm@0 150 {
rlm@0 151 ALCdevice *pDevice = (ALCdevice*)dwInstance;
rlm@0 152 WinMMData *pData = pDevice->ExtraData;
rlm@0 153
rlm@0 154 (void)hDevice;
rlm@0 155 (void)dwParam2;
rlm@0 156
rlm@0 157 if(uMsg != WOM_DONE)
rlm@0 158 return;
rlm@0 159
rlm@0 160 // Decrement number of buffers in use
rlm@0 161 InterlockedDecrement(&pData->lWaveBuffersCommitted);
rlm@0 162
rlm@0 163 if(pData->bWaveShutdown == AL_FALSE)
rlm@0 164 {
rlm@0 165 // Notify Wave Processor Thread that a Wave Header has returned
rlm@0 166 PostThreadMessage(pData->ulWaveThreadID, uMsg, 0, dwParam1);
rlm@0 167 }
rlm@0 168 else
rlm@0 169 {
rlm@0 170 if(pData->lWaveBuffersCommitted == 0)
rlm@0 171 {
rlm@0 172 // Post 'Quit' Message to WaveOut Processor Thread
rlm@0 173 PostThreadMessage(pData->ulWaveThreadID, WM_QUIT, 0, 0);
rlm@0 174 }
rlm@0 175 }
rlm@0 176 }
rlm@0 177
rlm@0 178 /*
rlm@0 179 PlaybackThreadProc
rlm@0 180
rlm@0 181 Used by "MMSYSTEM" Device. Called when a WaveOut buffer has used up its
rlm@0 182 audio data.
rlm@0 183 */
rlm@0 184 static DWORD WINAPI PlaybackThreadProc(LPVOID lpParameter)
rlm@0 185 {
rlm@0 186 ALCdevice *pDevice = (ALCdevice*)lpParameter;
rlm@0 187 WinMMData *pData = pDevice->ExtraData;
rlm@0 188 LPWAVEHDR pWaveHdr;
rlm@0 189 ALuint FrameSize;
rlm@0 190 MSG msg;
rlm@0 191
rlm@0 192 FrameSize = FrameSizeFromDevFmt(pDevice->FmtChans, pDevice->FmtType);
rlm@0 193
rlm@0 194 SetRTPriority();
rlm@0 195
rlm@0 196 while(GetMessage(&msg, NULL, 0, 0))
rlm@0 197 {
rlm@0 198 if(msg.message != WOM_DONE || pData->bWaveShutdown)
rlm@0 199 continue;
rlm@0 200
rlm@0 201 pWaveHdr = ((LPWAVEHDR)msg.lParam);
rlm@0 202
rlm@0 203 aluMixData(pDevice, pWaveHdr->lpData, pWaveHdr->dwBufferLength/FrameSize);
rlm@0 204
rlm@0 205 // Send buffer back to play more data
rlm@0 206 waveOutWrite(pData->hWaveHandle.Out, pWaveHdr, sizeof(WAVEHDR));
rlm@0 207 InterlockedIncrement(&pData->lWaveBuffersCommitted);
rlm@0 208 }
rlm@0 209
rlm@0 210 // Signal Wave Thread completed event
rlm@0 211 if(pData->hWaveThreadEvent)
rlm@0 212 SetEvent(pData->hWaveThreadEvent);
rlm@0 213
rlm@0 214 ExitThread(0);
rlm@0 215
rlm@0 216 return 0;
rlm@0 217 }
rlm@0 218
rlm@0 219 /*
rlm@0 220 WaveInProc
rlm@0 221
rlm@0 222 Posts a message to 'CaptureThreadProc' everytime a WaveIn Buffer is completed and
rlm@0 223 returns to the application (with more data)
rlm@0 224 */
rlm@0 225 static void CALLBACK WaveInProc(HWAVEIN hDevice,UINT uMsg,DWORD_PTR dwInstance,DWORD_PTR dwParam1,DWORD_PTR dwParam2)
rlm@0 226 {
rlm@0 227 ALCdevice *pDevice = (ALCdevice*)dwInstance;
rlm@0 228 WinMMData *pData = pDevice->ExtraData;
rlm@0 229
rlm@0 230 (void)hDevice;
rlm@0 231 (void)dwParam2;
rlm@0 232
rlm@0 233 if(uMsg != WIM_DATA)
rlm@0 234 return;
rlm@0 235
rlm@0 236 // Decrement number of buffers in use
rlm@0 237 InterlockedDecrement(&pData->lWaveBuffersCommitted);
rlm@0 238
rlm@0 239 if(pData->bWaveShutdown == AL_FALSE)
rlm@0 240 {
rlm@0 241 // Notify Wave Processor Thread that a Wave Header has returned
rlm@0 242 PostThreadMessage(pData->ulWaveThreadID,uMsg,0,dwParam1);
rlm@0 243 }
rlm@0 244 else
rlm@0 245 {
rlm@0 246 if(pData->lWaveBuffersCommitted == 0)
rlm@0 247 {
rlm@0 248 // Post 'Quit' Message to WaveIn Processor Thread
rlm@0 249 PostThreadMessage(pData->ulWaveThreadID,WM_QUIT,0,0);
rlm@0 250 }
rlm@0 251 }
rlm@0 252 }
rlm@0 253
rlm@0 254 /*
rlm@0 255 CaptureThreadProc
rlm@0 256
rlm@0 257 Used by "MMSYSTEM" Device. Called when a WaveIn buffer had been filled with new
rlm@0 258 audio data.
rlm@0 259 */
rlm@0 260 static DWORD WINAPI CaptureThreadProc(LPVOID lpParameter)
rlm@0 261 {
rlm@0 262 ALCdevice *pDevice = (ALCdevice*)lpParameter;
rlm@0 263 WinMMData *pData = pDevice->ExtraData;
rlm@0 264 LPWAVEHDR pWaveHdr;
rlm@0 265 ALuint FrameSize;
rlm@0 266 MSG msg;
rlm@0 267
rlm@0 268 FrameSize = FrameSizeFromDevFmt(pDevice->FmtChans, pDevice->FmtType);
rlm@0 269
rlm@0 270 while(GetMessage(&msg, NULL, 0, 0))
rlm@0 271 {
rlm@0 272 if(msg.message != WIM_DATA || pData->bWaveShutdown)
rlm@0 273 continue;
rlm@0 274
rlm@0 275 pWaveHdr = ((LPWAVEHDR)msg.lParam);
rlm@0 276
rlm@0 277 WriteRingBuffer(pData->pRing, (ALubyte*)pWaveHdr->lpData,
rlm@0 278 pWaveHdr->dwBytesRecorded/FrameSize);
rlm@0 279
rlm@0 280 // Send buffer back to capture more data
rlm@0 281 waveInAddBuffer(pData->hWaveHandle.In,pWaveHdr,sizeof(WAVEHDR));
rlm@0 282 InterlockedIncrement(&pData->lWaveBuffersCommitted);
rlm@0 283 }
rlm@0 284
rlm@0 285 // Signal Wave Thread completed event
rlm@0 286 if(pData->hWaveThreadEvent)
rlm@0 287 SetEvent(pData->hWaveThreadEvent);
rlm@0 288
rlm@0 289 ExitThread(0);
rlm@0 290
rlm@0 291 return 0;
rlm@0 292 }
rlm@0 293
rlm@0 294
rlm@0 295 static ALCboolean WinMMOpenPlayback(ALCdevice *pDevice, const ALCchar *deviceName)
rlm@0 296 {
rlm@0 297 WAVEFORMATEX wfexFormat;
rlm@0 298 WinMMData *pData = NULL;
rlm@0 299 UINT lDeviceID = 0;
rlm@0 300 MMRESULT res;
rlm@0 301 ALuint i = 0;
rlm@0 302
rlm@0 303 // Find the Device ID matching the deviceName if valid
rlm@0 304 if(!deviceName || strcmp(deviceName, woDefault) == 0)
rlm@0 305 lDeviceID = WAVE_MAPPER;
rlm@0 306 else
rlm@0 307 {
rlm@0 308 if(!PlaybackDeviceList)
rlm@0 309 ProbePlaybackDevices();
rlm@0 310
rlm@0 311 for(i = 0;i < NumPlaybackDevices;i++)
rlm@0 312 {
rlm@0 313 if(PlaybackDeviceList[i] &&
rlm@0 314 strcmp(deviceName, PlaybackDeviceList[i]) == 0)
rlm@0 315 {
rlm@0 316 lDeviceID = i;
rlm@0 317 break;
rlm@0 318 }
rlm@0 319 }
rlm@0 320 if(i == NumPlaybackDevices)
rlm@0 321 return ALC_FALSE;
rlm@0 322 }
rlm@0 323
rlm@0 324 pData = calloc(1, sizeof(*pData));
rlm@0 325 if(!pData)
rlm@0 326 {
rlm@0 327 alcSetError(pDevice, ALC_OUT_OF_MEMORY);
rlm@0 328 return ALC_FALSE;
rlm@0 329 }
rlm@0 330 pDevice->ExtraData = pData;
rlm@0 331
rlm@0 332 if(pDevice->FmtChans != DevFmtMono)
rlm@0 333 {
rlm@0 334 if((pDevice->Flags&DEVICE_CHANNELS_REQUEST) &&
rlm@0 335 pDevice->FmtChans != DevFmtStereo)
rlm@0 336 {
rlm@0 337 ERR("Failed to set %s, got Stereo instead\n", DevFmtChannelsString(pDevice->FmtChans));
rlm@0 338 pDevice->Flags &= ~DEVICE_CHANNELS_REQUEST;
rlm@0 339 }
rlm@0 340 pDevice->FmtChans = DevFmtStereo;
rlm@0 341 }
rlm@0 342 switch(pDevice->FmtType)
rlm@0 343 {
rlm@0 344 case DevFmtByte:
rlm@0 345 pDevice->FmtType = DevFmtUByte;
rlm@0 346 break;
rlm@0 347 case DevFmtUShort:
rlm@0 348 case DevFmtFloat:
rlm@0 349 pDevice->FmtType = DevFmtShort;
rlm@0 350 break;
rlm@0 351 case DevFmtUByte:
rlm@0 352 case DevFmtShort:
rlm@0 353 break;
rlm@0 354 }
rlm@0 355
rlm@0 356 memset(&wfexFormat, 0, sizeof(WAVEFORMATEX));
rlm@0 357 wfexFormat.wFormatTag = WAVE_FORMAT_PCM;
rlm@0 358 wfexFormat.nChannels = ChannelsFromDevFmt(pDevice->FmtChans);
rlm@0 359 wfexFormat.wBitsPerSample = BytesFromDevFmt(pDevice->FmtType) * 8;
rlm@0 360 wfexFormat.nBlockAlign = wfexFormat.wBitsPerSample *
rlm@0 361 wfexFormat.nChannels / 8;
rlm@0 362 wfexFormat.nSamplesPerSec = pDevice->Frequency;
rlm@0 363 wfexFormat.nAvgBytesPerSec = wfexFormat.nSamplesPerSec *
rlm@0 364 wfexFormat.nBlockAlign;
rlm@0 365 wfexFormat.cbSize = 0;
rlm@0 366
rlm@0 367 if((res=waveOutOpen(&pData->hWaveHandle.Out, lDeviceID, &wfexFormat, (DWORD_PTR)&WaveOutProc, (DWORD_PTR)pDevice, CALLBACK_FUNCTION)) != MMSYSERR_NOERROR)
rlm@0 368 {
rlm@0 369 ERR("waveOutOpen failed: %u\n", res);
rlm@0 370 goto failure;
rlm@0 371 }
rlm@0 372
rlm@0 373 pData->hWaveThreadEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
rlm@0 374 if(pData->hWaveThreadEvent == NULL)
rlm@0 375 {
rlm@0 376 ERR("CreateEvent failed: %lu\n", GetLastError());
rlm@0 377 goto failure;
rlm@0 378 }
rlm@0 379
rlm@0 380 pData->Frequency = pDevice->Frequency;
rlm@0 381
rlm@0 382 pDevice->szDeviceName = strdup((lDeviceID==WAVE_MAPPER) ? woDefault :
rlm@0 383 PlaybackDeviceList[lDeviceID]);
rlm@0 384 return ALC_TRUE;
rlm@0 385
rlm@0 386 failure:
rlm@0 387 if(pData->hWaveThreadEvent)
rlm@0 388 CloseHandle(pData->hWaveThreadEvent);
rlm@0 389
rlm@0 390 if(pData->hWaveHandle.Out)
rlm@0 391 waveOutClose(pData->hWaveHandle.Out);
rlm@0 392
rlm@0 393 free(pData);
rlm@0 394 pDevice->ExtraData = NULL;
rlm@0 395 return ALC_FALSE;
rlm@0 396 }
rlm@0 397
rlm@0 398 static void WinMMClosePlayback(ALCdevice *device)
rlm@0 399 {
rlm@0 400 WinMMData *pData = (WinMMData*)device->ExtraData;
rlm@0 401
rlm@0 402 // Close the Wave device
rlm@0 403 CloseHandle(pData->hWaveThreadEvent);
rlm@0 404 pData->hWaveThreadEvent = 0;
rlm@0 405
rlm@0 406 waveOutClose(pData->hWaveHandle.Out);
rlm@0 407 pData->hWaveHandle.Out = 0;
rlm@0 408
rlm@0 409 free(pData);
rlm@0 410 device->ExtraData = NULL;
rlm@0 411 }
rlm@0 412
rlm@0 413 static ALCboolean WinMMResetPlayback(ALCdevice *device)
rlm@0 414 {
rlm@0 415 WinMMData *pData = (WinMMData*)device->ExtraData;
rlm@0 416 ALbyte *BufferData;
rlm@0 417 ALint lBufferSize;
rlm@0 418 ALuint i;
rlm@0 419
rlm@0 420 pData->hWaveThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)PlaybackThreadProc, (LPVOID)device, 0, &pData->ulWaveThreadID);
rlm@0 421 if(pData->hWaveThread == NULL)
rlm@0 422 return ALC_FALSE;
rlm@0 423
rlm@0 424 device->UpdateSize = (ALuint)((ALuint64)device->UpdateSize *
rlm@0 425 pData->Frequency / device->Frequency);
rlm@0 426 if(device->Frequency != pData->Frequency)
rlm@0 427 {
rlm@0 428 if((device->Flags&DEVICE_FREQUENCY_REQUEST))
rlm@0 429 ERR("WinMM does not support changing sample rates (wanted %dhz, got %dhz)\n", device->Frequency, pData->Frequency);
rlm@0 430 device->Flags &= ~DEVICE_FREQUENCY_REQUEST;
rlm@0 431 device->Frequency = pData->Frequency;
rlm@0 432 }
rlm@0 433
rlm@0 434 SetDefaultWFXChannelOrder(device);
rlm@0 435
rlm@0 436 pData->lWaveBuffersCommitted = 0;
rlm@0 437
rlm@0 438 // Create 4 Buffers
rlm@0 439 lBufferSize = device->UpdateSize*device->NumUpdates / 4;
rlm@0 440 lBufferSize *= FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
rlm@0 441
rlm@0 442 BufferData = calloc(4, lBufferSize);
rlm@0 443 for(i = 0;i < 4;i++)
rlm@0 444 {
rlm@0 445 memset(&pData->WaveBuffer[i], 0, sizeof(WAVEHDR));
rlm@0 446 pData->WaveBuffer[i].dwBufferLength = lBufferSize;
rlm@0 447 pData->WaveBuffer[i].lpData = ((i==0) ? (LPSTR)BufferData :
rlm@0 448 (pData->WaveBuffer[i-1].lpData +
rlm@0 449 pData->WaveBuffer[i-1].dwBufferLength));
rlm@0 450 waveOutPrepareHeader(pData->hWaveHandle.Out, &pData->WaveBuffer[i], sizeof(WAVEHDR));
rlm@0 451 waveOutWrite(pData->hWaveHandle.Out, &pData->WaveBuffer[i], sizeof(WAVEHDR));
rlm@0 452 InterlockedIncrement(&pData->lWaveBuffersCommitted);
rlm@0 453 }
rlm@0 454
rlm@0 455 return ALC_TRUE;
rlm@0 456 }
rlm@0 457
rlm@0 458 static void WinMMStopPlayback(ALCdevice *device)
rlm@0 459 {
rlm@0 460 WinMMData *pData = (WinMMData*)device->ExtraData;
rlm@0 461 void *buffer = NULL;
rlm@0 462 int i;
rlm@0 463
rlm@0 464 if(pData->hWaveThread == NULL)
rlm@0 465 return;
rlm@0 466
rlm@0 467 // Set flag to stop processing headers
rlm@0 468 pData->bWaveShutdown = AL_TRUE;
rlm@0 469
rlm@0 470 // Wait for signal that Wave Thread has been destroyed
rlm@0 471 WaitForSingleObjectEx(pData->hWaveThreadEvent, 5000, FALSE);
rlm@0 472
rlm@0 473 CloseHandle(pData->hWaveThread);
rlm@0 474 pData->hWaveThread = 0;
rlm@0 475
rlm@0 476 pData->bWaveShutdown = AL_FALSE;
rlm@0 477
rlm@0 478 // Release the wave buffers
rlm@0 479 for(i = 0;i < 4;i++)
rlm@0 480 {
rlm@0 481 waveOutUnprepareHeader(pData->hWaveHandle.Out, &pData->WaveBuffer[i], sizeof(WAVEHDR));
rlm@0 482 if(i == 0) buffer = pData->WaveBuffer[i].lpData;
rlm@0 483 pData->WaveBuffer[i].lpData = NULL;
rlm@0 484 }
rlm@0 485 free(buffer);
rlm@0 486 }
rlm@0 487
rlm@0 488
rlm@0 489 static ALCboolean WinMMOpenCapture(ALCdevice *pDevice, const ALCchar *deviceName)
rlm@0 490 {
rlm@0 491 WAVEFORMATEX wfexCaptureFormat;
rlm@0 492 DWORD ulCapturedDataSize;
rlm@0 493 WinMMData *pData = NULL;
rlm@0 494 UINT lDeviceID = 0;
rlm@0 495 ALbyte *BufferData;
rlm@0 496 ALint lBufferSize;
rlm@0 497 MMRESULT res;
rlm@0 498 ALuint i;
rlm@0 499
rlm@0 500 if(!CaptureDeviceList)
rlm@0 501 ProbeCaptureDevices();
rlm@0 502
rlm@0 503 // Find the Device ID matching the deviceName if valid
rlm@0 504 if(deviceName)
rlm@0 505 {
rlm@0 506 for(i = 0;i < NumCaptureDevices;i++)
rlm@0 507 {
rlm@0 508 if(CaptureDeviceList[i] &&
rlm@0 509 strcmp(deviceName, CaptureDeviceList[i]) == 0)
rlm@0 510 {
rlm@0 511 lDeviceID = i;
rlm@0 512 break;
rlm@0 513 }
rlm@0 514 }
rlm@0 515 }
rlm@0 516 else
rlm@0 517 {
rlm@0 518 for(i = 0;i < NumCaptureDevices;i++)
rlm@0 519 {
rlm@0 520 if(CaptureDeviceList[i])
rlm@0 521 {
rlm@0 522 lDeviceID = i;
rlm@0 523 break;
rlm@0 524 }
rlm@0 525 }
rlm@0 526 }
rlm@0 527 if(i == NumCaptureDevices)
rlm@0 528 return ALC_FALSE;
rlm@0 529
rlm@0 530 pData = calloc(1, sizeof(*pData));
rlm@0 531 if(!pData)
rlm@0 532 {
rlm@0 533 alcSetError(pDevice, ALC_OUT_OF_MEMORY);
rlm@0 534 return ALC_FALSE;
rlm@0 535 }
rlm@0 536 pDevice->ExtraData = pData;
rlm@0 537
rlm@0 538 if((pDevice->FmtChans != DevFmtMono && pDevice->FmtChans != DevFmtStereo) ||
rlm@0 539 (pDevice->FmtType != DevFmtUByte && pDevice->FmtType != DevFmtShort))
rlm@0 540 {
rlm@0 541 alcSetError(pDevice, ALC_INVALID_ENUM);
rlm@0 542 goto failure;
rlm@0 543 }
rlm@0 544
rlm@0 545 memset(&wfexCaptureFormat, 0, sizeof(WAVEFORMATEX));
rlm@0 546 wfexCaptureFormat.wFormatTag = WAVE_FORMAT_PCM;
rlm@0 547 wfexCaptureFormat.nChannels = ChannelsFromDevFmt(pDevice->FmtChans);
rlm@0 548 wfexCaptureFormat.wBitsPerSample = BytesFromDevFmt(pDevice->FmtType) * 8;
rlm@0 549 wfexCaptureFormat.nBlockAlign = wfexCaptureFormat.wBitsPerSample *
rlm@0 550 wfexCaptureFormat.nChannels / 8;
rlm@0 551 wfexCaptureFormat.nSamplesPerSec = pDevice->Frequency;
rlm@0 552 wfexCaptureFormat.nAvgBytesPerSec = wfexCaptureFormat.nSamplesPerSec *
rlm@0 553 wfexCaptureFormat.nBlockAlign;
rlm@0 554 wfexCaptureFormat.cbSize = 0;
rlm@0 555
rlm@0 556 if((res=waveInOpen(&pData->hWaveHandle.In, lDeviceID, &wfexCaptureFormat, (DWORD_PTR)&WaveInProc, (DWORD_PTR)pDevice, CALLBACK_FUNCTION)) != MMSYSERR_NOERROR)
rlm@0 557 {
rlm@0 558 ERR("waveInOpen failed: %u\n", res);
rlm@0 559 goto failure;
rlm@0 560 }
rlm@0 561
rlm@0 562 pData->hWaveThreadEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
rlm@0 563 if(pData->hWaveThreadEvent == NULL)
rlm@0 564 {
rlm@0 565 ERR("CreateEvent failed: %lu\n", GetLastError());
rlm@0 566 goto failure;
rlm@0 567 }
rlm@0 568
rlm@0 569 pData->Frequency = pDevice->Frequency;
rlm@0 570
rlm@0 571 // Allocate circular memory buffer for the captured audio
rlm@0 572 ulCapturedDataSize = pDevice->UpdateSize*pDevice->NumUpdates;
rlm@0 573
rlm@0 574 // Make sure circular buffer is at least 100ms in size
rlm@0 575 if(ulCapturedDataSize < (wfexCaptureFormat.nSamplesPerSec / 10))
rlm@0 576 ulCapturedDataSize = wfexCaptureFormat.nSamplesPerSec / 10;
rlm@0 577
rlm@0 578 pData->pRing = CreateRingBuffer(wfexCaptureFormat.nBlockAlign, ulCapturedDataSize);
rlm@0 579 if(!pData->pRing)
rlm@0 580 goto failure;
rlm@0 581
rlm@0 582 pData->lWaveBuffersCommitted = 0;
rlm@0 583
rlm@0 584 // Create 4 Buffers of 50ms each
rlm@0 585 lBufferSize = wfexCaptureFormat.nAvgBytesPerSec / 20;
rlm@0 586 lBufferSize -= (lBufferSize % wfexCaptureFormat.nBlockAlign);
rlm@0 587
rlm@0 588 BufferData = calloc(4, lBufferSize);
rlm@0 589 if(!BufferData)
rlm@0 590 goto failure;
rlm@0 591
rlm@0 592 for(i = 0;i < 4;i++)
rlm@0 593 {
rlm@0 594 memset(&pData->WaveBuffer[i], 0, sizeof(WAVEHDR));
rlm@0 595 pData->WaveBuffer[i].dwBufferLength = lBufferSize;
rlm@0 596 pData->WaveBuffer[i].lpData = ((i==0) ? (LPSTR)BufferData :
rlm@0 597 (pData->WaveBuffer[i-1].lpData +
rlm@0 598 pData->WaveBuffer[i-1].dwBufferLength));
rlm@0 599 pData->WaveBuffer[i].dwFlags = 0;
rlm@0 600 pData->WaveBuffer[i].dwLoops = 0;
rlm@0 601 waveInPrepareHeader(pData->hWaveHandle.In, &pData->WaveBuffer[i], sizeof(WAVEHDR));
rlm@0 602 waveInAddBuffer(pData->hWaveHandle.In, &pData->WaveBuffer[i], sizeof(WAVEHDR));
rlm@0 603 InterlockedIncrement(&pData->lWaveBuffersCommitted);
rlm@0 604 }
rlm@0 605
rlm@0 606 pData->hWaveThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)CaptureThreadProc, (LPVOID)pDevice, 0, &pData->ulWaveThreadID);
rlm@0 607 if (pData->hWaveThread == NULL)
rlm@0 608 goto failure;
rlm@0 609
rlm@0 610 pDevice->szDeviceName = strdup(CaptureDeviceList[lDeviceID]);
rlm@0 611 return ALC_TRUE;
rlm@0 612
rlm@0 613 failure:
rlm@0 614 if(pData->hWaveThread)
rlm@0 615 CloseHandle(pData->hWaveThread);
rlm@0 616
rlm@0 617 for(i = 0;i < 4;i++)
rlm@0 618 {
rlm@0 619 if(pData->WaveBuffer[i].lpData)
rlm@0 620 {
rlm@0 621 waveInUnprepareHeader(pData->hWaveHandle.In, &pData->WaveBuffer[i], sizeof(WAVEHDR));
rlm@0 622 if(i == 0)
rlm@0 623 free(pData->WaveBuffer[i].lpData);
rlm@0 624 }
rlm@0 625 }
rlm@0 626
rlm@0 627 if(pData->pRing)
rlm@0 628 DestroyRingBuffer(pData->pRing);
rlm@0 629
rlm@0 630 if(pData->hWaveThreadEvent)
rlm@0 631 CloseHandle(pData->hWaveThreadEvent);
rlm@0 632
rlm@0 633 if(pData->hWaveHandle.In)
rlm@0 634 waveInClose(pData->hWaveHandle.In);
rlm@0 635
rlm@0 636 free(pData);
rlm@0 637 pDevice->ExtraData = NULL;
rlm@0 638 return ALC_FALSE;
rlm@0 639 }
rlm@0 640
rlm@0 641 static void WinMMCloseCapture(ALCdevice *pDevice)
rlm@0 642 {
rlm@0 643 WinMMData *pData = (WinMMData*)pDevice->ExtraData;
rlm@0 644 void *buffer = NULL;
rlm@0 645 int i;
rlm@0 646
rlm@0 647 // Call waveOutReset to shutdown wave device
rlm@0 648 pData->bWaveShutdown = AL_TRUE;
rlm@0 649 waveInReset(pData->hWaveHandle.In);
rlm@0 650
rlm@0 651 // Wait for signal that Wave Thread has been destroyed
rlm@0 652 WaitForSingleObjectEx(pData->hWaveThreadEvent, 5000, FALSE);
rlm@0 653
rlm@0 654 CloseHandle(pData->hWaveThread);
rlm@0 655 pData->hWaveThread = 0;
rlm@0 656
rlm@0 657 // Release the wave buffers
rlm@0 658 for(i = 0;i < 4;i++)
rlm@0 659 {
rlm@0 660 waveInUnprepareHeader(pData->hWaveHandle.In, &pData->WaveBuffer[i], sizeof(WAVEHDR));
rlm@0 661 if(i == 0) buffer = pData->WaveBuffer[i].lpData;
rlm@0 662 pData->WaveBuffer[i].lpData = NULL;
rlm@0 663 }
rlm@0 664 free(buffer);
rlm@0 665
rlm@0 666 DestroyRingBuffer(pData->pRing);
rlm@0 667 pData->pRing = NULL;
rlm@0 668
rlm@0 669 // Close the Wave device
rlm@0 670 CloseHandle(pData->hWaveThreadEvent);
rlm@0 671 pData->hWaveThreadEvent = 0;
rlm@0 672
rlm@0 673 waveInClose(pData->hWaveHandle.In);
rlm@0 674 pData->hWaveHandle.In = 0;
rlm@0 675
rlm@0 676 free(pData);
rlm@0 677 pDevice->ExtraData = NULL;
rlm@0 678 }
rlm@0 679
rlm@0 680 static void WinMMStartCapture(ALCdevice *pDevice)
rlm@0 681 {
rlm@0 682 WinMMData *pData = (WinMMData*)pDevice->ExtraData;
rlm@0 683 waveInStart(pData->hWaveHandle.In);
rlm@0 684 }
rlm@0 685
rlm@0 686 static void WinMMStopCapture(ALCdevice *pDevice)
rlm@0 687 {
rlm@0 688 WinMMData *pData = (WinMMData*)pDevice->ExtraData;
rlm@0 689 waveInStop(pData->hWaveHandle.In);
rlm@0 690 }
rlm@0 691
rlm@0 692 static ALCuint WinMMAvailableSamples(ALCdevice *pDevice)
rlm@0 693 {
rlm@0 694 WinMMData *pData = (WinMMData*)pDevice->ExtraData;
rlm@0 695 return RingBufferSize(pData->pRing);
rlm@0 696 }
rlm@0 697
rlm@0 698 static void WinMMCaptureSamples(ALCdevice *pDevice, ALCvoid *pBuffer, ALCuint lSamples)
rlm@0 699 {
rlm@0 700 WinMMData *pData = (WinMMData*)pDevice->ExtraData;
rlm@0 701
rlm@0 702 if(WinMMAvailableSamples(pDevice) >= lSamples)
rlm@0 703 ReadRingBuffer(pData->pRing, pBuffer, lSamples);
rlm@0 704 else
rlm@0 705 alcSetError(pDevice, ALC_INVALID_VALUE);
rlm@0 706 }
rlm@0 707
rlm@0 708
rlm@0 709 static const BackendFuncs WinMMFuncs = {
rlm@0 710 WinMMOpenPlayback,
rlm@0 711 WinMMClosePlayback,
rlm@0 712 WinMMResetPlayback,
rlm@0 713 WinMMStopPlayback,
rlm@0 714 WinMMOpenCapture,
rlm@0 715 WinMMCloseCapture,
rlm@0 716 WinMMStartCapture,
rlm@0 717 WinMMStopCapture,
rlm@0 718 WinMMCaptureSamples,
rlm@0 719 WinMMAvailableSamples
rlm@0 720 };
rlm@0 721
rlm@0 722 ALCboolean alcWinMMInit(BackendFuncs *FuncList)
rlm@0 723 {
rlm@0 724 *FuncList = WinMMFuncs;
rlm@0 725 return ALC_TRUE;
rlm@0 726 }
rlm@0 727
rlm@0 728 void alcWinMMDeinit()
rlm@0 729 {
rlm@0 730 ALuint lLoop;
rlm@0 731
rlm@0 732 for(lLoop = 0;lLoop < NumPlaybackDevices;lLoop++)
rlm@0 733 free(PlaybackDeviceList[lLoop]);
rlm@0 734 free(PlaybackDeviceList);
rlm@0 735 PlaybackDeviceList = NULL;
rlm@0 736
rlm@0 737 NumPlaybackDevices = 0;
rlm@0 738
rlm@0 739
rlm@0 740 for(lLoop = 0; lLoop < NumCaptureDevices; lLoop++)
rlm@0 741 free(CaptureDeviceList[lLoop]);
rlm@0 742 free(CaptureDeviceList);
rlm@0 743 CaptureDeviceList = NULL;
rlm@0 744
rlm@0 745 NumCaptureDevices = 0;
rlm@0 746 }
rlm@0 747
rlm@0 748 void alcWinMMProbe(enum DevProbe type)
rlm@0 749 {
rlm@0 750 ALuint i;
rlm@0 751
rlm@0 752 switch(type)
rlm@0 753 {
rlm@0 754 case DEVICE_PROBE:
rlm@0 755 ProbePlaybackDevices();
rlm@0 756 if(NumPlaybackDevices > 0)
rlm@0 757 AppendDeviceList(woDefault);
rlm@0 758 break;
rlm@0 759
rlm@0 760 case ALL_DEVICE_PROBE:
rlm@0 761 ProbePlaybackDevices();
rlm@0 762 if(NumPlaybackDevices > 0)
rlm@0 763 AppendAllDeviceList(woDefault);
rlm@0 764 for(i = 0;i < NumPlaybackDevices;i++)
rlm@0 765 {
rlm@0 766 if(PlaybackDeviceList[i])
rlm@0 767 AppendAllDeviceList(PlaybackDeviceList[i]);
rlm@0 768 }
rlm@0 769 break;
rlm@0 770
rlm@0 771 case CAPTURE_DEVICE_PROBE:
rlm@0 772 ProbeCaptureDevices();
rlm@0 773 for(i = 0;i < NumCaptureDevices;i++)
rlm@0 774 {
rlm@0 775 if(CaptureDeviceList[i])
rlm@0 776 AppendCaptureDeviceList(CaptureDeviceList[i]);
rlm@0 777 }
rlm@0 778 break;
rlm@0 779 }
rlm@0 780 }