Use QString's arg method to format the float value
[openal-soft.git] / Alc / backends / winmm.c
blob2b6db4ad7a66d50d48908f9c5d863da03cecad5e
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 "threads.h"
34 #include "backends/base.h"
36 #ifndef WAVE_FORMAT_IEEE_FLOAT
37 #define WAVE_FORMAT_IEEE_FLOAT 0x0003
38 #endif
40 #define DEVNAME_HEAD "OpenAL Soft on "
43 static vector_al_string PlaybackDevices;
44 static vector_al_string CaptureDevices;
46 static void clear_devlist(vector_al_string *list)
48 VECTOR_FOR_EACH(al_string, *list, al_string_deinit);
49 VECTOR_RESIZE(*list, 0, 0);
53 static void ProbePlaybackDevices(void)
55 ALuint numdevs;
56 ALuint i;
58 clear_devlist(&PlaybackDevices);
60 numdevs = waveOutGetNumDevs();
61 VECTOR_RESIZE(PlaybackDevices, 0, numdevs);
62 for(i = 0;i < numdevs;i++)
64 WAVEOUTCAPSW WaveCaps;
65 const al_string *iter;
66 al_string dname;
68 AL_STRING_INIT(dname);
69 if(waveOutGetDevCapsW(i, &WaveCaps, sizeof(WaveCaps)) == MMSYSERR_NOERROR)
71 ALuint count = 0;
72 while(1)
74 al_string_copy_cstr(&dname, DEVNAME_HEAD);
75 al_string_append_wcstr(&dname, WaveCaps.szPname);
76 if(count != 0)
78 char str[64];
79 snprintf(str, sizeof(str), " #%d", count+1);
80 al_string_append_cstr(&dname, str);
82 count++;
84 #define MATCH_ENTRY(i) (al_string_cmp(dname, *(i)) == 0)
85 VECTOR_FIND_IF(iter, const al_string, PlaybackDevices, MATCH_ENTRY);
86 if(iter == VECTOR_END(PlaybackDevices)) break;
87 #undef MATCH_ENTRY
90 TRACE("Got device \"%s\", ID %u\n", al_string_get_cstr(dname), i);
92 VECTOR_PUSH_BACK(PlaybackDevices, dname);
96 static void ProbeCaptureDevices(void)
98 ALuint numdevs;
99 ALuint i;
101 clear_devlist(&CaptureDevices);
103 numdevs = waveInGetNumDevs();
104 VECTOR_RESIZE(CaptureDevices, 0, numdevs);
105 for(i = 0;i < numdevs;i++)
107 WAVEINCAPSW WaveCaps;
108 const al_string *iter;
109 al_string dname;
111 AL_STRING_INIT(dname);
112 if(waveInGetDevCapsW(i, &WaveCaps, sizeof(WaveCaps)) == MMSYSERR_NOERROR)
114 ALuint count = 0;
115 while(1)
117 al_string_copy_cstr(&dname, DEVNAME_HEAD);
118 al_string_append_wcstr(&dname, WaveCaps.szPname);
119 if(count != 0)
121 char str[64];
122 snprintf(str, sizeof(str), " #%d", count+1);
123 al_string_append_cstr(&dname, str);
125 count++;
127 #define MATCH_ENTRY(i) (al_string_cmp(dname, *(i)) == 0)
128 VECTOR_FIND_IF(iter, const al_string, CaptureDevices, MATCH_ENTRY);
129 if(iter == VECTOR_END(CaptureDevices)) break;
130 #undef MATCH_ENTRY
133 TRACE("Got device \"%s\", ID %u\n", al_string_get_cstr(dname), i);
135 VECTOR_PUSH_BACK(CaptureDevices, dname);
140 typedef struct ALCwinmmPlayback {
141 DERIVE_FROM_TYPE(ALCbackend);
143 RefCount WaveBuffersCommitted;
144 WAVEHDR WaveBuffer[4];
146 HWAVEOUT OutHdl;
148 WAVEFORMATEX Format;
150 volatile ALboolean killNow;
151 althrd_t thread;
152 } ALCwinmmPlayback;
154 static void ALCwinmmPlayback_Construct(ALCwinmmPlayback *self, ALCdevice *device);
155 static void ALCwinmmPlayback_Destruct(ALCwinmmPlayback *self);
157 static void CALLBACK ALCwinmmPlayback_waveOutProc(HWAVEOUT device, UINT msg, DWORD_PTR instance, DWORD_PTR param1, DWORD_PTR param2);
158 static int ALCwinmmPlayback_mixerProc(void *arg);
160 static ALCenum ALCwinmmPlayback_open(ALCwinmmPlayback *self, const ALCchar *name);
161 static void ALCwinmmPlayback_close(ALCwinmmPlayback *self);
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 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(self->killNow)
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) (!al_string_empty(*(iter)) && \
261 (!deviceName || al_string_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 al_string_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 void ALCwinmmPlayback_close(ALCwinmmPlayback* UNUSED(self))
317 static ALCboolean ALCwinmmPlayback_reset(ALCwinmmPlayback *self)
319 ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
321 device->UpdateSize = (ALuint)((ALuint64)device->UpdateSize *
322 self->Format.nSamplesPerSec /
323 device->Frequency);
324 device->UpdateSize = (device->UpdateSize*device->NumUpdates + 3) / 4;
325 device->NumUpdates = 4;
326 device->Frequency = self->Format.nSamplesPerSec;
328 if(self->Format.wFormatTag == WAVE_FORMAT_IEEE_FLOAT)
330 if(self->Format.wBitsPerSample == 32)
331 device->FmtType = DevFmtFloat;
332 else
334 ERR("Unhandled IEEE float sample depth: %d\n", self->Format.wBitsPerSample);
335 return ALC_FALSE;
338 else if(self->Format.wFormatTag == WAVE_FORMAT_PCM)
340 if(self->Format.wBitsPerSample == 16)
341 device->FmtType = DevFmtShort;
342 else if(self->Format.wBitsPerSample == 8)
343 device->FmtType = DevFmtUByte;
344 else
346 ERR("Unhandled PCM sample depth: %d\n", self->Format.wBitsPerSample);
347 return ALC_FALSE;
350 else
352 ERR("Unhandled format tag: 0x%04x\n", self->Format.wFormatTag);
353 return ALC_FALSE;
356 if(self->Format.nChannels == 2)
357 device->FmtChans = DevFmtStereo;
358 else if(self->Format.nChannels == 1)
359 device->FmtChans = DevFmtMono;
360 else
362 ERR("Unhandled channel count: %d\n", self->Format.nChannels);
363 return ALC_FALSE;
365 SetDefaultWFXChannelOrder(device);
367 return ALC_TRUE;
370 static ALCboolean ALCwinmmPlayback_start(ALCwinmmPlayback *self)
372 ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
373 ALbyte *BufferData;
374 ALint BufferSize;
375 ALuint i;
377 self->killNow = AL_FALSE;
378 if(althrd_create(&self->thread, ALCwinmmPlayback_mixerProc, self) != althrd_success)
379 return ALC_FALSE;
381 InitRef(&self->WaveBuffersCommitted, 0);
383 // Create 4 Buffers
384 BufferSize = device->UpdateSize*device->NumUpdates / 4;
385 BufferSize *= FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
387 BufferData = calloc(4, BufferSize);
388 for(i = 0;i < 4;i++)
390 memset(&self->WaveBuffer[i], 0, sizeof(WAVEHDR));
391 self->WaveBuffer[i].dwBufferLength = BufferSize;
392 self->WaveBuffer[i].lpData = ((i==0) ? (CHAR*)BufferData :
393 (self->WaveBuffer[i-1].lpData +
394 self->WaveBuffer[i-1].dwBufferLength));
395 waveOutPrepareHeader(self->OutHdl, &self->WaveBuffer[i], sizeof(WAVEHDR));
396 waveOutWrite(self->OutHdl, &self->WaveBuffer[i], sizeof(WAVEHDR));
397 IncrementRef(&self->WaveBuffersCommitted);
400 return ALC_TRUE;
403 static void ALCwinmmPlayback_stop(ALCwinmmPlayback *self)
405 void *buffer = NULL;
406 int i;
408 if(self->killNow)
409 return;
411 // Set flag to stop processing headers
412 self->killNow = AL_TRUE;
413 althrd_join(self->thread, &i);
415 // Release the wave buffers
416 for(i = 0;i < 4;i++)
418 waveOutUnprepareHeader(self->OutHdl, &self->WaveBuffer[i], sizeof(WAVEHDR));
419 if(i == 0) buffer = self->WaveBuffer[i].lpData;
420 self->WaveBuffer[i].lpData = NULL;
422 free(buffer);
427 typedef struct ALCwinmmCapture {
428 DERIVE_FROM_TYPE(ALCbackend);
430 RefCount WaveBuffersCommitted;
431 WAVEHDR WaveBuffer[4];
433 HWAVEIN InHdl;
435 ll_ringbuffer_t *Ring;
437 WAVEFORMATEX Format;
439 volatile ALboolean killNow;
440 althrd_t thread;
441 } ALCwinmmCapture;
443 static void ALCwinmmCapture_Construct(ALCwinmmCapture *self, ALCdevice *device);
444 static void ALCwinmmCapture_Destruct(ALCwinmmCapture *self);
446 static void CALLBACK ALCwinmmCapture_waveInProc(HWAVEIN device, UINT msg, DWORD_PTR instance, DWORD_PTR param1, DWORD_PTR param2);
447 static int ALCwinmmCapture_captureProc(void *arg);
449 static ALCenum ALCwinmmCapture_open(ALCwinmmCapture *self, const ALCchar *name);
450 static void ALCwinmmCapture_close(ALCwinmmCapture *self);
451 static DECLARE_FORWARD(ALCwinmmCapture, ALCbackend, ALCboolean, reset)
452 static ALCboolean ALCwinmmCapture_start(ALCwinmmCapture *self);
453 static void ALCwinmmCapture_stop(ALCwinmmCapture *self);
454 static ALCenum ALCwinmmCapture_captureSamples(ALCwinmmCapture *self, ALCvoid *buffer, ALCuint samples);
455 static ALCuint ALCwinmmCapture_availableSamples(ALCwinmmCapture *self);
456 static DECLARE_FORWARD(ALCwinmmCapture, ALCbackend, ClockLatency, getClockLatency)
457 static DECLARE_FORWARD(ALCwinmmCapture, ALCbackend, void, lock)
458 static DECLARE_FORWARD(ALCwinmmCapture, ALCbackend, void, unlock)
459 DECLARE_DEFAULT_ALLOCATORS(ALCwinmmCapture)
461 DEFINE_ALCBACKEND_VTABLE(ALCwinmmCapture);
464 static void ALCwinmmCapture_Construct(ALCwinmmCapture *self, ALCdevice *device)
466 ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
467 SET_VTABLE2(ALCwinmmCapture, ALCbackend, self);
469 InitRef(&self->WaveBuffersCommitted, 0);
470 self->InHdl = NULL;
472 self->killNow = AL_TRUE;
475 static void ALCwinmmCapture_Destruct(ALCwinmmCapture *self)
477 if(self->InHdl)
478 waveInClose(self->InHdl);
479 self->InHdl = 0;
481 ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
485 /* ALCwinmmCapture_waveInProc
487 * Posts a message to 'ALCwinmmCapture_captureProc' everytime a WaveIn Buffer
488 * is completed and returns to the application (with more data).
490 static void CALLBACK ALCwinmmCapture_waveInProc(HWAVEIN UNUSED(device), UINT msg, DWORD_PTR instance, DWORD_PTR param1, DWORD_PTR UNUSED(param2))
492 ALCwinmmCapture *self = (ALCwinmmCapture*)instance;
494 if(msg != WIM_DATA)
495 return;
497 DecrementRef(&self->WaveBuffersCommitted);
498 PostThreadMessage(self->thread, msg, 0, param1);
501 static int ALCwinmmCapture_captureProc(void *arg)
503 ALCwinmmCapture *self = arg;
504 WAVEHDR *WaveHdr;
505 MSG msg;
507 althrd_setname(althrd_current(), RECORD_THREAD_NAME);
509 while(GetMessage(&msg, NULL, 0, 0))
511 if(msg.message != WIM_DATA)
512 continue;
513 /* Don't wait for other buffers to finish before quitting. We're
514 * closing so we don't need them. */
515 if(self->killNow)
516 break;
518 WaveHdr = ((WAVEHDR*)msg.lParam);
519 ll_ringbuffer_write(self->Ring, WaveHdr->lpData,
520 WaveHdr->dwBytesRecorded / self->Format.nBlockAlign
523 // Send buffer back to capture more data
524 waveInAddBuffer(self->InHdl, WaveHdr, sizeof(WAVEHDR));
525 IncrementRef(&self->WaveBuffersCommitted);
528 return 0;
532 static ALCenum ALCwinmmCapture_open(ALCwinmmCapture *self, const ALCchar *name)
534 ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
535 const al_string *iter;
536 ALbyte *BufferData = NULL;
537 DWORD CapturedDataSize;
538 ALint BufferSize;
539 UINT DeviceID;
540 MMRESULT res;
541 ALuint i;
543 if(VECTOR_SIZE(CaptureDevices) == 0)
544 ProbeCaptureDevices();
546 // Find the Device ID matching the deviceName if valid
547 #define MATCH_DEVNAME(iter) (!al_string_empty(*(iter)) && (!name || al_string_cmp_cstr(*iter, name) == 0))
548 VECTOR_FIND_IF(iter, const al_string, CaptureDevices, MATCH_DEVNAME);
549 if(iter == VECTOR_END(CaptureDevices))
550 return ALC_INVALID_VALUE;
551 #undef MATCH_DEVNAME
553 DeviceID = (UINT)(iter - VECTOR_BEGIN(CaptureDevices));
555 switch(device->FmtChans)
557 case DevFmtMono:
558 case DevFmtStereo:
559 break;
561 case DevFmtQuad:
562 case DevFmtX51:
563 case DevFmtX51Rear:
564 case DevFmtX61:
565 case DevFmtX71:
566 case DevFmtAmbi1:
567 case DevFmtAmbi2:
568 case DevFmtAmbi3:
569 return ALC_INVALID_ENUM;
572 switch(device->FmtType)
574 case DevFmtUByte:
575 case DevFmtShort:
576 case DevFmtInt:
577 case DevFmtFloat:
578 break;
580 case DevFmtByte:
581 case DevFmtUShort:
582 case DevFmtUInt:
583 return ALC_INVALID_ENUM;
586 memset(&self->Format, 0, sizeof(WAVEFORMATEX));
587 self->Format.wFormatTag = ((device->FmtType == DevFmtFloat) ?
588 WAVE_FORMAT_IEEE_FLOAT : WAVE_FORMAT_PCM);
589 self->Format.nChannels = ChannelsFromDevFmt(device->FmtChans);
590 self->Format.wBitsPerSample = BytesFromDevFmt(device->FmtType) * 8;
591 self->Format.nBlockAlign = self->Format.wBitsPerSample *
592 self->Format.nChannels / 8;
593 self->Format.nSamplesPerSec = device->Frequency;
594 self->Format.nAvgBytesPerSec = self->Format.nSamplesPerSec *
595 self->Format.nBlockAlign;
596 self->Format.cbSize = 0;
598 if((res=waveInOpen(&self->InHdl, DeviceID, &self->Format, (DWORD_PTR)&ALCwinmmCapture_waveInProc, (DWORD_PTR)self, CALLBACK_FUNCTION)) != MMSYSERR_NOERROR)
600 ERR("waveInOpen failed: %u\n", res);
601 goto failure;
604 // Allocate circular memory buffer for the captured audio
605 CapturedDataSize = device->UpdateSize*device->NumUpdates;
607 // Make sure circular buffer is at least 100ms in size
608 if(CapturedDataSize < (self->Format.nSamplesPerSec / 10))
609 CapturedDataSize = self->Format.nSamplesPerSec / 10;
611 self->Ring = ll_ringbuffer_create(CapturedDataSize+1, self->Format.nBlockAlign);
612 if(!self->Ring) goto failure;
614 InitRef(&self->WaveBuffersCommitted, 0);
616 // Create 4 Buffers of 50ms each
617 BufferSize = self->Format.nAvgBytesPerSec / 20;
618 BufferSize -= (BufferSize % self->Format.nBlockAlign);
620 BufferData = calloc(4, BufferSize);
621 if(!BufferData) goto failure;
623 for(i = 0;i < 4;i++)
625 memset(&self->WaveBuffer[i], 0, sizeof(WAVEHDR));
626 self->WaveBuffer[i].dwBufferLength = BufferSize;
627 self->WaveBuffer[i].lpData = ((i==0) ? (CHAR*)BufferData :
628 (self->WaveBuffer[i-1].lpData +
629 self->WaveBuffer[i-1].dwBufferLength));
630 self->WaveBuffer[i].dwFlags = 0;
631 self->WaveBuffer[i].dwLoops = 0;
632 waveInPrepareHeader(self->InHdl, &self->WaveBuffer[i], sizeof(WAVEHDR));
633 waveInAddBuffer(self->InHdl, &self->WaveBuffer[i], sizeof(WAVEHDR));
634 IncrementRef(&self->WaveBuffersCommitted);
637 self->killNow = AL_FALSE;
638 if(althrd_create(&self->thread, ALCwinmmCapture_captureProc, self) != althrd_success)
639 goto failure;
641 al_string_copy(&device->DeviceName, VECTOR_ELEM(CaptureDevices, DeviceID));
642 return ALC_NO_ERROR;
644 failure:
645 if(BufferData)
647 for(i = 0;i < 4;i++)
648 waveInUnprepareHeader(self->InHdl, &self->WaveBuffer[i], sizeof(WAVEHDR));
649 free(BufferData);
652 ll_ringbuffer_free(self->Ring);
653 self->Ring = NULL;
655 if(self->InHdl)
656 waveInClose(self->InHdl);
657 self->InHdl = NULL;
659 return ALC_INVALID_VALUE;
662 static void ALCwinmmCapture_close(ALCwinmmCapture *self)
664 void *buffer = NULL;
665 int i;
667 /* Tell the processing thread to quit and wait for it to do so. */
668 self->killNow = AL_TRUE;
669 PostThreadMessage(self->thread, WM_QUIT, 0, 0);
671 althrd_join(self->thread, &i);
673 /* Make sure capture is stopped and all pending buffers are flushed. */
674 waveInReset(self->InHdl);
676 // Release the wave buffers
677 for(i = 0;i < 4;i++)
679 waveInUnprepareHeader(self->InHdl, &self->WaveBuffer[i], sizeof(WAVEHDR));
680 if(i == 0) buffer = self->WaveBuffer[i].lpData;
681 self->WaveBuffer[i].lpData = NULL;
683 free(buffer);
685 ll_ringbuffer_free(self->Ring);
686 self->Ring = NULL;
688 // Close the Wave device
689 waveInClose(self->InHdl);
690 self->InHdl = NULL;
693 static ALCboolean ALCwinmmCapture_start(ALCwinmmCapture *self)
695 waveInStart(self->InHdl);
696 return ALC_TRUE;
699 static void ALCwinmmCapture_stop(ALCwinmmCapture *self)
701 waveInStop(self->InHdl);
704 static ALCenum ALCwinmmCapture_captureSamples(ALCwinmmCapture *self, ALCvoid *buffer, ALCuint samples)
706 ll_ringbuffer_read(self->Ring, buffer, samples);
707 return ALC_NO_ERROR;
710 static ALCuint ALCwinmmCapture_availableSamples(ALCwinmmCapture *self)
712 return ll_ringbuffer_read_space(self->Ring);
716 static inline void AppendAllDevicesList2(const al_string *name)
718 if(!al_string_empty(*name))
719 AppendAllDevicesList(al_string_get_cstr(*name));
721 static inline void AppendCaptureDeviceList2(const al_string *name)
723 if(!al_string_empty(*name))
724 AppendCaptureDeviceList(al_string_get_cstr(*name));
727 typedef struct ALCwinmmBackendFactory {
728 DERIVE_FROM_TYPE(ALCbackendFactory);
729 } ALCwinmmBackendFactory;
730 #define ALCWINMMBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCwinmmBackendFactory, ALCbackendFactory) } }
732 static ALCboolean ALCwinmmBackendFactory_init(ALCwinmmBackendFactory *self);
733 static void ALCwinmmBackendFactory_deinit(ALCwinmmBackendFactory *self);
734 static ALCboolean ALCwinmmBackendFactory_querySupport(ALCwinmmBackendFactory *self, ALCbackend_Type type);
735 static void ALCwinmmBackendFactory_probe(ALCwinmmBackendFactory *self, enum DevProbe type);
736 static ALCbackend* ALCwinmmBackendFactory_createBackend(ALCwinmmBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
738 DEFINE_ALCBACKENDFACTORY_VTABLE(ALCwinmmBackendFactory);
741 static ALCboolean ALCwinmmBackendFactory_init(ALCwinmmBackendFactory* UNUSED(self))
743 VECTOR_INIT(PlaybackDevices);
744 VECTOR_INIT(CaptureDevices);
746 return ALC_TRUE;
749 static void ALCwinmmBackendFactory_deinit(ALCwinmmBackendFactory* UNUSED(self))
751 clear_devlist(&PlaybackDevices);
752 VECTOR_DEINIT(PlaybackDevices);
754 clear_devlist(&CaptureDevices);
755 VECTOR_DEINIT(CaptureDevices);
758 static ALCboolean ALCwinmmBackendFactory_querySupport(ALCwinmmBackendFactory* UNUSED(self), ALCbackend_Type type)
760 if(type == ALCbackend_Playback || type == ALCbackend_Capture)
761 return ALC_TRUE;
762 return ALC_FALSE;
765 static void ALCwinmmBackendFactory_probe(ALCwinmmBackendFactory* UNUSED(self), enum DevProbe type)
767 switch(type)
769 case ALL_DEVICE_PROBE:
770 ProbePlaybackDevices();
771 VECTOR_FOR_EACH(const al_string, PlaybackDevices, AppendAllDevicesList2);
772 break;
774 case CAPTURE_DEVICE_PROBE:
775 ProbeCaptureDevices();
776 VECTOR_FOR_EACH(const al_string, CaptureDevices, AppendCaptureDeviceList2);
777 break;
781 static ALCbackend* ALCwinmmBackendFactory_createBackend(ALCwinmmBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type)
783 if(type == ALCbackend_Playback)
785 ALCwinmmPlayback *backend;
786 NEW_OBJ(backend, ALCwinmmPlayback)(device);
787 if(!backend) return NULL;
788 return STATIC_CAST(ALCbackend, backend);
790 if(type == ALCbackend_Capture)
792 ALCwinmmCapture *backend;
793 NEW_OBJ(backend, ALCwinmmCapture)(device);
794 if(!backend) return NULL;
795 return STATIC_CAST(ALCbackend, backend);
798 return NULL;
801 ALCbackendFactory *ALCwinmmBackendFactory_getFactory(void)
803 static ALCwinmmBackendFactory factory = ALCWINMMBACKENDFACTORY_INITIALIZER;
804 return STATIC_CAST(ALCbackendFactory, &factory);