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.
29 #include "tizen-audio-internal.h"
30 #include "tizen-audio-impl.h"
32 #ifdef __USE_TINYALSA__
33 /* Convert pcm format from pulse to alsa */
34 static const uint32_t g_format_convert_table[] = {
35 [AUDIO_SAMPLE_U8] = PCM_FORMAT_S8,
36 [AUDIO_SAMPLE_S16LE] = PCM_FORMAT_S16_LE,
37 [AUDIO_SAMPLE_S32LE] = PCM_FORMAT_S32_LE,
38 [AUDIO_SAMPLE_S24_32LE] = PCM_FORMAT_S24_LE
41 /* FIXME : To avoid build warning... */
42 int _snd_pcm_poll_descriptor(snd_pcm_t *pcm);
43 /* Convert pcm format from pulse to alsa */
44 static const uint32_t g_format_convert_table[] = {
45 [AUDIO_SAMPLE_U8] = SND_PCM_FORMAT_U8,
46 [AUDIO_SAMPLE_ALAW] = SND_PCM_FORMAT_A_LAW,
47 [AUDIO_SAMPLE_ULAW] = SND_PCM_FORMAT_MU_LAW,
48 [AUDIO_SAMPLE_S16LE] = SND_PCM_FORMAT_S16_LE,
49 [AUDIO_SAMPLE_S16BE] = SND_PCM_FORMAT_S16_BE,
50 [AUDIO_SAMPLE_FLOAT32LE] = SND_PCM_FORMAT_FLOAT_LE,
51 [AUDIO_SAMPLE_FLOAT32BE] = SND_PCM_FORMAT_FLOAT_BE,
52 [AUDIO_SAMPLE_S32LE] = SND_PCM_FORMAT_S32_LE,
53 [AUDIO_SAMPLE_S32BE] = SND_PCM_FORMAT_S32_BE,
54 [AUDIO_SAMPLE_S24LE] = SND_PCM_FORMAT_S24_3LE,
55 [AUDIO_SAMPLE_S24BE] = SND_PCM_FORMAT_S24_3BE,
56 [AUDIO_SAMPLE_S24_32LE] = SND_PCM_FORMAT_S24_LE,
57 [AUDIO_SAMPLE_S24_32BE] = SND_PCM_FORMAT_S24_BE
61 static uint32_t __convert_format(audio_sample_format_t format)
63 return g_format_convert_table[format];
66 /* #define DEBUG_TIMING */
68 static int __voice_pcm_set_params(audio_hal_t *ah, snd_pcm_t *pcm)
70 snd_pcm_hw_params_t *params = NULL;
74 AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER);
75 AUDIO_RETURN_VAL_IF_FAIL(pcm, AUDIO_ERR_PARAMETER);
77 /* Skip parameter setting to null device. */
78 if (snd_pcm_type(pcm) == SND_PCM_TYPE_NULL)
79 return AUDIO_ERR_IOCTL;
81 /* Allocate a hardware parameters object. */
82 snd_pcm_hw_params_alloca(¶ms);
84 /* Fill it in with default values. */
85 if (snd_pcm_hw_params_any(pcm, params) < 0) {
86 AUDIO_LOG_ERROR("snd_pcm_hw_params_any() : failed! - %s\n", snd_strerror(err));
90 /* Set the desired hardware parameters. */
91 /* Interleaved mode */
92 err = snd_pcm_hw_params_set_access(pcm, params, SND_PCM_ACCESS_RW_INTERLEAVED);
94 AUDIO_LOG_ERROR("snd_pcm_hw_params_set_access() : failed! - %s\n", snd_strerror(err));
97 err = snd_pcm_hw_params_set_rate(pcm, params, 8000, 0);
99 AUDIO_LOG_ERROR("snd_pcm_hw_params_set_rate() : failed! - %s\n", snd_strerror(err));
101 err = snd_pcm_hw_params(pcm, params);
103 AUDIO_LOG_ERROR("snd_pcm_hw_params() : failed! - %s\n", snd_strerror(err));
107 /* Dump current param */
108 snd_pcm_hw_params_get_access(params, (snd_pcm_access_t *) &val);
109 AUDIO_LOG_DEBUG("access type = %s\n", snd_pcm_access_name((snd_pcm_access_t)val));
111 snd_pcm_hw_params_get_format(params, (snd_pcm_format_t*)&val);
112 AUDIO_LOG_DEBUG("format = '%s' (%s)\n",
113 snd_pcm_format_name((snd_pcm_format_t)val),
114 snd_pcm_format_description((snd_pcm_format_t)val));
116 snd_pcm_hw_params_get_subformat(params, (snd_pcm_subformat_t *)&val);
117 AUDIO_LOG_DEBUG("subformat = '%s' (%s)\n",
118 snd_pcm_subformat_name((snd_pcm_subformat_t)val),
119 snd_pcm_subformat_description((snd_pcm_subformat_t)val));
121 snd_pcm_hw_params_get_channels(params, &val);
122 AUDIO_LOG_DEBUG("channels = %d\n", val);
130 #ifdef __USE_TINYALSA__
131 static struct pcm *__tinyalsa_open_device(audio_pcm_sample_spec_t *ss, size_t period_size, size_t period_count, uint32_t direction)
133 struct pcm *pcm = NULL;
134 struct pcm_config config;
136 AUDIO_RETURN_NULL_IF_FAIL(ss);
138 config.channels = ss->channels;
139 config.rate = ss->rate;
140 config.period_size = period_size;
141 config.period_count = period_count;
142 config.format = ss->format;
143 config.start_threshold = period_size;
144 config.stop_threshold = 0xFFFFFFFF;
145 config.silence_threshold = 0;
147 AUDIO_LOG_INFO("direction %d, channels %d, rate %d, format %d, period_size %d, period_count %d", direction, ss->channels, ss->rate, ss->format, period_size, period_count);
149 pcm = pcm_open((direction == AUDIO_DIRECTION_OUT) ? PLAYBACK_CARD_ID : CAPTURE_CARD_ID,
150 (direction == AUDIO_DIRECTION_OUT) ? PLAYBACK_PCM_DEVICE_ID : CAPTURE_PCM_DEVICE_ID,
151 (direction == AUDIO_DIRECTION_OUT) ? PCM_OUT : PCM_IN,
153 if (!pcm || !pcm_is_ready(pcm)) {
154 AUDIO_LOG_ERROR("Unable to open device (%s)", pcm_get_error(pcm));
162 static int __tinyalsa_pcm_recover(struct pcm *pcm, int err)
166 if (err == -EINTR) /* nothing to do, continue */
169 AUDIO_LOG_INFO("XRUN occurred");
170 err = pcm_prepare(pcm);
172 AUDIO_LOG_ERROR("Could not recover from XRUN occurred, prepare failed : %d", err);
177 if (err == -ESTRPIPE) {
178 /* tinyalsa does not support pcm resume, dont't care suspend case */
179 AUDIO_LOG_ERROR("Could not recover from suspend : %d", err);
186 audio_return_t _fmradio_pcm_open(audio_hal_t *ah)
188 audio_return_t audio_ret = AUDIO_RET_OK;
190 const char *device_name = NULL;
191 audio_pcm_sample_spec_t sample_spec;
192 uint8_t use_mmap = 0;
193 sample_spec.rate = 44100;
194 sample_spec.channels = 2;
195 sample_spec.format = SND_PCM_FORMAT_S16_LE;
197 if ((audio_ret = _ucm_get_device_name(ah, AUDIO_USE_CASE_VERB_FMRADIO, AUDIO_DIRECTION_OUT, &device_name)))
200 #ifdef __USE_TINYALSA__
201 AUDIO_LOG_WARN("need implementation for tinyAlsa");
202 return AUDIO_ERR_NOT_IMPLEMENTED;
205 if((ret = snd_pcm_open((snd_pcm_t **)&ah->device.fmradio_pcm_out, (char *)device_name, SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
206 AUDIO_LOG_ERROR("[%s] out pcm_open failed", device_name);
207 audio_ret = AUDIO_ERR_IOCTL;
210 AUDIO_LOG_INFO("[%s] out pcm_open success:%p", device_name, ah->device.fmradio_pcm_out);
212 if ((audio_ret = _pcm_set_hw_params(ah->device.fmradio_pcm_out, &sample_spec, &use_mmap, NULL, NULL)) < 0) {
213 AUDIO_LOG_ERROR("[%s] out __set_pcm_hw_params failed", device_name);
214 if ((audio_ret = _pcm_close(ah->device.pcm_out)))
215 AUDIO_LOG_ERROR("failed to _pcm_close(), ret(0x%x)", audio_ret);
216 ah->device.fmradio_pcm_out = NULL;
224 free((void*)device_name);
229 audio_return_t _fmradio_pcm_close(audio_hal_t *ah)
231 audio_return_t audio_ret = AUDIO_RET_OK;
233 AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER);
235 AUDIO_LOG_INFO("close fmradio pcm handles");
237 if (ah->device.fmradio_pcm_out) {
238 if ((audio_ret = _pcm_close(ah->device.fmradio_pcm_out)))
239 AUDIO_LOG_ERROR("failed to _fmradio_pcm_close() for pcm_out, ret(0x%x)", audio_ret);
241 ah->device.fmradio_pcm_out = NULL;
242 AUDIO_LOG_INFO("fmradio pcm_out handle close success");
249 audio_return_t _voice_pcm_open(audio_hal_t *ah)
253 AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER);
255 #ifdef __USE_TINYALSA__
256 AUDIO_LOG_WARN("need implementation for tinyAlsa");
257 return AUDIO_ERR_NOT_IMPLEMENTED;
259 AUDIO_LOG_INFO("open voice pcm handles");
261 /* Get playback voice-pcm from ucm conf. Open and set-params */
262 if ((err = snd_pcm_open((snd_pcm_t **)&ah->device.pcm_out, VOICE_PCM_DEVICE, SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
263 AUDIO_LOG_ERROR("snd_pcm_open for %s failed. %s", VOICE_PCM_DEVICE, snd_strerror(err));
264 return AUDIO_ERR_IOCTL;
266 ret = __voice_pcm_set_params(ah, ah->device.pcm_out);
268 AUDIO_LOG_INFO("pcm playback device open success device(%s)", VOICE_PCM_DEVICE);
270 /* Get capture voice-pcm from ucm conf. Open and set-params */
271 if ((err = snd_pcm_open((snd_pcm_t **)&ah->device.pcm_in, VOICE_PCM_DEVICE, SND_PCM_STREAM_CAPTURE, 0)) < 0) {
272 AUDIO_LOG_ERROR("snd_pcm_open for %s failed. %s", VOICE_PCM_DEVICE, snd_strerror(err));
273 return AUDIO_ERR_IOCTL;
275 ret = __voice_pcm_set_params(ah, ah->device.pcm_in);
276 AUDIO_LOG_INFO("pcm captures device open success device(%s)", VOICE_PCM_DEVICE);
279 return (ret == 0 ? AUDIO_RET_OK : AUDIO_ERR_INTERNAL);
282 audio_return_t _voice_pcm_close(audio_hal_t *ah, uint32_t direction)
284 audio_return_t audio_ret = AUDIO_RET_OK;
286 AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER);
288 AUDIO_LOG_INFO("close voice pcm handles");
290 if (ah->device.pcm_out && (direction == AUDIO_DIRECTION_OUT)) {
291 if ((audio_ret = _pcm_close(ah->device.pcm_out)))
292 AUDIO_LOG_ERROR("failed to _pcm_close() for pcm_out, ret(0x%x)", audio_ret);
294 ah->device.pcm_out = NULL;
295 AUDIO_LOG_INFO("voice pcm_out handle close success");
297 } else if (ah->device.pcm_in && (direction == AUDIO_DIRECTION_IN)) {
298 if ((audio_ret = _pcm_close(ah->device.pcm_in)))
299 AUDIO_LOG_ERROR("failed to _pcm_close() for pcm_in, ret(0x%x)", audio_ret);
301 ah->device.pcm_in = NULL;
302 AUDIO_LOG_INFO("voice pcm_in handle close success");
309 audio_return_t _reset_pcm_devices(audio_hal_t *ah)
311 audio_return_t audio_ret = AUDIO_RET_OK;
313 AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER);
315 if (ah->device.pcm_out) {
316 if (!(audio_ret = _pcm_close(ah->device.pcm_out))) {
317 ah->device.pcm_out = NULL;
318 AUDIO_LOG_INFO("pcm_out handle close success");
321 if (ah->device.pcm_in) {
322 if (!(audio_ret = _pcm_close(ah->device.pcm_in))) {
323 ah->device.pcm_in = NULL;
324 AUDIO_LOG_INFO("pcm_in handle close success");
327 if (ah->device.fmradio_pcm_out) {
328 if (!(audio_ret = _pcm_close(ah->device.fmradio_pcm_out))) {
329 ah->device.fmradio_pcm_out = NULL;
330 AUDIO_LOG_INFO("fmradio_pcm_out handle close success");
337 audio_return_t _pcm_open(void **pcm_handle, uint32_t direction, void *sample_spec, uint32_t period_size, uint32_t periods)
339 #ifdef __USE_TINYALSA__
340 audio_pcm_sample_spec_t *ss;
343 ss = (audio_pcm_sample_spec_t *)sample_spec;
344 ss->format = __convert_format((audio_sample_format_t)ss->format);
346 *pcm_handle = __tinyalsa_open_device(ss, (size_t)period_size, (size_t)periods, direction);
347 if (*pcm_handle == NULL) {
348 AUDIO_LOG_ERROR("Error opening PCM device");
349 return AUDIO_ERR_RESOURCE;
352 if ((err = pcm_prepare((struct pcm *)*pcm_handle)) != 0) {
353 AUDIO_LOG_ERROR("Error prepare PCM device : %d", err);
358 char *device_name = NULL;
360 mode = SND_PCM_NONBLOCK | SND_PCM_NO_AUTO_RESAMPLE | SND_PCM_NO_AUTO_CHANNELS | SND_PCM_NO_AUTO_FORMAT;
362 if (direction == AUDIO_DIRECTION_OUT)
363 device_name = PLAYBACK_PCM_DEVICE;
364 else if (direction == AUDIO_DIRECTION_IN)
365 device_name = CAPTURE_PCM_DEVICE;
367 AUDIO_LOG_ERROR("Error get device_name, direction : %d", direction);
368 return AUDIO_ERR_RESOURCE;
371 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) {
372 AUDIO_LOG_ERROR("Error opening PCM device %s : %s", device_name, snd_strerror(err));
373 return AUDIO_ERR_RESOURCE;
376 if ((err = _pcm_set_params(*pcm_handle, direction, sample_spec, period_size, periods)) != AUDIO_RET_OK) {
377 AUDIO_LOG_ERROR("Failed to set pcm parameters : %d", err);
381 AUDIO_LOG_INFO("PCM device %s", device_name);
387 audio_return_t _pcm_start(void *pcm_handle)
391 #ifdef __USE_TINYALSA__
392 if ((err = pcm_start(pcm_handle)) < 0) {
393 AUDIO_LOG_ERROR("Error starting PCM handle : %d", err);
394 return AUDIO_ERR_RESOURCE;
397 if ((err = snd_pcm_start(pcm_handle)) < 0) {
398 AUDIO_LOG_ERROR("Error starting PCM handle : %s", snd_strerror(err));
399 return AUDIO_ERR_RESOURCE;
403 AUDIO_LOG_INFO("PCM handle 0x%x start", pcm_handle);
407 audio_return_t _pcm_stop(void *pcm_handle)
411 #ifdef __USE_TINYALSA__
412 if ((err = pcm_stop(pcm_handle)) < 0) {
413 AUDIO_LOG_ERROR("Error stopping PCM handle : %d", err);
414 return AUDIO_ERR_RESOURCE;
417 if ((err = snd_pcm_drop(pcm_handle)) < 0) {
418 AUDIO_LOG_ERROR("Error stopping PCM handle : %s", snd_strerror(err));
419 return AUDIO_ERR_RESOURCE;
423 AUDIO_LOG_INFO("PCM handle 0x%x stop", pcm_handle);
427 audio_return_t _pcm_close(void *pcm_handle)
431 AUDIO_LOG_INFO("Try to close PCM handle 0x%x", pcm_handle);
433 #ifdef __USE_TINYALSA__
434 if ((err = pcm_close(pcm_handle)) < 0) {
435 AUDIO_LOG_ERROR("Error closing PCM handle : %d", err);
436 return AUDIO_ERR_RESOURCE;
439 if ((err = snd_pcm_close(pcm_handle)) < 0) {
440 AUDIO_LOG_ERROR("Error closing PCM handle : %s", snd_strerror(err));
441 return AUDIO_ERR_RESOURCE;
448 audio_return_t _pcm_avail(void *pcm_handle, uint32_t *avail)
450 #ifdef __USE_TINYALSA__
451 struct timespec tspec;
452 unsigned int frames_avail = 0;
455 err = pcm_get_htimestamp(pcm_handle, &frames_avail, &tspec);
457 AUDIO_LOG_ERROR("Could not get avail and timespec at PCM handle 0x%x : %d", pcm_handle, err);
458 return AUDIO_ERR_IOCTL;
462 AUDIO_LOG_DEBUG("avail = %d", frames_avail);
465 *avail = (uint32_t)frames_avail;
467 snd_pcm_sframes_t frames_avail;
469 if ((frames_avail = snd_pcm_avail(pcm_handle)) < 0) {
470 AUDIO_LOG_ERROR("Could not get avail at PCM handle 0x%x : %d", pcm_handle, frames_avail);
471 return AUDIO_ERR_IOCTL;
475 AUDIO_LOG_DEBUG("avail = %d", frames_avail);
478 *avail = (uint32_t)frames_avail;
484 audio_return_t _pcm_write(void *pcm_handle, const void *buffer, uint32_t frames)
486 #ifdef __USE_TINYALSA__
489 err = pcm_write(pcm_handle, buffer, pcm_frames_to_bytes(pcm_handle, (unsigned int)frames));
491 AUDIO_LOG_ERROR("Failed to write pcm : %d", err);
492 return AUDIO_ERR_IOCTL;
496 AUDIO_LOG_DEBUG("_pcm_write = %d", frames);
499 snd_pcm_sframes_t frames_written;
501 AUDIO_RETURN_VAL_IF_FAIL(pcm_handle, AUDIO_ERR_PARAMETER);
503 frames_written = snd_pcm_writei(pcm_handle, buffer, (snd_pcm_uframes_t) frames);
504 if (frames_written < 0) {
505 AUDIO_LOG_ERROR("Failed to write pcm : %d", frames_written);
506 return AUDIO_ERR_IOCTL;
510 AUDIO_LOG_DEBUG("_pcm_write = (%d / %d)", frames_written, frames);
517 audio_return_t _pcm_read(void *pcm_handle, void *buffer, uint32_t frames)
519 #ifdef __USE_TINYALSA__
522 err = pcm_read(pcm_handle, buffer, pcm_frames_to_bytes(pcm_handle, (unsigned int)frames));
524 AUDIO_LOG_ERROR("Failed to read pcm : %d", err);
525 return AUDIO_ERR_IOCTL;
529 AUDIO_LOG_DEBUG("audio_pcm_read = %d", frames);
532 snd_pcm_sframes_t frames_read;
534 frames_read = snd_pcm_readi(pcm_handle, buffer, (snd_pcm_uframes_t)frames);
535 if (frames_read < 0) {
536 AUDIO_LOG_ERROR("Failed to read pcm : %d", frames_read);
537 return AUDIO_ERR_IOCTL;
541 AUDIO_LOG_DEBUG("_pcm_read = (%d / %d)", frames_read, frames);
548 audio_return_t _pcm_get_fd(void *pcm_handle, int *fd)
550 /* we use an internal API of the (tiny)alsa library, so it causes warning message during compile */
551 #ifdef __USE_TINYALSA__
552 *fd = _pcm_poll_descriptor((struct pcm *)pcm_handle);
554 *fd = _snd_pcm_poll_descriptor((snd_pcm_t *)pcm_handle);
559 audio_return_t _pcm_recover(void *pcm_handle, int revents)
563 AUDIO_RETURN_VAL_IF_FAIL(pcm_handle, AUDIO_ERR_PARAMETER);
565 if (revents & POLLERR)
566 AUDIO_LOG_DEBUG("Got POLLERR from ALSA");
567 if (revents & POLLNVAL)
568 AUDIO_LOG_DEBUG("Got POLLNVAL from ALSA");
569 if (revents & POLLHUP)
570 AUDIO_LOG_DEBUG("Got POLLHUP from ALSA");
571 if (revents & POLLPRI)
572 AUDIO_LOG_DEBUG("Got POLLPRI from ALSA");
573 if (revents & POLLIN)
574 AUDIO_LOG_DEBUG("Got POLLIN from ALSA");
575 if (revents & POLLOUT)
576 AUDIO_LOG_DEBUG("Got POLLOUT from ALSA");
578 #ifdef __USE_TINYALSA__
579 state = pcm_state(pcm_handle);
580 AUDIO_LOG_DEBUG("PCM state is %d", state);
584 if ((err = __tinyalsa_pcm_recover(pcm_handle, -EPIPE)) != 0) {
585 AUDIO_LOG_ERROR("Could not recover from POLLERR|POLLNVAL|POLLHUP and XRUN : %d", err);
586 return AUDIO_ERR_IOCTL;
590 case PCM_STATE_SUSPENDED:
591 if ((err = __tinyalsa_pcm_recover(pcm_handle, -ESTRPIPE)) != 0) {
592 AUDIO_LOG_ERROR("Could not recover from POLLERR|POLLNVAL|POLLHUP and SUSPENDED : %d", err);
593 return AUDIO_ERR_IOCTL;
598 pcm_stop(pcm_handle);
599 if ((err = pcm_prepare(pcm_handle)) < 0) {
600 AUDIO_LOG_ERROR("Could not recover from POLLERR|POLLNVAL|POLLHUP with pcm_prepare() : %d", err);
601 return AUDIO_ERR_IOCTL;
605 state = snd_pcm_state(pcm_handle);
606 AUDIO_LOG_DEBUG("PCM state is %s", snd_pcm_state_name(state));
608 /* Try to recover from this error */
611 case SND_PCM_STATE_XRUN:
612 if ((err = snd_pcm_recover(pcm_handle, -EPIPE, 1)) != 0) {
613 AUDIO_LOG_ERROR("Could not recover from POLLERR|POLLNVAL|POLLHUP and XRUN : %d", err);
614 return AUDIO_ERR_IOCTL;
618 case SND_PCM_STATE_SUSPENDED:
619 if ((err = snd_pcm_recover(pcm_handle, -ESTRPIPE, 1)) != 0) {
620 AUDIO_LOG_ERROR("Could not recover from POLLERR|POLLNVAL|POLLHUP and SUSPENDED : %d", err);
621 return AUDIO_ERR_IOCTL;
626 snd_pcm_drop(pcm_handle);
627 if ((err = snd_pcm_prepare(pcm_handle)) < 0) {
628 AUDIO_LOG_ERROR("Could not recover from POLLERR|POLLNVAL|POLLHUP with snd_pcm_prepare() : %d", err);
629 return AUDIO_ERR_IOCTL;
635 AUDIO_LOG_DEBUG("_pcm_recover");
639 audio_return_t _pcm_get_params(void *pcm_handle, uint32_t direction, void **sample_spec, uint32_t *period_size, uint32_t *periods)
641 #ifdef __USE_TINYALSA__
642 audio_pcm_sample_spec_t *ss;
643 unsigned int _period_size, _buffer_size, _periods, _format, _rate, _channels;
644 unsigned int _start_threshold, _stop_threshold, _silence_threshold;
645 struct pcm_config *config;
647 ss = (audio_pcm_sample_spec_t *)*sample_spec;
649 /* we use an internal API of the tiny alsa library, so it causes warning message during compile */
650 _pcm_config(pcm_handle, &config);
652 *period_size = config->period_size;
653 *periods = config->period_count;
654 _buffer_size = config->period_size * config->period_count;
655 ss->format = config->format;
656 ss->rate = config->rate;
657 ss->channels = config->channels;
658 _start_threshold = config->start_threshold;
659 _stop_threshold = config->stop_threshold;
660 _silence_threshold = config->silence_threshold;
662 AUDIO_LOG_DEBUG("_pcm_get_params (handle 0x%x, format %d, rate %d, channels %d, period_size %d, periods %d, buffer_size %d)", pcm_handle, config->format, config->rate, config->channels, config->period_size, config->period_count, _buffer_size);
665 audio_pcm_sample_spec_t *ss;
667 snd_pcm_uframes_t _period_size, _buffer_size;
668 snd_pcm_format_t _format;
669 unsigned int _rate, _channels;
670 snd_pcm_uframes_t _start_threshold, _stop_threshold, _silence_threshold, _avail_min;
671 unsigned int _periods;
672 snd_pcm_hw_params_t *hwparams;
673 snd_pcm_sw_params_t *swparams;
675 ss = (audio_pcm_sample_spec_t *)*sample_spec;
677 snd_pcm_hw_params_alloca(&hwparams);
678 snd_pcm_sw_params_alloca(&swparams);
680 if ((err = snd_pcm_hw_params_current(pcm_handle, hwparams)) < 0) {
681 AUDIO_LOG_ERROR("snd_pcm_hw_params_current() failed : %d", err);
682 return AUDIO_ERR_PARAMETER;
685 if ((err = snd_pcm_hw_params_get_period_size(hwparams, &_period_size, &dir)) < 0 ||
686 (err = snd_pcm_hw_params_get_buffer_size(hwparams, &_buffer_size)) < 0 ||
687 (err = snd_pcm_hw_params_get_periods(hwparams, &_periods, &dir)) < 0 ||
688 (err = snd_pcm_hw_params_get_format(hwparams, &_format)) < 0 ||
689 (err = snd_pcm_hw_params_get_rate(hwparams, &_rate, &dir)) < 0 ||
690 (err = snd_pcm_hw_params_get_channels(hwparams, &_channels)) < 0) {
691 AUDIO_LOG_ERROR("snd_pcm_hw_params_get_{period_size|buffer_size|periods|format|rate|channels}() failed : %s", err);
692 return AUDIO_ERR_PARAMETER;
695 *period_size = _period_size;
697 ss->format = _format;
699 ss->channels = _channels;
701 if ((err = snd_pcm_sw_params_current(pcm_handle, swparams)) < 0) {
702 AUDIO_LOG_ERROR("snd_pcm_sw_params_current() failed : %d", err);
703 return AUDIO_ERR_PARAMETER;
706 if ((err = snd_pcm_sw_params_get_start_threshold(swparams, &_start_threshold)) < 0 ||
707 (err = snd_pcm_sw_params_get_stop_threshold(swparams, &_stop_threshold)) < 0 ||
708 (err = snd_pcm_sw_params_get_silence_threshold(swparams, &_silence_threshold)) < 0 ||
709 (err = snd_pcm_sw_params_get_avail_min(swparams, &_avail_min)) < 0) {
710 AUDIO_LOG_ERROR("snd_pcm_sw_params_get_{start_threshold|stop_threshold|silence_threshold|avail_min}() failed : %s", err);
713 AUDIO_LOG_DEBUG("_pcm_get_params (handle 0x%x, format %d, rate %d, channels %d, period_size %d, periods %d, buffer_size %d)", pcm_handle, _format, _rate, _channels, _period_size, _periods, _buffer_size);
719 audio_return_t _pcm_set_params(void *pcm_handle, uint32_t direction, void *sample_spec, uint32_t period_size, uint32_t periods)
721 #ifdef __USE_TINYALSA__
722 /* Parameters are only acceptable in pcm_open() function */
723 AUDIO_LOG_DEBUG("_pcm_set_params");
726 audio_pcm_sample_spec_t ss;
727 snd_pcm_uframes_t _buffer_size;
728 snd_pcm_hw_params_t *hwparams;
729 snd_pcm_sw_params_t *swparams;
731 ss = *(audio_pcm_sample_spec_t *)sample_spec;
733 snd_pcm_hw_params_alloca(&hwparams);
734 snd_pcm_sw_params_alloca(&swparams);
737 if ((err = snd_pcm_hw_params_any(pcm_handle, hwparams)) < 0) {
738 AUDIO_LOG_ERROR("snd_pcm_hw_params_any() failed : %d", err);
739 return AUDIO_ERR_PARAMETER;
742 if ((err = snd_pcm_hw_params_set_rate_resample(pcm_handle, hwparams, 0)) < 0) {
743 AUDIO_LOG_ERROR("snd_pcm_hw_params_set_rate_resample() failed : %d", err);
744 return AUDIO_ERR_PARAMETER;
747 if ((err = snd_pcm_hw_params_set_access(pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
748 AUDIO_LOG_ERROR("snd_pcm_hw_params_set_access() failed : %d", err);
749 return AUDIO_ERR_PARAMETER;
752 ss.format = __convert_format((audio_sample_format_t)ss.format);
753 if ((err = snd_pcm_hw_params_set_format(pcm_handle, hwparams, ss.format)) < 0) {
754 AUDIO_LOG_ERROR("snd_pcm_hw_params_set_format() failed : %d", err);
755 return AUDIO_ERR_PARAMETER;
758 if ((err = snd_pcm_hw_params_set_rate(pcm_handle, hwparams, ss.rate, 0)) < 0) {
759 AUDIO_LOG_ERROR("snd_pcm_hw_params_set_rate() failed : %d", err);
760 return AUDIO_ERR_PARAMETER;
763 if ((err = snd_pcm_hw_params_set_channels(pcm_handle, hwparams, ss.channels)) < 0) {
764 AUDIO_LOG_ERROR("snd_pcm_hw_params_set_channels(%u) failed : %d", err);
765 return AUDIO_ERR_PARAMETER;
768 if ((err = snd_pcm_hw_params_set_period_size(pcm_handle, hwparams, period_size, 0)) < 0) {
769 AUDIO_LOG_ERROR("snd_pcm_hw_params_set_period_size(%u) failed : %d", err);
770 return AUDIO_ERR_PARAMETER;
773 if ((err = snd_pcm_hw_params_set_periods(pcm_handle, hwparams, periods, 0)) < 0) {
774 AUDIO_LOG_ERROR("snd_pcm_hw_params_set_periods(%u) failed : %d", periods, err);
775 return AUDIO_ERR_PARAMETER;
778 _buffer_size = period_size * periods;
779 if ((err = snd_pcm_hw_params_set_buffer_size(pcm_handle, hwparams, _buffer_size)) < 0) {
780 AUDIO_LOG_ERROR("snd_pcm_hw_params_set_buffer_size(%u) failed : %d", periods * periods, err);
781 return AUDIO_ERR_PARAMETER;
784 if ((err = snd_pcm_hw_params(pcm_handle, hwparams)) < 0) {
785 AUDIO_LOG_ERROR("snd_pcm_hw_params failed : %d", err);
786 return AUDIO_ERR_IOCTL;
790 if ((err = snd_pcm_sw_params_current(pcm_handle, swparams)) < 0) {
791 AUDIO_LOG_ERROR("Unable to determine current swparams : %d", err);
792 return AUDIO_ERR_PARAMETER;
795 if ((err = snd_pcm_sw_params_set_tstamp_mode(pcm_handle, swparams, SND_PCM_TSTAMP_ENABLE)) < 0) {
796 AUDIO_LOG_ERROR("Unable to enable time stamping : %d", err);
797 return AUDIO_ERR_PARAMETER;
800 if ((err = snd_pcm_sw_params_set_stop_threshold(pcm_handle, swparams, 0xFFFFFFFF)) < 0) {
801 AUDIO_LOG_ERROR("Unable to set stop threshold : %d", err);
802 return AUDIO_ERR_PARAMETER;
805 if ((err = snd_pcm_sw_params_set_start_threshold(pcm_handle, swparams, period_size / 2)) < 0) {
806 AUDIO_LOG_ERROR("Unable to set start threshold : %d", err);
807 return AUDIO_ERR_PARAMETER;
810 if ((err = snd_pcm_sw_params_set_avail_min(pcm_handle, swparams, 1024)) < 0) {
811 AUDIO_LOG_ERROR("snd_pcm_sw_params_set_avail_min() failed : %d", err);
812 return AUDIO_ERR_PARAMETER;
815 if ((err = snd_pcm_sw_params(pcm_handle, swparams)) < 0) {
816 AUDIO_LOG_ERROR("Unable to set sw params : %d", err);
817 return AUDIO_ERR_IOCTL;
821 if ((err = snd_pcm_prepare(pcm_handle)) < 0) {
822 AUDIO_LOG_ERROR("snd_pcm_prepare() failed : %d", err);
823 return AUDIO_ERR_IOCTL;
826 AUDIO_LOG_DEBUG("_pcm_set_params (handle 0x%x, format %d, rate %d, channels %d, period_size %d, periods %d, buffer_size %d)", pcm_handle, ss.format, ss.rate, ss.channels, period_size, periods, _buffer_size);
832 /* Generic snd pcm interface APIs */
833 audio_return_t _pcm_set_hw_params(snd_pcm_t *pcm, audio_pcm_sample_spec_t *sample_spec, uint8_t *use_mmap, snd_pcm_uframes_t *period_size, snd_pcm_uframes_t *buffer_size)
835 audio_return_t ret = AUDIO_RET_OK;
836 snd_pcm_hw_params_t *hwparams;
839 unsigned int val = 0;
840 snd_pcm_uframes_t _period_size = period_size ? *period_size : 0;
841 snd_pcm_uframes_t _buffer_size = buffer_size ? *buffer_size : 0;
842 uint8_t _use_mmap = use_mmap && *use_mmap;
843 uint32_t channels = 0;
845 AUDIO_RETURN_VAL_IF_FAIL(pcm, AUDIO_ERR_PARAMETER);
847 snd_pcm_hw_params_alloca(&hwparams);
849 /* Skip parameter setting to null device. */
850 if (snd_pcm_type(pcm) == SND_PCM_TYPE_NULL)
851 return AUDIO_ERR_IOCTL;
853 /* Allocate a hardware parameters object. */
854 snd_pcm_hw_params_alloca(&hwparams);
856 /* Fill it in with default values. */
857 if (snd_pcm_hw_params_any(pcm, hwparams) < 0) {
858 AUDIO_LOG_ERROR("snd_pcm_hw_params_any() : failed! - %s\n", snd_strerror(err));
862 /* Set the desired hardware parameters. */
866 if (snd_pcm_hw_params_set_access(pcm, hwparams, SND_PCM_ACCESS_MMAP_INTERLEAVED) < 0) {
868 /* mmap() didn't work, fall back to interleaved */
870 if ((ret = snd_pcm_hw_params_set_access(pcm, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
871 AUDIO_LOG_DEBUG("snd_pcm_hw_params_set_access() failed: %s", snd_strerror(ret));
878 } else if ((ret = snd_pcm_hw_params_set_access(pcm, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
879 AUDIO_LOG_DEBUG("snd_pcm_hw_params_set_access() failed: %s", snd_strerror(ret));
882 AUDIO_LOG_DEBUG("setting rate - %d", sample_spec->rate);
883 err = snd_pcm_hw_params_set_rate(pcm, hwparams, sample_spec->rate, 0);
885 AUDIO_LOG_ERROR("snd_pcm_hw_params_set_rate() : failed! - %s\n", snd_strerror(err));
888 err = snd_pcm_hw_params(pcm, hwparams);
890 AUDIO_LOG_ERROR("snd_pcm_hw_params() : failed! - %s\n", snd_strerror(err));
894 /* Dump current param */
896 if ((ret = snd_pcm_hw_params_current(pcm, hwparams)) < 0) {
897 AUDIO_LOG_INFO("snd_pcm_hw_params_current() failed: %s", snd_strerror(ret));
901 if ((ret = snd_pcm_hw_params_get_period_size(hwparams, &_period_size, &dir)) < 0 ||
902 (ret = snd_pcm_hw_params_get_buffer_size(hwparams, &_buffer_size)) < 0) {
903 AUDIO_LOG_INFO("snd_pcm_hw_params_get_{period|buffer}_size() failed: %s", snd_strerror(ret));
907 snd_pcm_hw_params_get_access(hwparams, (snd_pcm_access_t *) &val);
908 AUDIO_LOG_DEBUG("access type = %s\n", snd_pcm_access_name((snd_pcm_access_t)val));
910 snd_pcm_hw_params_get_format(hwparams, &sample_spec->format);
911 AUDIO_LOG_DEBUG("format = '%s' (%s)\n",
912 snd_pcm_format_name((snd_pcm_format_t)sample_spec->format),
913 snd_pcm_format_description((snd_pcm_format_t)sample_spec->format));
915 snd_pcm_hw_params_get_subformat(hwparams, (snd_pcm_subformat_t *)&val);
916 AUDIO_LOG_DEBUG("subformat = '%s' (%s)\n",
917 snd_pcm_subformat_name((snd_pcm_subformat_t)val),
918 snd_pcm_subformat_description((snd_pcm_subformat_t)val));
920 snd_pcm_hw_params_get_channels(hwparams, &channels);
921 sample_spec->channels = (uint8_t)channels;
922 AUDIO_LOG_DEBUG("channels = %d\n", sample_spec->channels);
925 *buffer_size = _buffer_size;
928 *period_size = _period_size;
931 *use_mmap = _use_mmap;
936 return AUDIO_ERR_RESOURCE;
939 audio_return_t _pcm_set_sw_params(snd_pcm_t *pcm, snd_pcm_uframes_t avail_min, uint8_t period_event)
941 snd_pcm_sw_params_t *swparams;
942 snd_pcm_uframes_t boundary;
945 AUDIO_RETURN_VAL_IF_FAIL(pcm, AUDIO_ERR_PARAMETER);
947 snd_pcm_sw_params_alloca(&swparams);
949 if ((err = snd_pcm_sw_params_current(pcm, swparams)) < 0) {
950 AUDIO_LOG_WARN("Unable to determine current swparams: %s\n", snd_strerror(err));
953 if ((err = snd_pcm_sw_params_set_period_event(pcm, swparams, period_event)) < 0) {
954 AUDIO_LOG_WARN("Unable to disable period event: %s\n", snd_strerror(err));
957 if ((err = snd_pcm_sw_params_set_tstamp_mode(pcm, swparams, SND_PCM_TSTAMP_ENABLE)) < 0) {
958 AUDIO_LOG_WARN("Unable to enable time stamping: %s\n", snd_strerror(err));
961 if ((err = snd_pcm_sw_params_get_boundary(swparams, &boundary)) < 0) {
962 AUDIO_LOG_WARN("Unable to get boundary: %s\n", snd_strerror(err));
965 if ((err = snd_pcm_sw_params_set_stop_threshold(pcm, swparams, boundary)) < 0) {
966 AUDIO_LOG_WARN("Unable to set stop threshold: %s\n", snd_strerror(err));
969 if ((err = snd_pcm_sw_params_set_start_threshold(pcm, swparams, (snd_pcm_uframes_t) avail_min)) < 0) {
970 AUDIO_LOG_WARN("Unable to set start threshold: %s\n", snd_strerror(err));
973 if ((err = snd_pcm_sw_params_set_avail_min(pcm, swparams, avail_min)) < 0) {
974 AUDIO_LOG_WARN("snd_pcm_sw_params_set_avail_min() failed: %s", snd_strerror(err));
977 if ((err = snd_pcm_sw_params(pcm, swparams)) < 0) {
978 AUDIO_LOG_WARN("Unable to set sw params: %s\n", snd_strerror(err));