4 * Copyright (c) 2016 Samsung Electronics Co., Ltd. All rights reserved.
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
28 #include "tizen-audio-internal.h"
29 #include "tizen-audio-impl.h"
31 #ifndef __USE_TINYALSA__
32 #define DEVICE_NAME_MAX 32
35 #ifdef __USE_TINYALSA__
36 /* Convert pcm format from pulse to alsa */
37 static const uint32_t g_format_convert_table[] = {
38 [AUDIO_SAMPLE_U8] = PCM_FORMAT_S8,
39 [AUDIO_SAMPLE_S16LE] = PCM_FORMAT_S16_LE,
40 [AUDIO_SAMPLE_S32LE] = PCM_FORMAT_S32_LE,
41 [AUDIO_SAMPLE_S24_32LE] = PCM_FORMAT_S24_LE
44 /* FIXME : To avoid build warning... */
45 int _snd_pcm_poll_descriptor(snd_pcm_t *pcm);
46 /* Convert pcm format from pulse to alsa */
47 static const uint32_t g_format_convert_table[] = {
48 [AUDIO_SAMPLE_U8] = SND_PCM_FORMAT_U8,
49 [AUDIO_SAMPLE_ALAW] = SND_PCM_FORMAT_A_LAW,
50 [AUDIO_SAMPLE_ULAW] = SND_PCM_FORMAT_MU_LAW,
51 [AUDIO_SAMPLE_S16LE] = SND_PCM_FORMAT_S16_LE,
52 [AUDIO_SAMPLE_S16BE] = SND_PCM_FORMAT_S16_BE,
53 [AUDIO_SAMPLE_FLOAT32LE] = SND_PCM_FORMAT_FLOAT_LE,
54 [AUDIO_SAMPLE_FLOAT32BE] = SND_PCM_FORMAT_FLOAT_BE,
55 [AUDIO_SAMPLE_S32LE] = SND_PCM_FORMAT_S32_LE,
56 [AUDIO_SAMPLE_S32BE] = SND_PCM_FORMAT_S32_BE,
57 [AUDIO_SAMPLE_S24LE] = SND_PCM_FORMAT_S24_3LE,
58 [AUDIO_SAMPLE_S24BE] = SND_PCM_FORMAT_S24_3BE,
59 [AUDIO_SAMPLE_S24_32LE] = SND_PCM_FORMAT_S24_LE,
60 [AUDIO_SAMPLE_S24_32BE] = SND_PCM_FORMAT_S24_BE
64 static uint32_t __convert_format(audio_sample_format_s format)
66 return g_format_convert_table[format];
69 #ifdef __USE_TINYALSA__
70 static int __parse_card_device_number(const char *card, const char *device, unsigned int *card_u, unsigned int *device_u) {
71 AUDIO_RETURN_VAL_IF_FAIL(card, AUDIO_ERR_PARAMETER);
72 AUDIO_RETURN_VAL_IF_FAIL(device, AUDIO_ERR_PARAMETER);
73 AUDIO_RETURN_VAL_IF_FAIL(card_u, AUDIO_ERR_PARAMETER);
74 AUDIO_RETURN_VAL_IF_FAIL(device_u, AUDIO_ERR_PARAMETER);
76 AUDIO_LOG_DEBUG("card : %s, device : %s", card, device);
78 *card_u = (unsigned int) strtol(card, NULL, 10);
79 *device_u = (unsigned int) strtol(device, NULL, 10);
84 static struct pcm *__tinyalsa_open_device(const char *card, const char *device, audio_pcm_sample_spec_s *ss, size_t period_size, size_t period_count, uint32_t direction)
86 struct pcm *pcm = NULL;
87 struct pcm_config config;
88 unsigned int card_u, device_u;
90 AUDIO_RETURN_NULL_IF_FAIL(device);
91 AUDIO_RETURN_NULL_IF_FAIL(ss);
93 config.channels = ss->channels;
94 config.rate = ss->rate;
95 config.period_size = period_size;
96 config.period_count = period_count;
97 config.format = ss->format;
98 config.start_threshold = period_size;
99 config.stop_threshold = 0xFFFFFFFF;
100 config.silence_threshold = 0;
102 AUDIO_LOG_INFO("card %s, device %s, direction %d, channels %d, rate %d, format %d, period_size %d, period_count %d",
103 card, device, direction, ss->channels, ss->rate, ss->format, period_size, period_count);
105 if (__parse_card_device_number(card, device, &card_u, &device_u) < 0) {
106 AUDIO_LOG_ERROR("Failed to get card device number from %s", device);
110 pcm = pcm_open(card_u, device_u, (direction == AUDIO_DIRECTION_OUT) ? PCM_OUT : PCM_IN, &config);
111 if (!pcm || !pcm_is_ready(pcm)) {
112 AUDIO_LOG_ERROR("Unable to open device (%s)", pcm_get_error(pcm));
120 static int __tinyalsa_pcm_recover(struct pcm *pcm, int err)
124 if (err == -EINTR) /* nothing to do, continue */
127 AUDIO_LOG_INFO("XRUN occurred");
128 err = pcm_prepare(pcm);
130 AUDIO_LOG_ERROR("Could not recover from XRUN occurred, prepare failed : %d", err);
135 if (err == -ESTRPIPE) {
136 /* tinyalsa does not support pcm resume, dont't care suspend case */
137 AUDIO_LOG_ERROR("Could not recover from suspend : %d", err);
144 #ifndef __USE_TINYALSA__
145 static int __make_alsa_device_name(const char *card, const char *device, char device_name[])
147 AUDIO_RETURN_VAL_IF_FAIL(card, AUDIO_ERR_PARAMETER);
148 AUDIO_RETURN_VAL_IF_FAIL(device, AUDIO_ERR_PARAMETER);
149 AUDIO_RETURN_VAL_IF_FAIL(device_name, AUDIO_ERR_PARAMETER);
151 snprintf(device_name, DEVICE_NAME_MAX, "hw:%s,%s", card, device);
156 audio_return_e _pcm_open(const char *card, const char *device, uint32_t direction, void *sample_spec,
157 uint32_t period_size, uint32_t periods, void **pcm_handle)
161 AUDIO_RETURN_VAL_IF_FAIL(card, AUDIO_ERR_PARAMETER);
162 AUDIO_RETURN_VAL_IF_FAIL(device, AUDIO_ERR_PARAMETER);
163 AUDIO_RETURN_VAL_IF_FAIL((direction == AUDIO_DIRECTION_OUT) || (direction == AUDIO_DIRECTION_IN),
164 AUDIO_ERR_PARAMETER);
166 AUDIO_LOG_INFO("card(%s) device(%s) direction(%u) period_size(%u) periods(%u)",
167 card, device, direction, period_size, periods);
168 #ifdef __USE_TINYALSA__
169 audio_pcm_sample_spec_s *ss;
171 ss = (audio_pcm_sample_spec_s *)sample_spec;
172 ss->format = __convert_format((audio_sample_format_s)ss->format);
174 *pcm_handle = __tinyalsa_open_device(card, device, ss, (size_t)period_size, (size_t)periods, direction);
175 if (*pcm_handle == NULL) {
176 AUDIO_LOG_ERROR("Error opening PCM device");
177 return AUDIO_ERR_RESOURCE;
180 if ((err = pcm_prepare((struct pcm *)*pcm_handle)) != 0) {
181 AUDIO_LOG_ERROR("Error prepare PCM device : %d", err);
187 char device_name[DEVICE_NAME_MAX];
189 __make_alsa_device_name(card, device, device_name);
190 mode = SND_PCM_NONBLOCK | SND_PCM_NO_AUTO_RESAMPLE | SND_PCM_NO_AUTO_CHANNELS | SND_PCM_NO_AUTO_FORMAT;
192 if ((err = snd_pcm_open((snd_pcm_t **)pcm_handle, device_name, (direction == AUDIO_DIRECTION_OUT) ? SND_PCM_STREAM_PLAYBACK : SND_PCM_STREAM_CAPTURE, mode)) < 0) {
193 AUDIO_LOG_ERROR("Error opening PCM device %s : %s", device_name, snd_strerror(err));
194 return AUDIO_ERR_RESOURCE;
197 if ((ret = _pcm_set_params(*pcm_handle, direction, sample_spec, period_size, periods)) != AUDIO_RET_OK) {
198 AUDIO_LOG_ERROR("Failed to set pcm parameters : %d", ret);
202 AUDIO_LOG_INFO("PCM device %s", device_name);
208 audio_return_e _pcm_start(void *pcm_handle)
212 #ifdef __USE_TINYALSA__
213 if ((err = pcm_start(pcm_handle)) < 0) {
214 AUDIO_LOG_ERROR("Error starting PCM handle : %d", err);
215 return AUDIO_ERR_RESOURCE;
218 if ((err = snd_pcm_start(pcm_handle)) < 0) {
219 AUDIO_LOG_ERROR("Error starting PCM handle : %s", snd_strerror(err));
220 return AUDIO_ERR_RESOURCE;
224 AUDIO_LOG_INFO("PCM handle %p start", pcm_handle);
228 audio_return_e _pcm_stop(void *pcm_handle)
232 #ifdef __USE_TINYALSA__
233 if ((err = pcm_stop(pcm_handle)) < 0) {
234 AUDIO_LOG_ERROR("Error stopping PCM handle : %d", err);
235 return AUDIO_ERR_RESOURCE;
238 if ((err = snd_pcm_drop(pcm_handle)) < 0) {
239 AUDIO_LOG_ERROR("Error stopping PCM handle : %s", snd_strerror(err));
240 return AUDIO_ERR_RESOURCE;
244 AUDIO_LOG_INFO("PCM handle %p stop", pcm_handle);
248 audio_return_e _pcm_close(void *pcm_handle)
252 AUDIO_LOG_INFO("Try to close PCM handle %p", pcm_handle);
254 #ifdef __USE_TINYALSA__
255 if ((err = pcm_close(pcm_handle)) < 0) {
256 AUDIO_LOG_ERROR("Error closing PCM handle : %d", err);
257 return AUDIO_ERR_RESOURCE;
260 if ((err = snd_pcm_close(pcm_handle)) < 0) {
261 AUDIO_LOG_ERROR("Error closing PCM handle : %s", snd_strerror(err));
262 return AUDIO_ERR_RESOURCE;
269 audio_return_e _pcm_avail(void *pcm_handle, uint32_t *avail)
271 #ifdef __USE_TINYALSA__
272 struct timespec tspec;
273 unsigned int frames_avail = 0;
276 err = pcm_get_htimestamp(pcm_handle, &frames_avail, &tspec);
278 AUDIO_LOG_ERROR("Could not get avail and timespec at PCM handle %p : %d", pcm_handle, err);
279 return AUDIO_ERR_IOCTL;
283 AUDIO_LOG_DEBUG("avail = %d", frames_avail);
286 *avail = (uint32_t)frames_avail;
288 snd_pcm_sframes_t frames_avail;
290 if ((frames_avail = snd_pcm_avail(pcm_handle)) < 0) {
291 AUDIO_LOG_ERROR("Could not get avail at PCM handle %p : %ld", pcm_handle, frames_avail);
292 return AUDIO_ERR_IOCTL;
296 AUDIO_LOG_DEBUG("avail = %d", frames_avail);
299 *avail = (uint32_t)frames_avail;
305 audio_return_e _pcm_write(void *pcm_handle, const void *buffer, uint32_t frames)
307 #ifdef __USE_TINYALSA__
310 err = pcm_write(pcm_handle, buffer, pcm_frames_to_bytes(pcm_handle, (unsigned int)frames));
312 AUDIO_LOG_ERROR("Failed to write pcm : %d", err);
313 return AUDIO_ERR_IOCTL;
317 AUDIO_LOG_DEBUG("_pcm_write = %d", frames);
320 snd_pcm_sframes_t frames_written;
322 AUDIO_RETURN_VAL_IF_FAIL(pcm_handle, AUDIO_ERR_PARAMETER);
324 frames_written = snd_pcm_writei(pcm_handle, buffer, (snd_pcm_uframes_t) frames);
325 if (frames_written < 0) {
326 AUDIO_LOG_ERROR("Failed to write pcm : %ld", frames_written);
327 return AUDIO_ERR_IOCTL;
331 AUDIO_LOG_DEBUG("_pcm_write = (%d / %d)", frames_written, frames);
338 audio_return_e _pcm_read(void *pcm_handle, void *buffer, uint32_t frames)
340 #ifdef __USE_TINYALSA__
343 err = pcm_read(pcm_handle, buffer, pcm_frames_to_bytes(pcm_handle, (unsigned int)frames));
345 AUDIO_LOG_ERROR("Failed to read pcm : %d", err);
346 return AUDIO_ERR_IOCTL;
350 AUDIO_LOG_DEBUG("audio_pcm_read = %d", frames);
353 snd_pcm_sframes_t frames_read;
355 frames_read = snd_pcm_readi(pcm_handle, buffer, (snd_pcm_uframes_t)frames);
356 if (frames_read < 0) {
357 AUDIO_LOG_ERROR("Failed to read pcm : %ld", frames_read);
358 return AUDIO_ERR_IOCTL;
362 AUDIO_LOG_DEBUG("_pcm_read = (%d / %d)", frames_read, frames);
369 audio_return_e _pcm_get_fd(void *pcm_handle, int *fd)
371 /* we use an internal API of the (tiny)alsa library, so it causes warning message during compile */
372 #ifdef __USE_TINYALSA__
373 *fd = _pcm_poll_descriptor((struct pcm *)pcm_handle);
375 *fd = _snd_pcm_poll_descriptor((snd_pcm_t *)pcm_handle);
380 audio_return_e _pcm_recover(void *pcm_handle, int revents)
384 AUDIO_RETURN_VAL_IF_FAIL(pcm_handle, AUDIO_ERR_PARAMETER);
386 if (revents & POLLERR)
387 AUDIO_LOG_DEBUG("Got POLLERR from ALSA");
388 if (revents & POLLNVAL)
389 AUDIO_LOG_DEBUG("Got POLLNVAL from ALSA");
390 if (revents & POLLHUP)
391 AUDIO_LOG_DEBUG("Got POLLHUP from ALSA");
392 if (revents & POLLPRI)
393 AUDIO_LOG_DEBUG("Got POLLPRI from ALSA");
394 if (revents & POLLIN)
395 AUDIO_LOG_DEBUG("Got POLLIN from ALSA");
396 if (revents & POLLOUT)
397 AUDIO_LOG_DEBUG("Got POLLOUT from ALSA");
399 #ifdef __USE_TINYALSA__
400 state = pcm_state(pcm_handle);
401 AUDIO_LOG_DEBUG("PCM state is %d", state);
405 if ((err = __tinyalsa_pcm_recover(pcm_handle, -EPIPE)) != 0) {
406 AUDIO_LOG_ERROR("Could not recover from POLLERR|POLLNVAL|POLLHUP and XRUN : %d", err);
407 return AUDIO_ERR_IOCTL;
411 case PCM_STATE_SUSPENDED:
412 if ((err = __tinyalsa_pcm_recover(pcm_handle, -ESTRPIPE)) != 0) {
413 AUDIO_LOG_ERROR("Could not recover from POLLERR|POLLNVAL|POLLHUP and SUSPENDED : %d", err);
414 return AUDIO_ERR_IOCTL;
419 pcm_stop(pcm_handle);
420 if ((err = pcm_prepare(pcm_handle)) < 0) {
421 AUDIO_LOG_ERROR("Could not recover from POLLERR|POLLNVAL|POLLHUP with pcm_prepare() : %d", err);
422 return AUDIO_ERR_IOCTL;
426 state = snd_pcm_state(pcm_handle);
427 AUDIO_LOG_DEBUG("PCM state is %s", snd_pcm_state_name(state));
429 /* Try to recover from this error */
432 case SND_PCM_STATE_XRUN:
433 if ((err = snd_pcm_recover(pcm_handle, -EPIPE, 1)) != 0) {
434 AUDIO_LOG_ERROR("Could not recover from POLLERR|POLLNVAL|POLLHUP and XRUN : %d", err);
435 return AUDIO_ERR_IOCTL;
439 case SND_PCM_STATE_SUSPENDED:
440 if ((err = snd_pcm_recover(pcm_handle, -ESTRPIPE, 1)) != 0) {
441 AUDIO_LOG_ERROR("Could not recover from POLLERR|POLLNVAL|POLLHUP and SUSPENDED : %d", err);
442 return AUDIO_ERR_IOCTL;
447 snd_pcm_drop(pcm_handle);
448 if ((err = snd_pcm_prepare(pcm_handle)) < 0) {
449 AUDIO_LOG_ERROR("Could not recover from POLLERR|POLLNVAL|POLLHUP with snd_pcm_prepare() : %d", err);
450 return AUDIO_ERR_IOCTL;
456 AUDIO_LOG_DEBUG("_pcm_recover");
460 audio_return_e _pcm_get_params(void *pcm_handle, uint32_t direction, void **sample_spec, uint32_t *period_size, uint32_t *periods)
462 #ifdef __USE_TINYALSA__
463 audio_pcm_sample_spec_s *ss;
464 unsigned int _period_size, _buffer_size, _periods, _format, _rate, _channels;
465 unsigned int _start_threshold, _stop_threshold, _silence_threshold;
466 struct pcm_config *config;
468 ss = (audio_pcm_sample_spec_s *)*sample_spec;
470 /* we use an internal API of the tiny alsa library, so it causes warning message during compile */
471 _pcm_config(pcm_handle, &config);
473 *period_size = config->period_size;
474 *periods = config->period_count;
475 _buffer_size = config->period_size * config->period_count;
476 ss->format = config->format;
477 ss->rate = config->rate;
478 ss->channels = config->channels;
479 _start_threshold = config->start_threshold;
480 _stop_threshold = config->stop_threshold;
481 _silence_threshold = config->silence_threshold;
483 AUDIO_LOG_DEBUG("_pcm_get_params (handle %p, format %d, rate %u, channels %u, period_size %u, periods %u, buffer_size %u)",
484 pcm_handle, config->format, config->rate, config->channels, config->period_size, config->period_count, _buffer_size);
487 audio_pcm_sample_spec_s *ss;
489 snd_pcm_uframes_t _period_size = 0 , _buffer_size = 0;
490 snd_pcm_format_t _format;
491 unsigned int _rate = 0, _channels = 0;
492 snd_pcm_uframes_t _start_threshold = 0, _stop_threshold = 0, _silence_threshold = 0, _avail_min = 0;
493 unsigned int _periods = 0;
494 snd_pcm_hw_params_t *hwparams;
495 snd_pcm_sw_params_t *swparams;
497 ss = (audio_pcm_sample_spec_s *)*sample_spec;
499 snd_pcm_hw_params_alloca(&hwparams);
500 snd_pcm_sw_params_alloca(&swparams);
502 if ((err = snd_pcm_hw_params_current(pcm_handle, hwparams)) < 0) {
503 AUDIO_LOG_ERROR("snd_pcm_hw_params_current() failed : %d", err);
504 return AUDIO_ERR_PARAMETER;
507 if ((err = snd_pcm_hw_params_get_period_size(hwparams, &_period_size, &dir)) < 0 ||
508 (err = snd_pcm_hw_params_get_buffer_size(hwparams, &_buffer_size)) < 0 ||
509 (err = snd_pcm_hw_params_get_periods(hwparams, &_periods, &dir)) < 0 ||
510 (err = snd_pcm_hw_params_get_format(hwparams, &_format)) < 0 ||
511 (err = snd_pcm_hw_params_get_rate(hwparams, &_rate, &dir)) < 0 ||
512 (err = snd_pcm_hw_params_get_channels(hwparams, &_channels)) < 0) {
513 AUDIO_LOG_ERROR("snd_pcm_hw_params_get_{period_size|buffer_size|periods|format|rate|channels}() failed : %d", err);
514 return AUDIO_ERR_PARAMETER;
517 *period_size = _period_size;
519 ss->format = _format;
521 ss->channels = _channels;
523 if ((err = snd_pcm_sw_params_current(pcm_handle, swparams)) < 0) {
524 AUDIO_LOG_ERROR("snd_pcm_sw_params_current() failed : %d", err);
525 return AUDIO_ERR_PARAMETER;
528 if ((err = snd_pcm_sw_params_get_start_threshold(swparams, &_start_threshold)) < 0 ||
529 (err = snd_pcm_sw_params_get_stop_threshold(swparams, &_stop_threshold)) < 0 ||
530 (err = snd_pcm_sw_params_get_silence_threshold(swparams, &_silence_threshold)) < 0 ||
531 (err = snd_pcm_sw_params_get_avail_min(swparams, &_avail_min)) < 0) {
532 AUDIO_LOG_ERROR("snd_pcm_sw_params_get_{start_threshold|stop_threshold|silence_threshold|avail_min}() failed : %d", err);
535 AUDIO_LOG_DEBUG("_pcm_get_params (handle %p, format %d, rate %u, channels %u, period_size %lu, periods %u, buffer_size %lu,",
536 pcm_handle, _format, _rate, _channels, _period_size, _periods, _buffer_size);
537 AUDIO_LOG_DEBUG(" start_threshold %lu, stop_threshold %lu, silence_threshold %lu, avail_min %lu)",
538 _start_threshold, _stop_threshold, _silence_threshold, _avail_min);
545 audio_return_e _pcm_set_params(void *pcm_handle, uint32_t direction, void *sample_spec, uint32_t period_size, uint32_t periods)
547 #ifdef __USE_TINYALSA__
548 /* Parameters are only acceptable in pcm_open() function */
549 AUDIO_LOG_DEBUG("_pcm_set_params");
552 audio_pcm_sample_spec_s ss;
553 snd_pcm_uframes_t _buffer_size;
554 snd_pcm_hw_params_t *hwparams;
555 snd_pcm_sw_params_t *swparams;
557 ss = *(audio_pcm_sample_spec_s *)sample_spec;
559 snd_pcm_hw_params_alloca(&hwparams);
560 snd_pcm_sw_params_alloca(&swparams);
563 if ((err = snd_pcm_hw_params_any(pcm_handle, hwparams)) < 0) {
564 AUDIO_LOG_ERROR("snd_pcm_hw_params_any() failed : %d", err);
565 return AUDIO_ERR_PARAMETER;
568 if ((err = snd_pcm_hw_params_set_rate_resample(pcm_handle, hwparams, 0)) < 0) {
569 AUDIO_LOG_ERROR("snd_pcm_hw_params_set_rate_resample() failed : %d", err);
570 return AUDIO_ERR_PARAMETER;
573 if ((err = snd_pcm_hw_params_set_access(pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
574 AUDIO_LOG_ERROR("snd_pcm_hw_params_set_access() failed : %d", err);
575 return AUDIO_ERR_PARAMETER;
578 ss.format = __convert_format((audio_sample_format_s)ss.format);
579 if ((err = snd_pcm_hw_params_set_format(pcm_handle, hwparams, ss.format)) < 0) {
580 AUDIO_LOG_ERROR("snd_pcm_hw_params_set_format() failed : %d", err);
581 return AUDIO_ERR_PARAMETER;
584 if ((err = snd_pcm_hw_params_set_rate(pcm_handle, hwparams, ss.rate, 0)) < 0) {
585 AUDIO_LOG_ERROR("snd_pcm_hw_params_set_rate() failed : %d", err);
586 return AUDIO_ERR_PARAMETER;
589 if ((err = snd_pcm_hw_params_set_channels(pcm_handle, hwparams, ss.channels)) < 0) {
590 AUDIO_LOG_ERROR("snd_pcm_hw_params_set_channels(%u) failed : %d", ss.channels, err);
591 return AUDIO_ERR_PARAMETER;
594 if ((err = snd_pcm_hw_params_set_period_size(pcm_handle, hwparams, period_size, 0)) < 0) {
595 AUDIO_LOG_ERROR("snd_pcm_hw_params_set_period_size(%u) failed : %d", period_size, err);
596 return AUDIO_ERR_PARAMETER;
599 if ((err = snd_pcm_hw_params_set_periods(pcm_handle, hwparams, periods, 0)) < 0) {
600 AUDIO_LOG_ERROR("snd_pcm_hw_params_set_periods(%u) failed : %d", periods, err);
601 return AUDIO_ERR_PARAMETER;
604 _buffer_size = period_size * periods;
605 if ((err = snd_pcm_hw_params_set_buffer_size(pcm_handle, hwparams, _buffer_size)) < 0) {
606 AUDIO_LOG_ERROR("snd_pcm_hw_params_set_buffer_size(%lu) failed : %d", _buffer_size, err);
607 return AUDIO_ERR_PARAMETER;
610 if ((err = snd_pcm_hw_params(pcm_handle, hwparams)) < 0) {
611 AUDIO_LOG_ERROR("snd_pcm_hw_params failed : %d", err);
612 return AUDIO_ERR_IOCTL;
616 if ((err = snd_pcm_sw_params_current(pcm_handle, swparams)) < 0) {
617 AUDIO_LOG_ERROR("Unable to determine current swparams : %d", err);
618 return AUDIO_ERR_PARAMETER;
621 if ((err = snd_pcm_sw_params_set_tstamp_mode(pcm_handle, swparams, SND_PCM_TSTAMP_ENABLE)) < 0) {
622 AUDIO_LOG_ERROR("Unable to enable time stamping : %d", err);
623 return AUDIO_ERR_PARAMETER;
626 if ((err = snd_pcm_sw_params_set_stop_threshold(pcm_handle, swparams, 0xFFFFFFFF)) < 0) {
627 AUDIO_LOG_ERROR("Unable to set stop threshold : %d", err);
628 return AUDIO_ERR_PARAMETER;
631 if ((err = snd_pcm_sw_params_set_start_threshold(pcm_handle, swparams, _buffer_size)) < 0) {
632 AUDIO_LOG_ERROR("Unable to set start threshold : %d", err);
633 return AUDIO_ERR_PARAMETER;
636 if ((err = snd_pcm_sw_params_set_avail_min(pcm_handle, swparams, period_size)) < 0) {
637 AUDIO_LOG_ERROR("snd_pcm_sw_params_set_avail_min() failed : %d", err);
638 return AUDIO_ERR_PARAMETER;
641 if ((err = snd_pcm_sw_params(pcm_handle, swparams)) < 0) {
642 AUDIO_LOG_ERROR("Unable to set sw params : %d", err);
643 return AUDIO_ERR_IOCTL;
647 if ((err = snd_pcm_prepare(pcm_handle)) < 0) {
648 AUDIO_LOG_ERROR("snd_pcm_prepare() failed : %d", err);
649 return AUDIO_ERR_IOCTL;
652 AUDIO_LOG_DEBUG("_pcm_set_params (handle %p, format %d, rate %u, channels %u, period_size %u, periods %u, buffer_size %lu)",
653 pcm_handle, ss.format, ss.rate, ss.channels, period_size, periods, _buffer_size);
659 /* Generic snd pcm interface APIs */
660 audio_return_e _pcm_set_hw_params(snd_pcm_t *pcm, audio_pcm_sample_spec_s *sample_spec, uint8_t *use_mmap, snd_pcm_uframes_t *period_size, snd_pcm_uframes_t *buffer_size)
662 audio_return_e ret = AUDIO_RET_OK;
663 snd_pcm_hw_params_t *hwparams;
666 unsigned int val = 0;
667 snd_pcm_uframes_t _period_size = period_size ? *period_size : 0;
668 snd_pcm_uframes_t _buffer_size = buffer_size ? *buffer_size : 0;
669 uint8_t _use_mmap = use_mmap && *use_mmap;
670 uint32_t channels = 0;
672 AUDIO_RETURN_VAL_IF_FAIL(pcm, AUDIO_ERR_PARAMETER);
674 snd_pcm_hw_params_alloca(&hwparams);
676 /* Skip parameter setting to null device. */
677 if (snd_pcm_type(pcm) == SND_PCM_TYPE_NULL)
678 return AUDIO_ERR_IOCTL;
680 /* Allocate a hardware parameters object. */
681 snd_pcm_hw_params_alloca(&hwparams);
683 /* Fill it in with default values. */
684 if (snd_pcm_hw_params_any(pcm, hwparams) < 0) {
685 AUDIO_LOG_ERROR("snd_pcm_hw_params_any() : failed! - %s\n", snd_strerror(err));
689 /* Set the desired hardware parameters. */
693 if (snd_pcm_hw_params_set_access(pcm, hwparams, SND_PCM_ACCESS_MMAP_INTERLEAVED) < 0) {
695 /* mmap() didn't work, fall back to interleaved */
697 if ((ret = snd_pcm_hw_params_set_access(pcm, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
698 AUDIO_LOG_DEBUG("snd_pcm_hw_params_set_access() failed: %s", snd_strerror(ret));
705 } else if ((ret = snd_pcm_hw_params_set_access(pcm, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
706 AUDIO_LOG_DEBUG("snd_pcm_hw_params_set_access() failed: %s", snd_strerror(ret));
709 AUDIO_LOG_DEBUG("setting rate - %d", sample_spec->rate);
710 err = snd_pcm_hw_params_set_rate(pcm, hwparams, sample_spec->rate, 0);
712 AUDIO_LOG_ERROR("snd_pcm_hw_params_set_rate() : failed! - %s\n", snd_strerror(err));
715 err = snd_pcm_hw_params(pcm, hwparams);
717 AUDIO_LOG_ERROR("snd_pcm_hw_params() : failed! - %s\n", snd_strerror(err));
721 /* Dump current param */
723 if ((ret = snd_pcm_hw_params_current(pcm, hwparams)) < 0) {
724 AUDIO_LOG_INFO("snd_pcm_hw_params_current() failed: %s", snd_strerror(ret));
728 if ((ret = snd_pcm_hw_params_get_period_size(hwparams, &_period_size, &dir)) < 0 ||
729 (ret = snd_pcm_hw_params_get_buffer_size(hwparams, &_buffer_size)) < 0) {
730 AUDIO_LOG_INFO("snd_pcm_hw_params_get_{period|buffer}_size() failed: %s", snd_strerror(ret));
734 snd_pcm_hw_params_get_access(hwparams, (snd_pcm_access_t *) &val);
735 AUDIO_LOG_DEBUG("access type = %s\n", snd_pcm_access_name((snd_pcm_access_t)val));
737 snd_pcm_hw_params_get_format(hwparams, &sample_spec->format);
738 AUDIO_LOG_DEBUG("format = '%s' (%s)\n",
739 snd_pcm_format_name((snd_pcm_format_t)sample_spec->format),
740 snd_pcm_format_description((snd_pcm_format_t)sample_spec->format));
742 snd_pcm_hw_params_get_subformat(hwparams, (snd_pcm_subformat_t *)&val);
743 AUDIO_LOG_DEBUG("subformat = '%s' (%s)\n",
744 snd_pcm_subformat_name((snd_pcm_subformat_t)val),
745 snd_pcm_subformat_description((snd_pcm_subformat_t)val));
747 snd_pcm_hw_params_get_channels(hwparams, &channels);
748 sample_spec->channels = (uint8_t)channels;
749 AUDIO_LOG_DEBUG("channels = %d\n", sample_spec->channels);
752 *buffer_size = _buffer_size;
755 *period_size = _period_size;
758 *use_mmap = _use_mmap;
763 return AUDIO_ERR_RESOURCE;
766 audio_return_e _pcm_set_sw_params(snd_pcm_t *pcm, snd_pcm_uframes_t avail_min, uint8_t period_event)
768 snd_pcm_sw_params_t *swparams;
769 snd_pcm_uframes_t boundary;
772 AUDIO_RETURN_VAL_IF_FAIL(pcm, AUDIO_ERR_PARAMETER);
774 snd_pcm_sw_params_alloca(&swparams);
776 if ((err = snd_pcm_sw_params_current(pcm, swparams)) < 0) {
777 AUDIO_LOG_WARN("Unable to determine current swparams: %s\n", snd_strerror(err));
780 if ((err = snd_pcm_sw_params_set_period_event(pcm, swparams, period_event)) < 0) {
781 AUDIO_LOG_WARN("Unable to disable period event: %s\n", snd_strerror(err));
784 if ((err = snd_pcm_sw_params_set_tstamp_mode(pcm, swparams, SND_PCM_TSTAMP_ENABLE)) < 0) {
785 AUDIO_LOG_WARN("Unable to enable time stamping: %s\n", snd_strerror(err));
788 if ((err = snd_pcm_sw_params_get_boundary(swparams, &boundary)) < 0) {
789 AUDIO_LOG_WARN("Unable to get boundary: %s\n", snd_strerror(err));
792 if ((err = snd_pcm_sw_params_set_stop_threshold(pcm, swparams, boundary)) < 0) {
793 AUDIO_LOG_WARN("Unable to set stop threshold: %s\n", snd_strerror(err));
796 if ((err = snd_pcm_sw_params_set_start_threshold(pcm, swparams, (snd_pcm_uframes_t) avail_min)) < 0) {
797 AUDIO_LOG_WARN("Unable to set start threshold: %s\n", snd_strerror(err));
800 if ((err = snd_pcm_sw_params_set_avail_min(pcm, swparams, avail_min)) < 0) {
801 AUDIO_LOG_WARN("snd_pcm_sw_params_set_avail_min() failed: %s", snd_strerror(err));
804 if ((err = snd_pcm_sw_params(pcm, swparams)) < 0) {
805 AUDIO_LOG_WARN("Unable to set sw params: %s\n", snd_strerror(err));