#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include <stdbool.h>
#include "tizen-audio-internal.h"
#include "tizen-audio-impl.h"
#define DEVICE_NAME_MAX 32
#endif
-#ifdef __USE_TINYALSA__
-/* Convert pcm format from pulse to alsa */
-static const uint32_t g_format_convert_table[] = {
- [AUDIO_SAMPLE_U8] = PCM_FORMAT_S8,
- [AUDIO_SAMPLE_S16LE] = PCM_FORMAT_S16_LE,
- [AUDIO_SAMPLE_S32LE] = PCM_FORMAT_S32_LE,
- [AUDIO_SAMPLE_S24_32LE] = PCM_FORMAT_S24_LE
-};
-#else /* alsa-lib */
+#ifndef __USE_TINYALSA__
/* FIXME : To avoid build warning... */
int _snd_pcm_poll_descriptor(snd_pcm_t *pcm);
-/* Convert pcm format from pulse to alsa */
-static const uint32_t g_format_convert_table[] = {
- [AUDIO_SAMPLE_U8] = SND_PCM_FORMAT_U8,
- [AUDIO_SAMPLE_ALAW] = SND_PCM_FORMAT_A_LAW,
- [AUDIO_SAMPLE_ULAW] = SND_PCM_FORMAT_MU_LAW,
- [AUDIO_SAMPLE_S16LE] = SND_PCM_FORMAT_S16_LE,
- [AUDIO_SAMPLE_S16BE] = SND_PCM_FORMAT_S16_BE,
- [AUDIO_SAMPLE_FLOAT32LE] = SND_PCM_FORMAT_FLOAT_LE,
- [AUDIO_SAMPLE_FLOAT32BE] = SND_PCM_FORMAT_FLOAT_BE,
- [AUDIO_SAMPLE_S32LE] = SND_PCM_FORMAT_S32_LE,
- [AUDIO_SAMPLE_S32BE] = SND_PCM_FORMAT_S32_BE,
- [AUDIO_SAMPLE_S24LE] = SND_PCM_FORMAT_S24_3LE,
- [AUDIO_SAMPLE_S24BE] = SND_PCM_FORMAT_S24_3BE,
- [AUDIO_SAMPLE_S24_32LE] = SND_PCM_FORMAT_S24_LE,
- [AUDIO_SAMPLE_S24_32BE] = SND_PCM_FORMAT_S24_BE
-};
#endif
-static uint32_t __convert_format(audio_sample_format_t format)
-{
- return g_format_convert_table[format];
-}
-
#ifdef __USE_TINYALSA__
static int __parse_card_device_number(const char *card, const char *device, unsigned int *card_u, unsigned int *device_u) {
AUDIO_RETURN_VAL_IF_FAIL(card, AUDIO_ERR_PARAMETER);
return 0;
}
-static struct pcm *__tinyalsa_open_device(const char *card, const char *device, audio_pcm_sample_spec_t *ss, size_t period_size, size_t period_count, uint32_t direction)
+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)
{
struct pcm *pcm = NULL;
struct pcm_config config;
}
#endif
-audio_return_t _pcm_open(const char *card, const char *device, uint32_t direction, void *sample_spec,
+audio_return_e _pcm_open(const char *card, const char *device, uint32_t direction, void *sample_spec,
uint32_t period_size, uint32_t periods, void **pcm_handle)
{
int err;
AUDIO_LOG_INFO("card(%s) device(%s) direction(%u) period_size(%u) periods(%u)",
card, device, direction, period_size, periods);
#ifdef __USE_TINYALSA__
- audio_pcm_sample_spec_t *ss;
-
- ss = (audio_pcm_sample_spec_t *)sample_spec;
- ss->format = __convert_format((audio_sample_format_t)ss->format);
+ audio_pcm_sample_spec_s *ss;
+ convert_hal_format_from_sample_spec(sample_spec, &ss);
*pcm_handle = __tinyalsa_open_device(card, device, ss, (size_t)period_size, (size_t)periods, direction);
if (*pcm_handle == NULL) {
AUDIO_LOG_ERROR("Error opening PCM device");
#else /* alsa-lib */
int mode;
- audio_return_t ret;
+ audio_return_e ret;
char device_name[DEVICE_NAME_MAX];
__make_alsa_device_name(card, device, device_name);
return AUDIO_RET_OK;
}
-audio_return_t _pcm_start(void *pcm_handle)
+audio_return_e _pcm_start(void *pcm_handle)
{
int err;
return AUDIO_RET_OK;
}
-audio_return_t _pcm_stop(void *pcm_handle)
+audio_return_e _pcm_stop(void *pcm_handle)
{
int err;
return AUDIO_RET_OK;
}
-audio_return_t _pcm_close(void *pcm_handle)
+audio_return_e _pcm_close(void *pcm_handle)
{
int err;
return AUDIO_RET_OK;
}
-audio_return_t _pcm_avail(void *pcm_handle, uint32_t *avail)
+audio_return_e _pcm_avail(void *pcm_handle, uint32_t *avail)
{
#ifdef __USE_TINYALSA__
struct timespec tspec;
return AUDIO_RET_OK;
}
-audio_return_t _pcm_write(void *pcm_handle, const void *buffer, uint32_t frames)
+audio_return_e _pcm_write(void *pcm_handle, const void *buffer, uint32_t frames)
{
#ifdef __USE_TINYALSA__
int err;
return AUDIO_RET_OK;
}
-audio_return_t _pcm_read(void *pcm_handle, void *buffer, uint32_t frames)
+audio_return_e _pcm_read(void *pcm_handle, void *buffer, uint32_t frames)
{
#ifdef __USE_TINYALSA__
int err;
return AUDIO_RET_OK;
}
-audio_return_t _pcm_get_fd(void *pcm_handle, int *fd)
+audio_return_e _pcm_get_fd(void *pcm_handle, int *fd)
{
/* we use an internal API of the (tiny)alsa library, so it causes warning message during compile */
#ifdef __USE_TINYALSA__
return AUDIO_RET_OK;
}
-audio_return_t _pcm_recover(void *pcm_handle, int revents)
+audio_return_e _pcm_recover(void *pcm_handle, int revents)
{
int state, err;
return AUDIO_RET_OK;
}
-audio_return_t _pcm_get_params(void *pcm_handle, uint32_t direction, void **sample_spec, uint32_t *period_size, uint32_t *periods)
+audio_return_e _pcm_get_params(void *pcm_handle, uint32_t direction, void *sample_spec, uint32_t *period_size, uint32_t *periods)
{
#ifdef __USE_TINYALSA__
- audio_pcm_sample_spec_t *ss;
+ audio_pcm_sample_spec_s *ss;
unsigned int _period_size, _buffer_size, _periods, _format, _rate, _channels;
unsigned int _start_threshold, _stop_threshold, _silence_threshold;
struct pcm_config *config;
- ss = (audio_pcm_sample_spec_t *)*sample_spec;
+ ss = (audio_pcm_sample_spec_s *)sample_spec;
/* we use an internal API of the tiny alsa library, so it causes warning message during compile */
_pcm_config(pcm_handle, &config);
pcm_handle, config->format, config->rate, config->channels, config->period_size, config->period_count, _buffer_size);
#else /* alsa-lib */
int err;
- audio_pcm_sample_spec_t *ss;
+ audio_pcm_sample_spec_s ss;
int dir;
- snd_pcm_uframes_t _period_size, _buffer_size;
+ snd_pcm_uframes_t _period_size = 0 , _buffer_size = 0;
snd_pcm_format_t _format;
- unsigned int _rate, _channels;
- snd_pcm_uframes_t _start_threshold, _stop_threshold, _silence_threshold, _avail_min;
- unsigned int _periods;
+ unsigned int _rate = 0, _channels = 0;
+ snd_pcm_uframes_t _start_threshold = 0, _stop_threshold = 0, _silence_threshold = 0, _avail_min = 0;
+ unsigned int _periods = 0;
snd_pcm_hw_params_t *hwparams;
snd_pcm_sw_params_t *swparams;
- ss = (audio_pcm_sample_spec_t *)*sample_spec;
-
snd_pcm_hw_params_alloca(&hwparams);
snd_pcm_sw_params_alloca(&swparams);
*period_size = _period_size;
*periods = _periods;
- ss->format = _format;
- ss->rate = _rate;
- ss->channels = _channels;
+
+ ss.rate = _rate;
+ ss.channels = _channels;
+ ss.format = _format;
+ convert_hal_format_to_sample_spec(&ss, sample_spec);
if ((err = snd_pcm_sw_params_current(pcm_handle, swparams)) < 0) {
AUDIO_LOG_ERROR("snd_pcm_sw_params_current() failed : %d", err);
AUDIO_LOG_ERROR("snd_pcm_sw_params_get_{start_threshold|stop_threshold|silence_threshold|avail_min}() failed : %d", err);
}
- AUDIO_LOG_DEBUG("_pcm_get_params (handle %p, format %d, rate %u, channels %u, period_size %lu, periods %u, buffer_size %lu)",
- pcm_handle, _format, _rate, _channels, _period_size, _periods, _buffer_size);
+ AUDIO_LOG_DEBUG("_pcm_get_params (handle %p, format %d, rate %u, channels %u, period_size %lu, periods %u, buffer_size %lu,",
+ pcm_handle, ss.format, ss.rate, ss.channels, _period_size, _periods, _buffer_size);
+
#endif
return AUDIO_RET_OK;
}
-audio_return_t _pcm_set_params(void *pcm_handle, uint32_t direction, void *sample_spec, uint32_t period_size, uint32_t periods)
+static int __set_format(void *pcm_handle, snd_pcm_hw_params_t *hwparams, snd_pcm_format_t *format)
+{
+ const snd_pcm_format_t formats[] = {
+ SND_PCM_FORMAT_U8,
+ SND_PCM_FORMAT_A_LAW,
+ SND_PCM_FORMAT_MU_LAW,
+ SND_PCM_FORMAT_S16_LE,
+ SND_PCM_FORMAT_S16_BE,
+ SND_PCM_FORMAT_FLOAT_LE,
+ SND_PCM_FORMAT_FLOAT_BE,
+ SND_PCM_FORMAT_S32_LE,
+ SND_PCM_FORMAT_S32_BE,
+ SND_PCM_FORMAT_S24_3LE,
+ SND_PCM_FORMAT_S24_3BE,
+ SND_PCM_FORMAT_S24_LE,
+ SND_PCM_FORMAT_S24_BE,
+ };
+ int i;
+
+ if (snd_pcm_hw_params_set_format(pcm_handle, hwparams, *format) >= 0)
+ return 0;
+
+ /* Try to find appropriate format */
+ for (i = 0; i < sizeof(formats) / sizeof(formats[0]); i++) {
+ if (snd_pcm_hw_params_set_format(pcm_handle, hwparams, formats[i]) >= 0) {
+ *format = formats[i];
+ AUDIO_LOG_INFO("Selected proper format automatically. format(%d)", formats[i]);
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+audio_return_e _pcm_set_params(void *pcm_handle, uint32_t direction, void *sample_spec, uint32_t period_size, uint32_t periods)
{
#ifdef __USE_TINYALSA__
/* Parameters are only acceptable in pcm_open() function */
AUDIO_LOG_DEBUG("_pcm_set_params");
#else /* alsa-lib */
int err;
- audio_pcm_sample_spec_t ss;
- snd_pcm_uframes_t _buffer_size;
snd_pcm_hw_params_t *hwparams;
snd_pcm_sw_params_t *swparams;
- ss = *(audio_pcm_sample_spec_t *)sample_spec;
+ snd_pcm_uframes_t period_size_near;
+ snd_pcm_uframes_t buffer_size;
+ snd_pcm_uframes_t buffer_size_near;
+ audio_pcm_sample_spec_s requested_ss;
+ audio_pcm_sample_spec_s selected_ss;
+ unsigned int ch;
snd_pcm_hw_params_alloca(&hwparams);
snd_pcm_sw_params_alloca(&swparams);
+ convert_hal_format_from_sample_spec(sample_spec, &requested_ss);
+ convert_hal_format_from_sample_spec(sample_spec, &selected_ss);
+
+ ch = selected_ss.channels;
+
/* Set hw params */
if ((err = snd_pcm_hw_params_any(pcm_handle, hwparams)) < 0) {
AUDIO_LOG_ERROR("snd_pcm_hw_params_any() failed : %d", err);
return AUDIO_ERR_PARAMETER;
}
- ss.format = __convert_format((audio_sample_format_t)ss.format);
- if ((err = snd_pcm_hw_params_set_format(pcm_handle, hwparams, ss.format)) < 0) {
- AUDIO_LOG_ERROR("snd_pcm_hw_params_set_format() failed : %d", err);
+ if ((err = __set_format(pcm_handle, hwparams, &selected_ss.format) < 0)) {
+ AUDIO_LOG_ERROR("Failed to set format.");
return AUDIO_ERR_PARAMETER;
}
- if ((err = snd_pcm_hw_params_set_rate(pcm_handle, hwparams, ss.rate, 0)) < 0) {
+ if ((err = snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams, &selected_ss.rate, NULL)) < 0) {
AUDIO_LOG_ERROR("snd_pcm_hw_params_set_rate() failed : %d", err);
return AUDIO_ERR_PARAMETER;
}
- if ((err = snd_pcm_hw_params_set_channels(pcm_handle, hwparams, ss.channels)) < 0) {
- AUDIO_LOG_ERROR("snd_pcm_hw_params_set_channels(%u) failed : %d", ss.channels, err);
+ if ((err = snd_pcm_hw_params_set_channels_near(pcm_handle, hwparams, &ch)) < 0) {
+ AUDIO_LOG_ERROR("snd_pcm_hw_params_set_channels(%u) failed : %d", selected_ss.channels, err);
return AUDIO_ERR_PARAMETER;
}
+ selected_ss.channels = ch;
+
+ if (requested_ss.rate != selected_ss.rate ||
+ requested_ss.format != selected_ss.format ||
+ requested_ss.channels != selected_ss.channels) {
+
+ uint32_t _period_size = selected_ss.rate * period_size / requested_ss.rate;
+
+ AUDIO_LOG_INFO("hwparam has been changed. rate(%d->%d), channels(%d->%d), format(%d->%d)",
+ requested_ss.rate, selected_ss.rate,
+ requested_ss.channels, selected_ss.channels,
+ requested_ss.format, selected_ss.format);
- if ((err = snd_pcm_hw_params_set_period_size(pcm_handle, hwparams, period_size, 0)) < 0) {
- AUDIO_LOG_ERROR("snd_pcm_hw_params_set_period_size(%u) failed : %d", period_size, err);
+ AUDIO_LOG_INFO("period_size must be calculated appropriately. period_size(%d->%d)",
+ period_size, _period_size);
+
+ period_size = _period_size;
+ }
+
+ if (snd_pcm_hw_params_set_periods_near(pcm_handle, hwparams, &periods, NULL) < 0) {
+ AUDIO_LOG_ERROR("snd_pcm_hw_params_set_periods_near failed : %d", err);
return AUDIO_ERR_PARAMETER;
}
- if ((err = snd_pcm_hw_params_set_periods(pcm_handle, hwparams, periods, 0)) < 0) {
- AUDIO_LOG_ERROR("snd_pcm_hw_params_set_periods(%u) failed : %d", periods, err);
+ period_size_near = period_size;
+ if (snd_pcm_hw_params_set_period_size_near(pcm_handle, hwparams, &period_size_near, NULL) < 0) {
+ AUDIO_LOG_ERROR("snd_pcm_hw_params_set_period_size_near(%u) failed : %d", period_size, err);
return AUDIO_ERR_PARAMETER;
}
+ AUDIO_LOG_INFO("requested period_size(%d). set period_size_near(%ld)", period_size, period_size_near);
+
+ period_size = period_size_near;
+ buffer_size = period_size * periods;
+ buffer_size_near = buffer_size;
- _buffer_size = period_size * periods;
- if ((err = snd_pcm_hw_params_set_buffer_size(pcm_handle, hwparams, _buffer_size)) < 0) {
- AUDIO_LOG_ERROR("snd_pcm_hw_params_set_buffer_size(%lu) failed : %d", _buffer_size, err);
+ if ((err = snd_pcm_hw_params_set_buffer_size_near(pcm_handle, hwparams, &buffer_size_near)) < 0) {
+ AUDIO_LOG_ERROR("snd_pcm_hw_params_set_buffer_size_near(%lu) failed : %d", buffer_size, err);
return AUDIO_ERR_PARAMETER;
}
+ AUDIO_LOG_INFO("requested buffer_size(%lu). set buffer_size(%lu)", buffer_size, buffer_size_near);
+
+ buffer_size = buffer_size_near;
+
if ((err = snd_pcm_hw_params(pcm_handle, hwparams)) < 0) {
AUDIO_LOG_ERROR("snd_pcm_hw_params failed : %d", err);
return AUDIO_ERR_IOCTL;
return AUDIO_ERR_PARAMETER;
}
- if ((err = snd_pcm_sw_params_set_start_threshold(pcm_handle, swparams, period_size / 2)) < 0) {
+ if ((err = snd_pcm_sw_params_set_start_threshold(pcm_handle, swparams, buffer_size)) < 0) {
AUDIO_LOG_ERROR("Unable to set start threshold : %d", err);
return AUDIO_ERR_PARAMETER;
}
- if ((err = snd_pcm_sw_params_set_avail_min(pcm_handle, swparams, 1024)) < 0) {
+ if ((err = snd_pcm_sw_params_set_avail_min(pcm_handle, swparams, period_size)) < 0) {
AUDIO_LOG_ERROR("snd_pcm_sw_params_set_avail_min() failed : %d", err);
return AUDIO_ERR_PARAMETER;
}
return AUDIO_ERR_IOCTL;
}
- AUDIO_LOG_DEBUG("_pcm_set_params (handle %p, format %d, rate %u, channels %u, period_size %u, periods %u, buffer_size %lu)",
- pcm_handle, ss.format, ss.rate, ss.channels, period_size, periods, _buffer_size);
+ convert_hal_format_to_sample_spec(&selected_ss, sample_spec);
+
+ AUDIO_LOG_DEBUG("_pcm_set_params (handle %p, format(%d), rate(%u), channels(%u), period_size(%u), periods(%u), buffer_size(%lu)",
+ pcm_handle, selected_ss.format, selected_ss.rate, selected_ss.channels, period_size, periods, buffer_size);
#endif
return AUDIO_RET_OK;
}
/* Generic snd pcm interface APIs */
-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)
+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)
{
- audio_return_t ret = AUDIO_RET_OK;
+ audio_return_e ret = AUDIO_RET_OK;
snd_pcm_hw_params_t *hwparams;
int err = 0;
int dir;
return AUDIO_ERR_RESOURCE;
}
-audio_return_t _pcm_set_sw_params(snd_pcm_t *pcm, snd_pcm_uframes_t avail_min, uint8_t period_event)
+audio_return_e _pcm_set_sw_params(snd_pcm_t *pcm, snd_pcm_uframes_t avail_min, uint8_t period_event)
{
snd_pcm_sw_params_t *swparams;
snd_pcm_uframes_t boundary;