Move the device lock into the backend function table
[openal-soft/openal-hmr.git] / Alc / backends / winmm.c
blob13012d81c2ee811f3c9ae2ffbb4f270a8da4dbfd
1 /**
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
21 #include "config.h"
23 #include <stdlib.h>
24 #include <stdio.h>
25 #include <memory.h>
27 #include <windows.h>
28 #include <mmsystem.h>
30 #include "alMain.h"
31 #include "AL/al.h"
32 #include "AL/alc.h"
34 #ifndef WAVE_FORMAT_IEEE_FLOAT
35 #define WAVE_FORMAT_IEEE_FLOAT 0x0003
36 #endif
39 typedef struct {
40 // MMSYSTEM Device
41 volatile ALboolean killNow;
42 HANDLE WaveThreadEvent;
43 HANDLE WaveThread;
44 DWORD WaveThreadID;
45 volatile LONG WaveBuffersCommitted;
46 WAVEHDR WaveBuffer[4];
48 union {
49 HWAVEIN In;
50 HWAVEOUT Out;
51 } WaveHandle;
53 WAVEFORMATEX Format;
55 RingBuffer *Ring;
56 } WinMMData;
59 static ALCchar **PlaybackDeviceList;
60 static ALuint NumPlaybackDevices;
61 static ALCchar **CaptureDeviceList;
62 static ALuint NumCaptureDevices;
65 static void ProbePlaybackDevices(void)
67 ALuint i;
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++)
76 WAVEOUTCAPS WaveCaps;
78 PlaybackDeviceList[i] = NULL;
79 if(waveOutGetDevCaps(i, &WaveCaps, sizeof(WaveCaps)) == MMSYSERR_NOERROR)
81 char name[1024];
82 ALuint count, j;
84 count = 0;
85 do {
86 if(count == 0)
87 snprintf(name, sizeof(name), "%s", WaveCaps.szPname);
88 else
89 snprintf(name, sizeof(name), "%s #%d", WaveCaps.szPname, count+1);
90 count++;
92 for(j = 0;j < i;j++)
94 if(strcmp(name, PlaybackDeviceList[j]) == 0)
95 break;
97 } while(j != i);
99 PlaybackDeviceList[i] = strdup(name);
104 static void ProbeCaptureDevices(void)
106 ALuint i;
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)
120 char name[1024];
121 ALuint count, j;
123 count = 0;
124 do {
125 if(count == 0)
126 snprintf(name, sizeof(name), "%s", WaveInCaps.szPname);
127 else
128 snprintf(name, sizeof(name), "%s #%d", WaveInCaps.szPname, count+1);
129 count++;
131 for(j = 0;j < i;j++)
133 if(strcmp(name, CaptureDeviceList[j]) == 0)
134 break;
136 } while(j != i);
138 CaptureDeviceList[i] = strdup(name);
145 WaveOutProc
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;
155 (void)device;
156 (void)param2;
158 if(msg != WOM_DONE)
159 return;
161 InterlockedDecrement(&data->WaveBuffersCommitted);
162 PostThreadMessage(data->WaveThreadID, msg, 0, param1);
166 PlaybackThreadProc
168 Used by "MMSYSTEM" Device. Called when a WaveOut buffer has used up its
169 audio data.
171 static DWORD WINAPI PlaybackThreadProc(LPVOID param)
173 ALCdevice *Device = (ALCdevice*)param;
174 WinMMData *data = Device->ExtraData;
175 LPWAVEHDR WaveHdr;
176 ALuint FrameSize;
177 MSG msg;
179 FrameSize = FrameSizeFromDevFmt(Device->FmtChans, Device->FmtType);
181 SetRTPriority();
183 while(GetMessage(&msg, NULL, 0, 0))
185 if(msg.message != WOM_DONE)
186 continue;
188 if(data->killNow)
190 if(data->WaveBuffersCommitted == 0)
191 break;
192 continue;
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);
207 ExitThread(0);
208 return 0;
212 WaveInProc
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;
222 (void)device;
223 (void)param2;
225 if(msg != WIM_DATA)
226 return;
228 InterlockedDecrement(&data->WaveBuffersCommitted);
229 PostThreadMessage(data->WaveThreadID, msg, 0, param1);
233 CaptureThreadProc
235 Used by "MMSYSTEM" Device. Called when a WaveIn buffer had been filled with new
236 audio data.
238 static DWORD WINAPI CaptureThreadProc(LPVOID param)
240 ALCdevice *Device = (ALCdevice*)param;
241 WinMMData *data = Device->ExtraData;
242 LPWAVEHDR WaveHdr;
243 ALuint FrameSize;
244 MSG msg;
246 FrameSize = FrameSizeFromDevFmt(Device->FmtChans, Device->FmtType);
248 while(GetMessage(&msg, NULL, 0, 0))
250 if(msg.message != WIM_DATA)
251 continue;
252 /* Don't wait for other buffers to finish before quitting. We're
253 * closing so we don't need them. */
254 if(data->killNow)
255 break;
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);
269 ExitThread(0);
270 return 0;
274 static ALCenum WinMMOpenPlayback(ALCdevice *Device, const ALCchar *deviceName)
276 WinMMData *data = NULL;
277 UINT DeviceID = 0;
278 MMRESULT res;
279 ALuint i = 0;
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))
290 DeviceID = i;
291 break;
294 if(i == NumPlaybackDevices)
295 return ALC_INVALID_VALUE;
297 data = calloc(1, sizeof(*data));
298 if(!data)
299 return ALC_OUT_OF_MEMORY;
300 Device->ExtraData = data;
302 retry_open:
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;
309 else
311 data->Format.wFormatTag = WAVE_FORMAT_PCM;
312 if(Device->FmtType == DevFmtUByte || Device->FmtType == DevFmtByte)
313 data->Format.wBitsPerSample = 8;
314 else
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;
330 goto retry_open;
332 ERR("waveOutOpen failed: %u\n", res);
333 goto failure;
336 data->WaveThreadEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
337 if(data->WaveThreadEvent == NULL)
339 ERR("CreateEvent failed: %lu\n", GetLastError());
340 goto failure;
343 Device->DeviceName = strdup(PlaybackDeviceList[DeviceID]);
344 return ALC_NO_ERROR;
346 failure:
347 if(data->WaveThreadEvent)
348 CloseHandle(data->WaveThreadEvent);
350 if(data->WaveHandle.Out)
351 waveOutClose(data->WaveHandle.Out);
353 free(data);
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;
369 free(data);
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 /
379 device->Frequency);
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;
388 else
390 ERR("Unhandled IEEE float sample depth: %d\n", data->Format.wBitsPerSample);
391 return ALC_FALSE;
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;
400 else
402 ERR("Unhandled PCM sample depth: %d\n", data->Format.wBitsPerSample);
403 return ALC_FALSE;
406 else
408 ERR("Unhandled format tag: 0x%04x\n", data->Format.wFormatTag);
409 return ALC_FALSE;
412 if(data->Format.nChannels == 2)
413 device->FmtChans = DevFmtStereo;
414 else if(data->Format.nChannels == 1)
415 device->FmtChans = DevFmtMono;
416 else
418 ERR("Unhandled channel count: %d\n", data->Format.nChannels);
419 return ALC_FALSE;
421 SetDefaultWFXChannelOrder(device);
423 return ALC_TRUE;
426 static ALCboolean WinMMStartPlayback(ALCdevice *device)
428 WinMMData *data = (WinMMData*)device->ExtraData;
429 ALbyte *BufferData;
430 ALint BufferSize;
431 ALuint i;
433 data->WaveThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)PlaybackThreadProc, (LPVOID)device, 0, &data->WaveThreadID);
434 if(data->WaveThread == NULL)
435 return ALC_FALSE;
437 data->WaveBuffersCommitted = 0;
439 // Create 4 Buffers
440 BufferSize = device->UpdateSize*device->NumUpdates / 4;
441 BufferSize *= FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
443 BufferData = calloc(4, BufferSize);
444 for(i = 0;i < 4;i++)
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);
456 return ALC_TRUE;
459 static void WinMMStopPlayback(ALCdevice *device)
461 WinMMData *data = (WinMMData*)device->ExtraData;
462 void *buffer = NULL;
463 int i;
465 if(data->WaveThread == NULL)
466 return;
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
480 for(i = 0;i < 4;i++)
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;
486 free(buffer);
490 static ALCenum WinMMOpenCapture(ALCdevice *Device, const ALCchar *deviceName)
492 ALbyte *BufferData = NULL;
493 DWORD CapturedDataSize;
494 WinMMData *data = NULL;
495 UINT DeviceID = 0;
496 ALint BufferSize;
497 MMRESULT res;
498 ALuint i;
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))
509 DeviceID = i;
510 break;
513 if(i == NumCaptureDevices)
514 return ALC_INVALID_VALUE;
516 switch(Device->FmtChans)
518 case DevFmtMono:
519 case DevFmtStereo:
520 break;
522 case DevFmtQuad:
523 case DevFmtX51:
524 case DevFmtX51Side:
525 case DevFmtX61:
526 case DevFmtX71:
527 return ALC_INVALID_ENUM;
530 switch(Device->FmtType)
532 case DevFmtUByte:
533 case DevFmtShort:
534 case DevFmtInt:
535 case DevFmtFloat:
536 break;
538 case DevFmtByte:
539 case DevFmtUShort:
540 case DevFmtUInt:
541 return ALC_INVALID_ENUM;
544 data = calloc(1, sizeof(*data));
545 if(!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);
564 goto failure;
567 data->WaveThreadEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
568 if(data->WaveThreadEvent == NULL)
570 ERR("CreateEvent failed: %lu\n", GetLastError());
571 goto failure;
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);
582 if(!data->Ring)
583 goto failure;
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);
592 if(!BufferData)
593 goto failure;
595 for(i = 0;i < 4;i++)
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)
611 goto failure;
613 Device->DeviceName = strdup(CaptureDeviceList[DeviceID]);
614 return ALC_NO_ERROR;
616 failure:
617 if(data->WaveThread)
618 CloseHandle(data->WaveThread);
620 if(BufferData)
622 for(i = 0;i < 4;i++)
623 waveInUnprepareHeader(data->WaveHandle.In, &data->WaveBuffer[i], sizeof(WAVEHDR));
624 free(BufferData);
627 if(data->Ring)
628 DestroyRingBuffer(data->Ring);
630 if(data->WaveThreadEvent)
631 CloseHandle(data->WaveThreadEvent);
633 if(data->WaveHandle.In)
634 waveInClose(data->WaveHandle.In);
636 free(data);
637 Device->ExtraData = NULL;
638 return ALC_INVALID_VALUE;
641 static void WinMMCloseCapture(ALCdevice *Device)
643 WinMMData *data = (WinMMData*)Device->ExtraData;
644 void *buffer = NULL;
645 int i;
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
660 for(i = 0;i < 4;i++)
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;
666 free(buffer);
668 DestroyRingBuffer(data->Ring);
669 data->Ring = NULL;
671 // Close the Wave device
672 CloseHandle(data->WaveThreadEvent);
673 data->WaveThreadEvent = 0;
675 waveInClose(data->WaveHandle.In);
676 data->WaveHandle.In = 0;
678 free(data);
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);
698 return ALC_NO_ERROR;
701 static ALCuint WinMMAvailableSamples(ALCdevice *Device)
703 WinMMData *data = (WinMMData*)Device->ExtraData;
704 return RingBufferSize(data->Ring);
708 static ALint64 WinMMGetLatency(ALCdevice *device)
710 (void)device;
711 return 0;
715 static const BackendFuncs WinMMFuncs = {
716 WinMMOpenPlayback,
717 WinMMClosePlayback,
718 WinMMResetPlayback,
719 WinMMStartPlayback,
720 WinMMStopPlayback,
721 WinMMOpenCapture,
722 WinMMCloseCapture,
723 WinMMStartCapture,
724 WinMMStopCapture,
725 WinMMCaptureSamples,
726 WinMMAvailableSamples,
727 ALCdevice_LockDefault,
728 ALCdevice_UnlockDefault,
729 WinMMGetLatency
732 ALCboolean alcWinMMInit(BackendFuncs *FuncList)
734 *FuncList = WinMMFuncs;
735 return ALC_TRUE;
738 void alcWinMMDeinit()
740 ALuint i;
742 for(i = 0;i < NumPlaybackDevices;i++)
743 free(PlaybackDeviceList[i]);
744 free(PlaybackDeviceList);
745 PlaybackDeviceList = NULL;
747 NumPlaybackDevices = 0;
750 for(i = 0;i < NumCaptureDevices;i++)
751 free(CaptureDeviceList[i]);
752 free(CaptureDeviceList);
753 CaptureDeviceList = NULL;
755 NumCaptureDevices = 0;
758 void alcWinMMProbe(enum DevProbe type)
760 ALuint i;
762 switch(type)
764 case ALL_DEVICE_PROBE:
765 ProbePlaybackDevices();
766 for(i = 0;i < NumPlaybackDevices;i++)
768 if(PlaybackDeviceList[i])
769 AppendAllDevicesList(PlaybackDeviceList[i]);
771 break;
773 case CAPTURE_DEVICE_PROBE:
774 ProbeCaptureDevices();
775 for(i = 0;i < NumCaptureDevices;i++)
777 if(CaptureDeviceList[i])
778 AppendCaptureDeviceList(CaptureDeviceList[i]);
780 break;