2 * OpenAL cross platform audio library
3 * Copyright (C) 2011-2013 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
28 #include <sys/select.h>
29 #include <sys/asoundlib.h>
30 #include <sys/neutrino.h>
41 snd_pcm_channel_setup_t csetup
;
42 snd_pcm_channel_params_t cparams
;
56 TYPEDEF_VECTOR(DevMap
, vector_DevMap
)
58 static vector_DevMap DeviceNameMap
;
59 static vector_DevMap CaptureNameMap
;
61 static const ALCchar qsaDevice
[] = "QSA Default";
66 {SND_PCM_SFMT_FLOAT_LE
},
67 {SND_PCM_SFMT_S32_LE
},
68 {SND_PCM_SFMT_U32_LE
},
69 {SND_PCM_SFMT_S16_LE
},
70 {SND_PCM_SFMT_U16_LE
},
107 static void deviceList(int type
, vector_DevMap
*devmap
)
110 snd_pcm_info_t pcminfo
;
111 int max_cards
, card
, err
, dev
;
114 struct snd_ctl_hw_info info
;
116 max_cards
= snd_cards();
120 VECTOR_RESERVE(*devmap
, max_cards
+1);
121 VECTOR_RESIZE(*devmap
, 0);
123 entry
.name
= strdup(qsaDevice
);
126 VECTOR_PUSH_BACK(*devmap
, entry
);
128 for(card
= 0;card
< max_cards
;card
++)
130 if((err
=snd_ctl_open(&handle
, card
)) < 0)
133 if((err
=snd_ctl_hw_info(handle
, &info
)) < 0)
135 snd_ctl_close(handle
);
139 for(dev
= 0;dev
< (int)info
.pcmdevs
;dev
++)
141 if((err
=snd_ctl_pcm_info(handle
, dev
, &pcminfo
)) < 0)
144 if((type
==SND_PCM_CHANNEL_PLAYBACK
&& (pcminfo
.flags
&SND_PCM_INFO_PLAYBACK
)) ||
145 (type
==SND_PCM_CHANNEL_CAPTURE
&& (pcminfo
.flags
&SND_PCM_INFO_CAPTURE
)))
147 snprintf(name
, sizeof(name
), "%s [%s] (hw:%d,%d)", info
.name
, pcminfo
.name
, card
, dev
);
148 entry
.name
= strdup(name
);
152 VECTOR_PUSH_BACK(*devmap
, entry
);
153 TRACE("Got device \"%s\", card %d, dev %d\n", name
, card
, dev
);
156 snd_ctl_close(handle
);
161 FORCE_ALIGN
static int qsa_proc_playback(void* ptr
)
163 ALCdevice
* device
=(ALCdevice
*)ptr
;
164 qsa_data
* data
=(qsa_data
*)device
->ExtraData
;
167 snd_pcm_channel_status_t status
;
168 struct sched_param param
;
171 struct timeval timeout
;
174 althrd_setname(althrd_current(), MIXER_THREAD_NAME
);
176 /* Increase default 10 priority to 11 to avoid jerky sound */
177 SchedGet(0, 0, ¶m
);
178 param
.sched_priority
=param
.sched_curpriority
+1;
179 SchedSet(0, 0, SCHED_NOCHANGE
, ¶m
);
181 ALint frame_size
=FrameSizeFromDevFmt(device
->FmtChans
, device
->FmtType
);
183 while (!data
->killNow
)
185 ALint len
=data
->size
;
186 write_ptr
=data
->buffer
;
188 avail
=len
/frame_size
;
189 aluMixData(device
, write_ptr
, avail
);
191 while (len
>0 && !data
->killNow
)
194 FD_SET(data
->audio_fd
, &wfds
);
198 /* Select also works like time slice to OS */
199 selectret
=select(data
->audio_fd
+1, NULL
, &wfds
, NULL
, &timeout
);
203 aluHandleDisconnect(device
);
208 if (FD_ISSET(data
->audio_fd
, &wfds
))
215 int wrote
=snd_pcm_plugin_write(data
->pcmHandle
, write_ptr
, len
);
219 if ((errno
==EAGAIN
) || (errno
==EWOULDBLOCK
))
224 memset(&status
, 0, sizeof (status
));
225 status
.channel
=SND_PCM_CHANNEL_PLAYBACK
;
227 snd_pcm_plugin_status(data
->pcmHandle
, &status
);
229 /* we need to reinitialize the sound channel if we've underrun the buffer */
230 if ((status
.status
==SND_PCM_STATUS_UNDERRUN
) ||
231 (status
.status
==SND_PCM_STATUS_READY
))
233 if ((snd_pcm_plugin_prepare(data
->pcmHandle
, SND_PCM_CHANNEL_PLAYBACK
))<0)
235 aluHandleDisconnect(device
);
255 static ALCenum
qsa_open_playback(ALCdevice
* device
, const ALCchar
* deviceName
)
261 data
= (qsa_data
*)calloc(1, sizeof(qsa_data
));
263 return ALC_OUT_OF_MEMORY
;
266 deviceName
= qsaDevice
;
268 if(strcmp(deviceName
, qsaDevice
) == 0)
269 status
= snd_pcm_open_preferred(&data
->pcmHandle
, &card
, &dev
, SND_PCM_OPEN_PLAYBACK
);
274 if(VECTOR_SIZE(DeviceNameMap
) == 0)
275 deviceList(SND_PCM_CHANNEL_PLAYBACK
, &DeviceNameMap
);
277 #define MATCH_DEVNAME(iter) ((iter)->name && strcmp(deviceName, (iter)->name)==0)
278 VECTOR_FIND_IF(iter
, const DevMap
, DeviceNameMap
, MATCH_DEVNAME
);
280 if(iter
== VECTOR_END(DeviceNameMap
))
283 return ALC_INVALID_DEVICE
;
286 status
= snd_pcm_open(&data
->pcmHandle
, iter
->card
, iter
->dev
, SND_PCM_OPEN_PLAYBACK
);
292 return ALC_INVALID_DEVICE
;
295 data
->audio_fd
= snd_pcm_file_descriptor(data
->pcmHandle
, SND_PCM_CHANNEL_PLAYBACK
);
296 if(data
->audio_fd
< 0)
298 snd_pcm_close(data
->pcmHandle
);
300 return ALC_INVALID_DEVICE
;
303 al_string_copy_cstr(&device
->DeviceName
, deviceName
);
304 device
->ExtraData
= data
;
309 static void qsa_close_playback(ALCdevice
* device
)
311 qsa_data
* data
=(qsa_data
*)device
->ExtraData
;
313 if (data
->buffer
!=NULL
)
319 snd_pcm_close(data
->pcmHandle
);
322 device
->ExtraData
=NULL
;
325 static ALCboolean
qsa_reset_playback(ALCdevice
* device
)
327 qsa_data
* data
=(qsa_data
*)device
->ExtraData
;
330 switch(device
->FmtType
)
333 format
=SND_PCM_SFMT_S8
;
336 format
=SND_PCM_SFMT_U8
;
339 format
=SND_PCM_SFMT_S16_LE
;
342 format
=SND_PCM_SFMT_U16_LE
;
345 format
=SND_PCM_SFMT_S32_LE
;
348 format
=SND_PCM_SFMT_U32_LE
;
351 format
=SND_PCM_SFMT_FLOAT_LE
;
355 /* we actually don't want to block on writes */
356 snd_pcm_nonblock_mode(data
->pcmHandle
, 1);
357 /* Disable mmap to control data transfer to the audio device */
358 snd_pcm_plugin_set_disable(data
->pcmHandle
, PLUGIN_DISABLE_MMAP
);
359 snd_pcm_plugin_set_disable(data
->pcmHandle
, PLUGIN_DISABLE_BUFFER_PARTIAL_BLOCKS
);
361 // configure a sound channel
362 memset(&data
->cparams
, 0, sizeof(data
->cparams
));
363 data
->cparams
.channel
=SND_PCM_CHANNEL_PLAYBACK
;
364 data
->cparams
.mode
=SND_PCM_MODE_BLOCK
;
365 data
->cparams
.start_mode
=SND_PCM_START_FULL
;
366 data
->cparams
.stop_mode
=SND_PCM_STOP_STOP
;
368 data
->cparams
.buf
.block
.frag_size
=device
->UpdateSize
*
369 ChannelsFromDevFmt(device
->FmtChans
)*BytesFromDevFmt(device
->FmtType
);
370 data
->cparams
.buf
.block
.frags_max
=device
->NumUpdates
;
371 data
->cparams
.buf
.block
.frags_min
=device
->NumUpdates
;
373 data
->cparams
.format
.interleave
=1;
374 data
->cparams
.format
.rate
=device
->Frequency
;
375 data
->cparams
.format
.voices
=ChannelsFromDevFmt(device
->FmtChans
);
376 data
->cparams
.format
.format
=format
;
378 if ((snd_pcm_plugin_params(data
->pcmHandle
, &data
->cparams
))<0)
380 int original_rate
=data
->cparams
.format
.rate
;
381 int original_voices
=data
->cparams
.format
.voices
;
382 int original_format
=data
->cparams
.format
.format
;
386 for (it
=0; it
<1; it
++)
388 /* Check for second pass */
391 original_rate
=ratelist
[0].rate
;
392 original_voices
=channellist
[0].channels
;
393 original_format
=formatlist
[0].format
;
397 /* At first downgrade sample format */
400 if (formatlist
[jt
].format
==data
->cparams
.format
.format
)
402 data
->cparams
.format
.format
=formatlist
[jt
+1].format
;
405 if (formatlist
[jt
].format
==0)
407 data
->cparams
.format
.format
=0;
413 if (data
->cparams
.format
.format
==0)
415 data
->cparams
.format
.format
=original_format
;
417 /* At secod downgrade sample rate */
420 if (ratelist
[jt
].rate
==data
->cparams
.format
.rate
)
422 data
->cparams
.format
.rate
=ratelist
[jt
+1].rate
;
425 if (ratelist
[jt
].rate
==0)
427 data
->cparams
.format
.rate
=0;
433 if (data
->cparams
.format
.rate
==0)
435 data
->cparams
.format
.rate
=original_rate
;
436 data
->cparams
.format
.format
=original_format
;
438 /* At third downgrade channels number */
441 if(channellist
[jt
].channels
==data
->cparams
.format
.voices
)
443 data
->cparams
.format
.voices
=channellist
[jt
+1].channels
;
446 if (channellist
[jt
].channels
==0)
448 data
->cparams
.format
.voices
=0;
455 if (data
->cparams
.format
.voices
==0)
461 data
->cparams
.buf
.block
.frag_size
=device
->UpdateSize
*
462 data
->cparams
.format
.voices
*
463 snd_pcm_format_width(data
->cparams
.format
.format
)/8;
464 data
->cparams
.buf
.block
.frags_max
=device
->NumUpdates
;
465 data
->cparams
.buf
.block
.frags_min
=device
->NumUpdates
;
466 if ((snd_pcm_plugin_params(data
->pcmHandle
, &data
->cparams
))<0)
476 if (data
->cparams
.format
.voices
!=0)
482 if (data
->cparams
.format
.voices
==0)
488 if ((snd_pcm_plugin_prepare(data
->pcmHandle
, SND_PCM_CHANNEL_PLAYBACK
))<0)
493 memset(&data
->csetup
, 0, sizeof(data
->csetup
));
494 data
->csetup
.channel
=SND_PCM_CHANNEL_PLAYBACK
;
495 if (snd_pcm_plugin_setup(data
->pcmHandle
, &data
->csetup
)<0)
500 /* now fill back to the our AL device */
501 device
->Frequency
=data
->cparams
.format
.rate
;
503 switch (data
->cparams
.format
.voices
)
506 device
->FmtChans
=DevFmtMono
;
509 device
->FmtChans
=DevFmtStereo
;
512 device
->FmtChans
=DevFmtQuad
;
515 device
->FmtChans
=DevFmtX51
;
518 device
->FmtChans
=DevFmtX61
;
521 device
->FmtChans
=DevFmtX71
;
524 device
->FmtChans
=DevFmtMono
;
528 switch (data
->cparams
.format
.format
)
530 case SND_PCM_SFMT_S8
:
531 device
->FmtType
=DevFmtByte
;
533 case SND_PCM_SFMT_U8
:
534 device
->FmtType
=DevFmtUByte
;
536 case SND_PCM_SFMT_S16_LE
:
537 device
->FmtType
=DevFmtShort
;
539 case SND_PCM_SFMT_U16_LE
:
540 device
->FmtType
=DevFmtUShort
;
542 case SND_PCM_SFMT_S32_LE
:
543 device
->FmtType
=DevFmtInt
;
545 case SND_PCM_SFMT_U32_LE
:
546 device
->FmtType
=DevFmtUInt
;
548 case SND_PCM_SFMT_FLOAT_LE
:
549 device
->FmtType
=DevFmtFloat
;
552 device
->FmtType
=DevFmtShort
;
556 SetDefaultChannelOrder(device
);
558 device
->UpdateSize
=data
->csetup
.buf
.block
.frag_size
/
559 (ChannelsFromDevFmt(device
->FmtChans
)*BytesFromDevFmt(device
->FmtType
));
560 device
->NumUpdates
=data
->csetup
.buf
.block
.frags
;
562 data
->size
=data
->csetup
.buf
.block
.frag_size
;
563 data
->buffer
=malloc(data
->size
);
572 static ALCboolean
qsa_start_playback(ALCdevice
* device
)
574 qsa_data
*data
= (qsa_data
*)device
->ExtraData
;
577 if(althrd_create(&data
->thread
, qsa_proc_playback
, device
) != althrd_success
)
583 static void qsa_stop_playback(ALCdevice
* device
)
585 qsa_data
*data
= (qsa_data
*)device
->ExtraData
;
592 althrd_join(data
->thread
, &res
);
599 static ALCenum
qsa_open_capture(ALCdevice
* device
, const ALCchar
* deviceName
)
606 data
=(qsa_data
*)calloc(1, sizeof(qsa_data
));
609 return ALC_OUT_OF_MEMORY
;
613 deviceName
= qsaDevice
;
615 if(strcmp(deviceName
, qsaDevice
) == 0)
616 status
= snd_pcm_open_preferred(&data
->pcmHandle
, &card
, &dev
, SND_PCM_OPEN_CAPTURE
);
621 if(VECTOR_SIZE(CaptureNameMap
) == 0)
622 deviceList(SND_PCM_CHANNEL_CAPTURE
, &CaptureNameMap
);
624 #define MATCH_DEVNAME(iter) ((iter)->name && strcmp(deviceName, (iter)->name)==0)
625 VECTOR_FIND_IF(iter
, const DevMap
, CaptureNameMap
, MATCH_DEVNAME
);
627 if(iter
== VECTOR_END(CaptureNameMap
))
630 return ALC_INVALID_DEVICE
;
633 status
= snd_pcm_open(&data
->pcmHandle
, iter
->card
, iter
->dev
, SND_PCM_OPEN_CAPTURE
);
639 return ALC_INVALID_DEVICE
;
642 data
->audio_fd
= snd_pcm_file_descriptor(data
->pcmHandle
, SND_PCM_CHANNEL_CAPTURE
);
643 if(data
->audio_fd
< 0)
645 snd_pcm_close(data
->pcmHandle
);
647 return ALC_INVALID_DEVICE
;
650 al_string_copy_cstr(&device
->DeviceName
, deviceName
);
651 device
->ExtraData
= data
;
653 switch (device
->FmtType
)
656 format
=SND_PCM_SFMT_S8
;
659 format
=SND_PCM_SFMT_U8
;
662 format
=SND_PCM_SFMT_S16_LE
;
665 format
=SND_PCM_SFMT_U16_LE
;
668 format
=SND_PCM_SFMT_S32_LE
;
671 format
=SND_PCM_SFMT_U32_LE
;
674 format
=SND_PCM_SFMT_FLOAT_LE
;
678 /* we actually don't want to block on reads */
679 snd_pcm_nonblock_mode(data
->pcmHandle
, 1);
680 /* Disable mmap to control data transfer to the audio device */
681 snd_pcm_plugin_set_disable(data
->pcmHandle
, PLUGIN_DISABLE_MMAP
);
683 /* configure a sound channel */
684 memset(&data
->cparams
, 0, sizeof(data
->cparams
));
685 data
->cparams
.mode
=SND_PCM_MODE_BLOCK
;
686 data
->cparams
.channel
=SND_PCM_CHANNEL_CAPTURE
;
687 data
->cparams
.start_mode
=SND_PCM_START_GO
;
688 data
->cparams
.stop_mode
=SND_PCM_STOP_STOP
;
690 data
->cparams
.buf
.block
.frag_size
=device
->UpdateSize
*
691 ChannelsFromDevFmt(device
->FmtChans
)*BytesFromDevFmt(device
->FmtType
);
692 data
->cparams
.buf
.block
.frags_max
=device
->NumUpdates
;
693 data
->cparams
.buf
.block
.frags_min
=device
->NumUpdates
;
695 data
->cparams
.format
.interleave
=1;
696 data
->cparams
.format
.rate
=device
->Frequency
;
697 data
->cparams
.format
.voices
=ChannelsFromDevFmt(device
->FmtChans
);
698 data
->cparams
.format
.format
=format
;
700 if(snd_pcm_plugin_params(data
->pcmHandle
, &data
->cparams
) < 0)
702 snd_pcm_close(data
->pcmHandle
);
704 device
->ExtraData
=NULL
;
706 return ALC_INVALID_VALUE
;
712 static void qsa_close_capture(ALCdevice
* device
)
714 qsa_data
* data
=(qsa_data
*)device
->ExtraData
;
716 if (data
->pcmHandle
!=NULL
)
717 snd_pcm_close(data
->pcmHandle
);
720 device
->ExtraData
=NULL
;
723 static void qsa_start_capture(ALCdevice
* device
)
725 qsa_data
* data
=(qsa_data
*)device
->ExtraData
;
728 if ((rstatus
=snd_pcm_plugin_prepare(data
->pcmHandle
, SND_PCM_CHANNEL_CAPTURE
))<0)
730 ERR("capture prepare failed: %s\n", snd_strerror(rstatus
));
734 memset(&data
->csetup
, 0, sizeof(data
->csetup
));
735 data
->csetup
.channel
=SND_PCM_CHANNEL_CAPTURE
;
736 if ((rstatus
=snd_pcm_plugin_setup(data
->pcmHandle
, &data
->csetup
))<0)
738 ERR("capture setup failed: %s\n", snd_strerror(rstatus
));
742 snd_pcm_capture_go(data
->pcmHandle
);
745 static void qsa_stop_capture(ALCdevice
* device
)
747 qsa_data
* data
=(qsa_data
*)device
->ExtraData
;
749 snd_pcm_capture_flush(data
->pcmHandle
);
752 static ALCuint
qsa_available_samples(ALCdevice
* device
)
754 qsa_data
* data
=(qsa_data
*)device
->ExtraData
;
755 snd_pcm_channel_status_t status
;
756 ALint frame_size
=FrameSizeFromDevFmt(device
->FmtChans
, device
->FmtType
);
760 memset(&status
, 0, sizeof (status
));
761 status
.channel
=SND_PCM_CHANNEL_CAPTURE
;
762 snd_pcm_plugin_status(data
->pcmHandle
, &status
);
763 if ((status
.status
==SND_PCM_STATUS_OVERRUN
) ||
764 (status
.status
==SND_PCM_STATUS_READY
))
766 if ((rstatus
=snd_pcm_plugin_prepare(data
->pcmHandle
, SND_PCM_CHANNEL_CAPTURE
))<0)
768 ERR("capture prepare failed: %s\n", snd_strerror(rstatus
));
769 aluHandleDisconnect(device
);
773 snd_pcm_capture_go(data
->pcmHandle
);
777 free_size
=data
->csetup
.buf
.block
.frag_size
*data
->csetup
.buf
.block
.frags
;
778 free_size
-=status
.free
;
780 return free_size
/frame_size
;
783 static ALCenum
qsa_capture_samples(ALCdevice
*device
, ALCvoid
*buffer
, ALCuint samples
)
785 qsa_data
* data
=(qsa_data
*)device
->ExtraData
;
787 snd_pcm_channel_status_t status
;
790 struct timeval timeout
;
792 ALint frame_size
=FrameSizeFromDevFmt(device
->FmtChans
, device
->FmtType
);
793 ALint len
=samples
*frame_size
;
801 FD_SET(data
->audio_fd
, &rfds
);
805 /* Select also works like time slice to OS */
807 selectret
=select(data
->audio_fd
+1, &rfds
, NULL
, NULL
, &timeout
);
811 aluHandleDisconnect(device
);
812 return ALC_INVALID_DEVICE
;
816 if (FD_ISSET(data
->audio_fd
, &rfds
))
818 bytes_read
=snd_pcm_plugin_read(data
->pcmHandle
, read_ptr
, len
);
826 if ((errno
==EAGAIN
) || (errno
==EWOULDBLOCK
))
831 memset(&status
, 0, sizeof (status
));
832 status
.channel
=SND_PCM_CHANNEL_CAPTURE
;
833 snd_pcm_plugin_status(data
->pcmHandle
, &status
);
835 /* we need to reinitialize the sound channel if we've overrun the buffer */
836 if ((status
.status
==SND_PCM_STATUS_OVERRUN
) ||
837 (status
.status
==SND_PCM_STATUS_READY
))
839 if ((rstatus
=snd_pcm_plugin_prepare(data
->pcmHandle
, SND_PCM_CHANNEL_CAPTURE
))<0)
841 ERR("capture prepare failed: %s\n", snd_strerror(rstatus
));
842 aluHandleDisconnect(device
);
843 return ALC_INVALID_DEVICE
;
845 snd_pcm_capture_go(data
->pcmHandle
);
850 read_ptr
+=bytes_read
;
858 static const BackendFuncs qsa_funcs
= {
869 qsa_available_samples
872 ALCboolean
alc_qsa_init(BackendFuncs
* func_list
)
874 *func_list
= qsa_funcs
;
878 void alc_qsa_deinit(void)
880 #define FREE_NAME(iter) free((iter)->name)
881 VECTOR_FOR_EACH(DevMap
, DeviceNameMap
, FREE_NAME
);
882 VECTOR_DEINIT(DeviceNameMap
);
884 VECTOR_FOR_EACH(DevMap
, CaptureNameMap
, FREE_NAME
);
885 VECTOR_DEINIT(CaptureNameMap
);
889 void alc_qsa_probe(enum DevProbe type
)
893 case ALL_DEVICE_PROBE
:
894 #define FREE_NAME(iter) free((iter)->name)
895 VECTOR_FOR_EACH(DevMap
, DeviceNameMap
, FREE_NAME
);
897 VECTOR_RESIZE(DeviceNameMap
, 0);
899 deviceList(SND_PCM_CHANNEL_PLAYBACK
, &DeviceNameMap
);
900 #define APPEND_DEVICE(iter) AppendAllDevicesList((iter)->name)
901 VECTOR_FOR_EACH(const DevMap
, DeviceNameMap
, APPEND_DEVICE
);
905 case CAPTURE_DEVICE_PROBE
:
906 #define FREE_NAME(iter) free((iter)->name)
907 VECTOR_FOR_EACH(DevMap
, CaptureNameMap
, FREE_NAME
);
909 VECTOR_RESIZE(CaptureNameMap
, 0);
911 deviceList(SND_PCM_CHANNEL_CAPTURE
, &CaptureNameMap
);
912 #define APPEND_DEVICE(iter) AppendCaptureDeviceList((iter)->name)
913 VECTOR_FOR_EACH(const DevMap
, CaptureNameMap
, APPEND_DEVICE
);