Implement capture support for SoundIO
[openal-soft.git] / Alc / backends / winmm.c
blob2f4c65dffef86cdf18efa92b0f058345dfe17a75
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.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 "alu.h"
32 #include "ringbuffer.h"
33 #include "threads.h"
35 #include "backends/base.h"
37 #ifndef WAVE_FORMAT_IEEE_FLOAT
38 #define WAVE_FORMAT_IEEE_FLOAT 0x0003
39 #endif
41 #define DEVNAME_HEAD "OpenAL Soft on "
44 static vector_al_string PlaybackDevices;
45 static vector_al_string CaptureDevices;
47 static void clear_devlist(vector_al_string *list)
49 VECTOR_FOR_EACH(al_string, *list, alstr_reset);
50 VECTOR_RESIZE(*list, 0, 0);
54 static void ProbePlaybackDevices(void)
56 ALuint numdevs;
57 ALuint i;
59 clear_devlist(&PlaybackDevices);
61 numdevs = waveOutGetNumDevs();
62 VECTOR_RESIZE(PlaybackDevices, 0, numdevs);
63 for(i = 0;i < numdevs;i++)
65 WAVEOUTCAPSW WaveCaps;
66 const al_string *iter;
67 al_string dname;
69 AL_STRING_INIT(dname);
70 if(waveOutGetDevCapsW(i, &WaveCaps, sizeof(WaveCaps)) == MMSYSERR_NOERROR)
72 ALuint count = 0;
73 while(1)
75 alstr_copy_cstr(&dname, DEVNAME_HEAD);
76 alstr_append_wcstr(&dname, WaveCaps.szPname);
77 if(count != 0)
79 char str[64];
80 snprintf(str, sizeof(str), " #%d", count+1);
81 alstr_append_cstr(&dname, str);
83 count++;
85 #define MATCH_ENTRY(i) (alstr_cmp(dname, *(i)) == 0)
86 VECTOR_FIND_IF(iter, const al_string, PlaybackDevices, MATCH_ENTRY);
87 if(iter == VECTOR_END(PlaybackDevices)) break;
88 #undef MATCH_ENTRY
91 TRACE("Got device \"%s\", ID %u\n", alstr_get_cstr(dname), i);
93 VECTOR_PUSH_BACK(PlaybackDevices, dname);
97 static void ProbeCaptureDevices(void)
99 ALuint numdevs;
100 ALuint i;
102 clear_devlist(&CaptureDevices);
104 numdevs = waveInGetNumDevs();
105 VECTOR_RESIZE(CaptureDevices, 0, numdevs);
106 for(i = 0;i < numdevs;i++)
108 WAVEINCAPSW WaveCaps;
109 const al_string *iter;
110 al_string dname;
112 AL_STRING_INIT(dname);
113 if(waveInGetDevCapsW(i, &WaveCaps, sizeof(WaveCaps)) == MMSYSERR_NOERROR)
115 ALuint count = 0;
116 while(1)
118 alstr_copy_cstr(&dname, DEVNAME_HEAD);
119 alstr_append_wcstr(&dname, WaveCaps.szPname);
120 if(count != 0)
122 char str[64];
123 snprintf(str, sizeof(str), " #%d", count+1);
124 alstr_append_cstr(&dname, str);
126 count++;
128 #define MATCH_ENTRY(i) (alstr_cmp(dname, *(i)) == 0)
129 VECTOR_FIND_IF(iter, const al_string, CaptureDevices, MATCH_ENTRY);
130 if(iter == VECTOR_END(CaptureDevices)) break;
131 #undef MATCH_ENTRY
134 TRACE("Got device \"%s\", ID %u\n", alstr_get_cstr(dname), i);
136 VECTOR_PUSH_BACK(CaptureDevices, dname);
141 typedef struct ALCwinmmPlayback {
142 DERIVE_FROM_TYPE(ALCbackend);
144 RefCount WaveBuffersCommitted;
145 WAVEHDR WaveBuffer[4];
147 HWAVEOUT OutHdl;
149 WAVEFORMATEX Format;
151 ATOMIC(ALenum) killNow;
152 althrd_t thread;
153 } ALCwinmmPlayback;
155 static void ALCwinmmPlayback_Construct(ALCwinmmPlayback *self, ALCdevice *device);
156 static void ALCwinmmPlayback_Destruct(ALCwinmmPlayback *self);
158 static void CALLBACK ALCwinmmPlayback_waveOutProc(HWAVEOUT device, UINT msg, DWORD_PTR instance, DWORD_PTR param1, DWORD_PTR param2);
159 static int ALCwinmmPlayback_mixerProc(void *arg);
161 static ALCenum ALCwinmmPlayback_open(ALCwinmmPlayback *self, const ALCchar *name);
162 static ALCboolean ALCwinmmPlayback_reset(ALCwinmmPlayback *self);
163 static ALCboolean ALCwinmmPlayback_start(ALCwinmmPlayback *self);
164 static void ALCwinmmPlayback_stop(ALCwinmmPlayback *self);
165 static DECLARE_FORWARD2(ALCwinmmPlayback, ALCbackend, ALCenum, captureSamples, ALCvoid*, ALCuint)
166 static DECLARE_FORWARD(ALCwinmmPlayback, ALCbackend, ALCuint, availableSamples)
167 static DECLARE_FORWARD(ALCwinmmPlayback, ALCbackend, ClockLatency, getClockLatency)
168 static DECLARE_FORWARD(ALCwinmmPlayback, ALCbackend, void, lock)
169 static DECLARE_FORWARD(ALCwinmmPlayback, ALCbackend, void, unlock)
170 DECLARE_DEFAULT_ALLOCATORS(ALCwinmmPlayback)
172 DEFINE_ALCBACKEND_VTABLE(ALCwinmmPlayback);
175 static void ALCwinmmPlayback_Construct(ALCwinmmPlayback *self, ALCdevice *device)
177 ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
178 SET_VTABLE2(ALCwinmmPlayback, ALCbackend, self);
180 InitRef(&self->WaveBuffersCommitted, 0);
181 self->OutHdl = NULL;
183 ATOMIC_INIT(&self->killNow, AL_TRUE);
186 static void ALCwinmmPlayback_Destruct(ALCwinmmPlayback *self)
188 if(self->OutHdl)
189 waveOutClose(self->OutHdl);
190 self->OutHdl = 0;
192 ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
196 /* ALCwinmmPlayback_waveOutProc
198 * Posts a message to 'ALCwinmmPlayback_mixerProc' everytime a WaveOut Buffer
199 * is completed and returns to the application (for more data)
201 static void CALLBACK ALCwinmmPlayback_waveOutProc(HWAVEOUT UNUSED(device), UINT msg, DWORD_PTR instance, DWORD_PTR param1, DWORD_PTR UNUSED(param2))
203 ALCwinmmPlayback *self = (ALCwinmmPlayback*)instance;
205 if(msg != WOM_DONE)
206 return;
208 DecrementRef(&self->WaveBuffersCommitted);
209 PostThreadMessage(self->thread, msg, 0, param1);
212 FORCE_ALIGN static int ALCwinmmPlayback_mixerProc(void *arg)
214 ALCwinmmPlayback *self = arg;
215 ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
216 WAVEHDR *WaveHdr;
217 MSG msg;
219 SetRTPriority();
220 althrd_setname(althrd_current(), MIXER_THREAD_NAME);
222 while(GetMessage(&msg, NULL, 0, 0))
224 if(msg.message != WOM_DONE)
225 continue;
227 if(ATOMIC_LOAD(&self->killNow, almemory_order_acquire))
229 if(ReadRef(&self->WaveBuffersCommitted) == 0)
230 break;
231 continue;
234 WaveHdr = ((WAVEHDR*)msg.lParam);
235 ALCwinmmPlayback_lock(self);
236 aluMixData(device, WaveHdr->lpData, WaveHdr->dwBufferLength /
237 self->Format.nBlockAlign);
238 ALCwinmmPlayback_unlock(self);
240 // Send buffer back to play more data
241 waveOutWrite(self->OutHdl, WaveHdr, sizeof(WAVEHDR));
242 IncrementRef(&self->WaveBuffersCommitted);
245 return 0;
249 static ALCenum ALCwinmmPlayback_open(ALCwinmmPlayback *self, const ALCchar *deviceName)
251 ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
252 const al_string *iter;
253 UINT DeviceID;
254 MMRESULT res;
256 if(VECTOR_SIZE(PlaybackDevices) == 0)
257 ProbePlaybackDevices();
259 // Find the Device ID matching the deviceName if valid
260 #define MATCH_DEVNAME(iter) (!alstr_empty(*(iter)) && \
261 (!deviceName || alstr_cmp_cstr(*(iter), deviceName) == 0))
262 VECTOR_FIND_IF(iter, const al_string, PlaybackDevices, MATCH_DEVNAME);
263 if(iter == VECTOR_END(PlaybackDevices))
264 return ALC_INVALID_VALUE;
265 #undef MATCH_DEVNAME
267 DeviceID = (UINT)(iter - VECTOR_BEGIN(PlaybackDevices));
269 retry_open:
270 memset(&self->Format, 0, sizeof(WAVEFORMATEX));
271 if(device->FmtType == DevFmtFloat)
273 self->Format.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
274 self->Format.wBitsPerSample = 32;
276 else
278 self->Format.wFormatTag = WAVE_FORMAT_PCM;
279 if(device->FmtType == DevFmtUByte || device->FmtType == DevFmtByte)
280 self->Format.wBitsPerSample = 8;
281 else
282 self->Format.wBitsPerSample = 16;
284 self->Format.nChannels = ((device->FmtChans == DevFmtMono) ? 1 : 2);
285 self->Format.nBlockAlign = self->Format.wBitsPerSample *
286 self->Format.nChannels / 8;
287 self->Format.nSamplesPerSec = device->Frequency;
288 self->Format.nAvgBytesPerSec = self->Format.nSamplesPerSec *
289 self->Format.nBlockAlign;
290 self->Format.cbSize = 0;
292 if((res=waveOutOpen(&self->OutHdl, DeviceID, &self->Format, (DWORD_PTR)&ALCwinmmPlayback_waveOutProc, (DWORD_PTR)self, CALLBACK_FUNCTION)) != MMSYSERR_NOERROR)
294 if(device->FmtType == DevFmtFloat)
296 device->FmtType = DevFmtShort;
297 goto retry_open;
299 ERR("waveOutOpen failed: %u\n", res);
300 goto failure;
303 alstr_copy(&device->DeviceName, VECTOR_ELEM(PlaybackDevices, DeviceID));
304 return ALC_NO_ERROR;
306 failure:
307 if(self->OutHdl)
308 waveOutClose(self->OutHdl);
309 self->OutHdl = NULL;
311 return ALC_INVALID_VALUE;
314 static ALCboolean ALCwinmmPlayback_reset(ALCwinmmPlayback *self)
316 ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
318 device->UpdateSize = (ALuint)((ALuint64)device->UpdateSize *
319 self->Format.nSamplesPerSec /
320 device->Frequency);
321 device->UpdateSize = (device->UpdateSize*device->NumUpdates + 3) / 4;
322 device->NumUpdates = 4;
323 device->Frequency = self->Format.nSamplesPerSec;
325 if(self->Format.wFormatTag == WAVE_FORMAT_IEEE_FLOAT)
327 if(self->Format.wBitsPerSample == 32)
328 device->FmtType = DevFmtFloat;
329 else
331 ERR("Unhandled IEEE float sample depth: %d\n", self->Format.wBitsPerSample);
332 return ALC_FALSE;
335 else if(self->Format.wFormatTag == WAVE_FORMAT_PCM)
337 if(self->Format.wBitsPerSample == 16)
338 device->FmtType = DevFmtShort;
339 else if(self->Format.wBitsPerSample == 8)
340 device->FmtType = DevFmtUByte;
341 else
343 ERR("Unhandled PCM sample depth: %d\n", self->Format.wBitsPerSample);
344 return ALC_FALSE;
347 else
349 ERR("Unhandled format tag: 0x%04x\n", self->Format.wFormatTag);
350 return ALC_FALSE;
353 if(self->Format.nChannels == 2)
354 device->FmtChans = DevFmtStereo;
355 else if(self->Format.nChannels == 1)
356 device->FmtChans = DevFmtMono;
357 else
359 ERR("Unhandled channel count: %d\n", self->Format.nChannels);
360 return ALC_FALSE;
362 SetDefaultWFXChannelOrder(device);
364 return ALC_TRUE;
367 static ALCboolean ALCwinmmPlayback_start(ALCwinmmPlayback *self)
369 ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
370 ALbyte *BufferData;
371 ALint BufferSize;
372 ALuint i;
374 ATOMIC_STORE(&self->killNow, AL_FALSE, almemory_order_release);
375 if(althrd_create(&self->thread, ALCwinmmPlayback_mixerProc, self) != althrd_success)
376 return ALC_FALSE;
378 InitRef(&self->WaveBuffersCommitted, 0);
380 // Create 4 Buffers
381 BufferSize = device->UpdateSize*device->NumUpdates / 4;
382 BufferSize *= FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
384 BufferData = calloc(4, BufferSize);
385 for(i = 0;i < 4;i++)
387 memset(&self->WaveBuffer[i], 0, sizeof(WAVEHDR));
388 self->WaveBuffer[i].dwBufferLength = BufferSize;
389 self->WaveBuffer[i].lpData = ((i==0) ? (CHAR*)BufferData :
390 (self->WaveBuffer[i-1].lpData +
391 self->WaveBuffer[i-1].dwBufferLength));
392 waveOutPrepareHeader(self->OutHdl, &self->WaveBuffer[i], sizeof(WAVEHDR));
393 waveOutWrite(self->OutHdl, &self->WaveBuffer[i], sizeof(WAVEHDR));
394 IncrementRef(&self->WaveBuffersCommitted);
397 return ALC_TRUE;
400 static void ALCwinmmPlayback_stop(ALCwinmmPlayback *self)
402 void *buffer = NULL;
403 int i;
405 if(ATOMIC_EXCHANGE(&self->killNow, AL_TRUE, almemory_order_acq_rel))
406 return;
407 althrd_join(self->thread, &i);
409 // Release the wave buffers
410 for(i = 0;i < 4;i++)
412 waveOutUnprepareHeader(self->OutHdl, &self->WaveBuffer[i], sizeof(WAVEHDR));
413 if(i == 0) buffer = self->WaveBuffer[i].lpData;
414 self->WaveBuffer[i].lpData = NULL;
416 free(buffer);
421 typedef struct ALCwinmmCapture {
422 DERIVE_FROM_TYPE(ALCbackend);
424 RefCount WaveBuffersCommitted;
425 WAVEHDR WaveBuffer[4];
427 HWAVEIN InHdl;
429 ll_ringbuffer_t *Ring;
431 WAVEFORMATEX Format;
433 ATOMIC(ALenum) killNow;
434 althrd_t thread;
435 } ALCwinmmCapture;
437 static void ALCwinmmCapture_Construct(ALCwinmmCapture *self, ALCdevice *device);
438 static void ALCwinmmCapture_Destruct(ALCwinmmCapture *self);
440 static void CALLBACK ALCwinmmCapture_waveInProc(HWAVEIN device, UINT msg, DWORD_PTR instance, DWORD_PTR param1, DWORD_PTR param2);
441 static int ALCwinmmCapture_captureProc(void *arg);
443 static ALCenum ALCwinmmCapture_open(ALCwinmmCapture *self, const ALCchar *name);
444 static DECLARE_FORWARD(ALCwinmmCapture, ALCbackend, ALCboolean, reset)
445 static ALCboolean ALCwinmmCapture_start(ALCwinmmCapture *self);
446 static void ALCwinmmCapture_stop(ALCwinmmCapture *self);
447 static ALCenum ALCwinmmCapture_captureSamples(ALCwinmmCapture *self, ALCvoid *buffer, ALCuint samples);
448 static ALCuint ALCwinmmCapture_availableSamples(ALCwinmmCapture *self);
449 static DECLARE_FORWARD(ALCwinmmCapture, ALCbackend, ClockLatency, getClockLatency)
450 static DECLARE_FORWARD(ALCwinmmCapture, ALCbackend, void, lock)
451 static DECLARE_FORWARD(ALCwinmmCapture, ALCbackend, void, unlock)
452 DECLARE_DEFAULT_ALLOCATORS(ALCwinmmCapture)
454 DEFINE_ALCBACKEND_VTABLE(ALCwinmmCapture);
457 static void ALCwinmmCapture_Construct(ALCwinmmCapture *self, ALCdevice *device)
459 ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
460 SET_VTABLE2(ALCwinmmCapture, ALCbackend, self);
462 InitRef(&self->WaveBuffersCommitted, 0);
463 self->InHdl = NULL;
465 ATOMIC_INIT(&self->killNow, AL_TRUE);
468 static void ALCwinmmCapture_Destruct(ALCwinmmCapture *self)
470 void *buffer = NULL;
471 int i;
473 /* Tell the processing thread to quit and wait for it to do so. */
474 if(!ATOMIC_EXCHANGE(&self->killNow, AL_TRUE, almemory_order_acq_rel))
476 PostThreadMessage(self->thread, WM_QUIT, 0, 0);
478 althrd_join(self->thread, &i);
480 /* Make sure capture is stopped and all pending buffers are flushed. */
481 waveInReset(self->InHdl);
483 // Release the wave buffers
484 for(i = 0;i < 4;i++)
486 waveInUnprepareHeader(self->InHdl, &self->WaveBuffer[i], sizeof(WAVEHDR));
487 if(i == 0) buffer = self->WaveBuffer[i].lpData;
488 self->WaveBuffer[i].lpData = NULL;
490 free(buffer);
493 ll_ringbuffer_free(self->Ring);
494 self->Ring = NULL;
496 // Close the Wave device
497 if(self->InHdl)
498 waveInClose(self->InHdl);
499 self->InHdl = 0;
501 ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
505 /* ALCwinmmCapture_waveInProc
507 * Posts a message to 'ALCwinmmCapture_captureProc' everytime a WaveIn Buffer
508 * is completed and returns to the application (with more data).
510 static void CALLBACK ALCwinmmCapture_waveInProc(HWAVEIN UNUSED(device), UINT msg, DWORD_PTR instance, DWORD_PTR param1, DWORD_PTR UNUSED(param2))
512 ALCwinmmCapture *self = (ALCwinmmCapture*)instance;
514 if(msg != WIM_DATA)
515 return;
517 DecrementRef(&self->WaveBuffersCommitted);
518 PostThreadMessage(self->thread, msg, 0, param1);
521 static int ALCwinmmCapture_captureProc(void *arg)
523 ALCwinmmCapture *self = arg;
524 WAVEHDR *WaveHdr;
525 MSG msg;
527 althrd_setname(althrd_current(), RECORD_THREAD_NAME);
529 while(GetMessage(&msg, NULL, 0, 0))
531 if(msg.message != WIM_DATA)
532 continue;
533 /* Don't wait for other buffers to finish before quitting. We're
534 * closing so we don't need them. */
535 if(ATOMIC_LOAD(&self->killNow, almemory_order_acquire))
536 break;
538 WaveHdr = ((WAVEHDR*)msg.lParam);
539 ll_ringbuffer_write(self->Ring, WaveHdr->lpData,
540 WaveHdr->dwBytesRecorded / self->Format.nBlockAlign
543 // Send buffer back to capture more data
544 waveInAddBuffer(self->InHdl, WaveHdr, sizeof(WAVEHDR));
545 IncrementRef(&self->WaveBuffersCommitted);
548 return 0;
552 static ALCenum ALCwinmmCapture_open(ALCwinmmCapture *self, const ALCchar *name)
554 ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
555 const al_string *iter;
556 ALbyte *BufferData = NULL;
557 DWORD CapturedDataSize;
558 ALint BufferSize;
559 UINT DeviceID;
560 MMRESULT res;
561 ALuint i;
563 if(VECTOR_SIZE(CaptureDevices) == 0)
564 ProbeCaptureDevices();
566 // Find the Device ID matching the deviceName if valid
567 #define MATCH_DEVNAME(iter) (!alstr_empty(*(iter)) && (!name || alstr_cmp_cstr(*iter, name) == 0))
568 VECTOR_FIND_IF(iter, const al_string, CaptureDevices, MATCH_DEVNAME);
569 if(iter == VECTOR_END(CaptureDevices))
570 return ALC_INVALID_VALUE;
571 #undef MATCH_DEVNAME
573 DeviceID = (UINT)(iter - VECTOR_BEGIN(CaptureDevices));
575 switch(device->FmtChans)
577 case DevFmtMono:
578 case DevFmtStereo:
579 break;
581 case DevFmtQuad:
582 case DevFmtX51:
583 case DevFmtX51Rear:
584 case DevFmtX61:
585 case DevFmtX71:
586 case DevFmtAmbi3D:
587 return ALC_INVALID_ENUM;
590 switch(device->FmtType)
592 case DevFmtUByte:
593 case DevFmtShort:
594 case DevFmtInt:
595 case DevFmtFloat:
596 break;
598 case DevFmtByte:
599 case DevFmtUShort:
600 case DevFmtUInt:
601 return ALC_INVALID_ENUM;
604 memset(&self->Format, 0, sizeof(WAVEFORMATEX));
605 self->Format.wFormatTag = ((device->FmtType == DevFmtFloat) ?
606 WAVE_FORMAT_IEEE_FLOAT : WAVE_FORMAT_PCM);
607 self->Format.nChannels = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder);
608 self->Format.wBitsPerSample = BytesFromDevFmt(device->FmtType) * 8;
609 self->Format.nBlockAlign = self->Format.wBitsPerSample *
610 self->Format.nChannels / 8;
611 self->Format.nSamplesPerSec = device->Frequency;
612 self->Format.nAvgBytesPerSec = self->Format.nSamplesPerSec *
613 self->Format.nBlockAlign;
614 self->Format.cbSize = 0;
616 if((res=waveInOpen(&self->InHdl, DeviceID, &self->Format, (DWORD_PTR)&ALCwinmmCapture_waveInProc, (DWORD_PTR)self, CALLBACK_FUNCTION)) != MMSYSERR_NOERROR)
618 ERR("waveInOpen failed: %u\n", res);
619 goto failure;
622 // Allocate circular memory buffer for the captured audio
623 CapturedDataSize = device->UpdateSize*device->NumUpdates;
625 // Make sure circular buffer is at least 100ms in size
626 if(CapturedDataSize < (self->Format.nSamplesPerSec / 10))
627 CapturedDataSize = self->Format.nSamplesPerSec / 10;
629 self->Ring = ll_ringbuffer_create(CapturedDataSize, self->Format.nBlockAlign, false);
630 if(!self->Ring) goto failure;
632 InitRef(&self->WaveBuffersCommitted, 0);
634 // Create 4 Buffers of 50ms each
635 BufferSize = self->Format.nAvgBytesPerSec / 20;
636 BufferSize -= (BufferSize % self->Format.nBlockAlign);
638 BufferData = calloc(4, BufferSize);
639 if(!BufferData) goto failure;
641 for(i = 0;i < 4;i++)
643 memset(&self->WaveBuffer[i], 0, sizeof(WAVEHDR));
644 self->WaveBuffer[i].dwBufferLength = BufferSize;
645 self->WaveBuffer[i].lpData = ((i==0) ? (CHAR*)BufferData :
646 (self->WaveBuffer[i-1].lpData +
647 self->WaveBuffer[i-1].dwBufferLength));
648 self->WaveBuffer[i].dwFlags = 0;
649 self->WaveBuffer[i].dwLoops = 0;
650 waveInPrepareHeader(self->InHdl, &self->WaveBuffer[i], sizeof(WAVEHDR));
651 waveInAddBuffer(self->InHdl, &self->WaveBuffer[i], sizeof(WAVEHDR));
652 IncrementRef(&self->WaveBuffersCommitted);
655 ATOMIC_STORE(&self->killNow, AL_FALSE, almemory_order_release);
656 if(althrd_create(&self->thread, ALCwinmmCapture_captureProc, self) != althrd_success)
657 goto failure;
659 alstr_copy(&device->DeviceName, VECTOR_ELEM(CaptureDevices, DeviceID));
660 return ALC_NO_ERROR;
662 failure:
663 if(BufferData)
665 for(i = 0;i < 4;i++)
666 waveInUnprepareHeader(self->InHdl, &self->WaveBuffer[i], sizeof(WAVEHDR));
667 free(BufferData);
670 ll_ringbuffer_free(self->Ring);
671 self->Ring = NULL;
673 if(self->InHdl)
674 waveInClose(self->InHdl);
675 self->InHdl = NULL;
677 return ALC_INVALID_VALUE;
680 static ALCboolean ALCwinmmCapture_start(ALCwinmmCapture *self)
682 waveInStart(self->InHdl);
683 return ALC_TRUE;
686 static void ALCwinmmCapture_stop(ALCwinmmCapture *self)
688 waveInStop(self->InHdl);
691 static ALCenum ALCwinmmCapture_captureSamples(ALCwinmmCapture *self, ALCvoid *buffer, ALCuint samples)
693 ll_ringbuffer_read(self->Ring, buffer, samples);
694 return ALC_NO_ERROR;
697 static ALCuint ALCwinmmCapture_availableSamples(ALCwinmmCapture *self)
699 return (ALCuint)ll_ringbuffer_read_space(self->Ring);
703 static inline void AppendAllDevicesList2(const al_string *name)
705 if(!alstr_empty(*name))
706 AppendAllDevicesList(alstr_get_cstr(*name));
708 static inline void AppendCaptureDeviceList2(const al_string *name)
710 if(!alstr_empty(*name))
711 AppendCaptureDeviceList(alstr_get_cstr(*name));
714 typedef struct ALCwinmmBackendFactory {
715 DERIVE_FROM_TYPE(ALCbackendFactory);
716 } ALCwinmmBackendFactory;
717 #define ALCWINMMBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCwinmmBackendFactory, ALCbackendFactory) } }
719 static ALCboolean ALCwinmmBackendFactory_init(ALCwinmmBackendFactory *self);
720 static void ALCwinmmBackendFactory_deinit(ALCwinmmBackendFactory *self);
721 static ALCboolean ALCwinmmBackendFactory_querySupport(ALCwinmmBackendFactory *self, ALCbackend_Type type);
722 static void ALCwinmmBackendFactory_probe(ALCwinmmBackendFactory *self, enum DevProbe type);
723 static ALCbackend* ALCwinmmBackendFactory_createBackend(ALCwinmmBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
725 DEFINE_ALCBACKENDFACTORY_VTABLE(ALCwinmmBackendFactory);
728 static ALCboolean ALCwinmmBackendFactory_init(ALCwinmmBackendFactory* UNUSED(self))
730 VECTOR_INIT(PlaybackDevices);
731 VECTOR_INIT(CaptureDevices);
733 return ALC_TRUE;
736 static void ALCwinmmBackendFactory_deinit(ALCwinmmBackendFactory* UNUSED(self))
738 clear_devlist(&PlaybackDevices);
739 VECTOR_DEINIT(PlaybackDevices);
741 clear_devlist(&CaptureDevices);
742 VECTOR_DEINIT(CaptureDevices);
745 static ALCboolean ALCwinmmBackendFactory_querySupport(ALCwinmmBackendFactory* UNUSED(self), ALCbackend_Type type)
747 if(type == ALCbackend_Playback || type == ALCbackend_Capture)
748 return ALC_TRUE;
749 return ALC_FALSE;
752 static void ALCwinmmBackendFactory_probe(ALCwinmmBackendFactory* UNUSED(self), enum DevProbe type)
754 switch(type)
756 case ALL_DEVICE_PROBE:
757 ProbePlaybackDevices();
758 VECTOR_FOR_EACH(const al_string, PlaybackDevices, AppendAllDevicesList2);
759 break;
761 case CAPTURE_DEVICE_PROBE:
762 ProbeCaptureDevices();
763 VECTOR_FOR_EACH(const al_string, CaptureDevices, AppendCaptureDeviceList2);
764 break;
768 static ALCbackend* ALCwinmmBackendFactory_createBackend(ALCwinmmBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type)
770 if(type == ALCbackend_Playback)
772 ALCwinmmPlayback *backend;
773 NEW_OBJ(backend, ALCwinmmPlayback)(device);
774 if(!backend) return NULL;
775 return STATIC_CAST(ALCbackend, backend);
777 if(type == ALCbackend_Capture)
779 ALCwinmmCapture *backend;
780 NEW_OBJ(backend, ALCwinmmCapture)(device);
781 if(!backend) return NULL;
782 return STATIC_CAST(ALCbackend, backend);
785 return NULL;
788 ALCbackendFactory *ALCwinmmBackendFactory_getFactory(void)
790 static ALCwinmmBackendFactory factory = ALCWINMMBACKENDFACTORY_INITIALIZER;
791 return STATIC_CAST(ALCbackendFactory, &factory);