From 4759ed39a6c11317a0e0f6583704a62d3688da29 Mon Sep 17 00:00:00 2001 From: Mok Jeongho Date: Mon, 7 Mar 2016 15:30:54 +0900 Subject: [PATCH] Initial audio hal for emulator [Version] Release 0.1.0 [Profile] Common [Issue Type] Initial Code Change-Id: Id435399f2a0d2c0fd4edadb149da543765055c05 --- LICENSE.Apache-2.0 | 202 +++++++++ Makefile.am | 17 + NOTICE | 3 + audio-hal-emul.manifest | 5 + autogen.sh | 10 + configure.ac | 60 +++ packaging/audio-hal-emul.spec | 50 +++ tizen-audio-comm.c | 91 +++++ tizen-audio-device.c | 930 ++++++++++++++++++++++++++++++++++++++++++ tizen-audio-internal.h | 311 ++++++++++++++ tizen-audio-util.c | 509 +++++++++++++++++++++++ tizen-audio-volume.c | 399 ++++++++++++++++++ tizen-audio.c | 263 ++++++++++++ tizen-audio.h | 135 ++++++ 14 files changed, 2985 insertions(+) create mode 100644 LICENSE.Apache-2.0 create mode 100644 Makefile.am create mode 100644 NOTICE create mode 100644 audio-hal-emul.manifest create mode 100755 autogen.sh create mode 100644 configure.ac create mode 100644 packaging/audio-hal-emul.spec create mode 100644 tizen-audio-comm.c create mode 100644 tizen-audio-device.c create mode 100644 tizen-audio-internal.h create mode 100644 tizen-audio-util.c create mode 100644 tizen-audio-volume.c create mode 100644 tizen-audio.c create mode 100644 tizen-audio.h diff --git a/LICENSE.Apache-2.0 b/LICENSE.Apache-2.0 new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/LICENSE.Apache-2.0 @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..6c46727 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,17 @@ +lib_LTLIBRARIES = libtizen-audio.la + +libtizen_audio_la_SOURCES = tizen-audio.c \ + tizen-audio-device.c \ + tizen-audio-volume.c \ + tizen-audio-comm.c \ + tizen-audio-util.c +libtizen_audio_la_LDFLAGS = $(AM_LDFLAGS) -disable-static -avoid-version +if USE_TINYALSA +libtizen_audio_la_LIBADD = $(AM_LDADD) $(ASOUNDLIB_LIBS) $(TINYALSA_LIBS) $(VCONF_LIBS) $(DLOG_LIBS) $(INIPARSER_LIBS) +libtizen_audio_la_CFLAGS = $(AM_CFLAGS) $(ASOUNDLIB_CFLAGS) $(TINYALSA_CFLAGS) $(VCONF_CFLAGS) $(DLOG_CFLAGS) $(INIPARSER_CFLAGS) -D__USE_TINYALSA__ +else +libtizen_audio_la_LIBADD = $(AM_LDADD) $(ASOUNDLIB_LIBS) $(VCONF_LIBS) $(DLOG_LIBS) $(INIPARSER_LIBS) +libtizen_audio_la_CFLAGS = $(AM_CFLAGS) $(ASOUNDLIB_CFLAGS) $(VCONF_CFLAGS) $(DLOG_CFLAGS) $(INIPARSER_CFLAGS) +endif +libtizen_audio_la_CFLAGS += -DUSE_DLOG + diff --git a/NOTICE b/NOTICE new file mode 100644 index 0000000..ccdad52 --- /dev/null +++ b/NOTICE @@ -0,0 +1,3 @@ +Copyright (c) Samsung Electronics Co., Ltd. All rights reserved. +Except as noted, this software is licensed under Apache License, Version 2. +Please, see the LICENSE file for Apache License terms and conditions. diff --git a/audio-hal-emul.manifest b/audio-hal-emul.manifest new file mode 100644 index 0000000..86dbb26 --- /dev/null +++ b/audio-hal-emul.manifest @@ -0,0 +1,5 @@ + + + + + diff --git a/autogen.sh b/autogen.sh new file mode 100755 index 0000000..8e229ef --- /dev/null +++ b/autogen.sh @@ -0,0 +1,10 @@ +#!/bin/sh + +# autogen.sh -- Autotools bootstrapping +# + +libtoolize --copy --force +aclocal && \ +autoheader && \ +autoconf && \ +automake --add-missing --copy diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..7b6ea5a --- /dev/null +++ b/configure.ac @@ -0,0 +1,60 @@ +AC_PREREQ([2.67]) + +AC_INIT([audio-hal-emul], [0.1]) +AM_INIT_AUTOMAKE([-Wall -Werror foreign]) +AC_CONFIG_HEADERS([config.h]) + +AC_CONFIG_MACRO_DIR([m4]) + +# Checks for programs. +m4_pattern_allow([AM_PROG_AR]) +AM_PROG_AR +AC_PROG_CC +AM_PROG_CC_C_O +AC_PROG_CXX +AC_PROG_LIBTOOL +AC_PROG_AWK +AC_PROG_CPP +AC_PROG_INSTALL +AC_PROG_LN_S +AC_PROG_MAKE_SET +PKG_PROG_PKG_CONFIG + +# Checks for libraries. + +PKG_CHECK_MODULES(ASOUNDLIB, alsa >= 1.0.24) +AC_SUBST(ASOUNDLIB_CFLAGS) +AC_SUBST(ASOUNDLIB_LIBS) + +if test $USE_TINYALSA = "1"; then +PKG_CHECK_MODULES(TINYALSA, tinyalsa) +AC_SUBST(TINYALSA_CFLAGS) +AC_SUBST(TINYALSA_LIBS) +AM_CONDITIONAL(USE_TINYALSA, true) +else +AM_CONDITIONAL(USE_TINYALSA, false) +fi + +PKG_CHECK_MODULES(VCONF, vconf) +AC_SUBST(VCONF_CFLAGS) +AC_SUBST(VCONF_LIBS) + +PKG_CHECK_MODULES(INIPARSER, iniparser) +AC_SUBST(INIPARSER_CFLAGS) +AC_SUBST(INIPARSER_LIBS) + +PKG_CHECK_MODULES(DLOG, dlog) +AC_SUBST(DLOG_CFLAGS) +AC_SUBST(DLOG_LIBS) + +# Checks for header files. + +# Checks for typedefs, structures, and compiler characteristics. + +# Checks for library functions. + + +AC_CONFIG_FILES([ \ + Makefile + ]) +AC_OUTPUT diff --git a/packaging/audio-hal-emul.spec b/packaging/audio-hal-emul.spec new file mode 100644 index 0000000..d073fda --- /dev/null +++ b/packaging/audio-hal-emul.spec @@ -0,0 +1,50 @@ +Name: audio-hal-emul +Summary: TIZEN Audio HAL for Emulator +Version: 0.1.0 +Release: 0 +Group: System/Libraries +License: Apache-2.0 +URL: http://tizen.org +Source0: audio-hal-emul-%{version}.tar.gz +BuildRequires: pkgconfig(vconf) +BuildRequires: pkgconfig(iniparser) +BuildRequires: pkgconfig(dlog) +BuildRequires: pkgconfig(alsa) +#BuildRequires: pkgconfig(tinyalsa) +Provides: libtizen-audio.so + +%description +TIZEN Audio HAL for Emulator + +%prep +%setup -q -n %{name}-%{version} + +%build +export CFLAGS="$CFLAGS -DTIZEN_DEBUG_ENABLE" +export CXXFLAGS="$CXXFLAGS -DTIZEN_DEBUG_ENABLE" +export FFLAGS="$FFLAGS -DTIZEN_DEBUG_ENABLE" + +export USE_TINYALSA="0" + +%autogen +%configure + +make %{?jobs:-j%jobs} + +%install +rm -rf %{buildroot} +mkdir -p %{buildroot}%{_datadir}/license +cp LICENSE.Apache-2.0 %{buildroot}%{_datadir}/license/%{name} +%make_install + +%post +/sbin/ldconfig + +%postun +/sbin/ldconfig + +%files +%manifest audio-hal-emul.manifest +%defattr(-,root,root,-) +%{_libdir}/libtizen-audio.so +%{_datadir}/license/%{name} diff --git a/tizen-audio-comm.c b/tizen-audio-comm.c new file mode 100644 index 0000000..d94aa22 --- /dev/null +++ b/tizen-audio-comm.c @@ -0,0 +1,91 @@ +/* + * audio-hal + * + * Copyright (c) 2000 - 2013 Samsung Electronics Co., Ltd. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "tizen-audio-internal.h" + +audio_return_t _audio_comm_send_message(audio_hal_t *ah, const char *name, int value) +{ + audio_return_t audio_ret = AUDIO_RET_OK; + + AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(name, AUDIO_ERR_PARAMETER); + + AUDIO_LOG_DEBUG("send message : name(%s), value(%d)", name, value); + if (ah->comm.msg_cb) { + ah->comm.msg_cb(name, value, ah->comm.user_data); + } + + return audio_ret; +} + +audio_return_t _audio_comm_set_message_callback(audio_hal_t *ah, message_cb callback, void *user_data) +{ + audio_return_t audio_ret = AUDIO_RET_OK; + + AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(callback, AUDIO_ERR_PARAMETER); + + ah->comm.msg_cb = callback; + ah->comm.user_data = user_data; + + AUDIO_LOG_DEBUG("message callback is set, callback(%p), user_data(%p)", ah->comm.msg_cb, ah->comm.user_data); + + return audio_ret; +} + +audio_return_t _audio_comm_unset_message_callback(audio_hal_t *ah) +{ + audio_return_t audio_ret = AUDIO_RET_OK; + + AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER); + + ah->comm.msg_cb = NULL; + ah->comm.user_data = NULL; + + AUDIO_LOG_DEBUG("message callback is unset"); + + return audio_ret; +} + +audio_return_t _audio_comm_init(audio_hal_t *ah) +{ + audio_return_t audio_ret = AUDIO_RET_OK; + + AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER); + + ah->comm.msg_cb = NULL; + ah->comm.user_data = NULL; + + return audio_ret; +} + +audio_return_t _audio_comm_deinit(audio_hal_t *ah) +{ + audio_return_t audio_ret = AUDIO_RET_OK; + + AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER); + + ah->comm.msg_cb = NULL; + ah->comm.user_data = NULL; + + return audio_ret; +} diff --git a/tizen-audio-device.c b/tizen-audio-device.c new file mode 100644 index 0000000..fff017d --- /dev/null +++ b/tizen-audio-device.c @@ -0,0 +1,930 @@ +/* + * audio-hal + * + * Copyright (c) 2000 - 2013 Samsung Electronics Co., Ltd. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include "tizen-audio-internal.h" + +/* #define DEBUG_TIMING */ + +static device_type_t outDeviceTypes[] = { + { AUDIO_DEVICE_OUT_SPEAKER, "Speaker" }, + { AUDIO_DEVICE_OUT_JACK, "Headphones" }, + { AUDIO_DEVICE_OUT_BT_SCO, "Bluetooth" }, + { AUDIO_DEVICE_OUT_AUX, "Line" }, + { AUDIO_DEVICE_OUT_HDMI, "HDMI" }, + { 0, 0 }, +}; + +static device_type_t inDeviceTypes[] = { + { AUDIO_DEVICE_IN_MAIN_MIC, "MainMic" }, + { AUDIO_DEVICE_IN_JACK, "HeadsetMic" }, + { AUDIO_DEVICE_IN_BT_SCO, "BT Mic" }, + { 0, 0 }, +}; + +static uint32_t convert_device_string_to_enum(const char* device_str, uint32_t direction) +{ + uint32_t device = 0; + + if (!strncmp(device_str, "builtin-speaker", MAX_NAME_LEN)) { + device = AUDIO_DEVICE_OUT_SPEAKER; + } else if (!strncmp(device_str, "builtin-receiver", MAX_NAME_LEN)) { + device = AUDIO_DEVICE_OUT_RECEIVER; + } else if ((!strncmp(device_str, "audio-jack", MAX_NAME_LEN)) && (direction == AUDIO_DIRECTION_OUT)) { + device = AUDIO_DEVICE_OUT_JACK; + } else if ((!strncmp(device_str, "bt", MAX_NAME_LEN)) && (direction == AUDIO_DIRECTION_OUT)) { + device = AUDIO_DEVICE_OUT_BT_SCO; + } else if (!strncmp(device_str, "aux", MAX_NAME_LEN)) { + device = AUDIO_DEVICE_OUT_AUX; + } else if (!strncmp(device_str, "hdmi", MAX_NAME_LEN)) { + device = AUDIO_DEVICE_OUT_HDMI; + } else if ((!strncmp(device_str, "builtin-mic", MAX_NAME_LEN))) { + device = AUDIO_DEVICE_IN_MAIN_MIC; + } else if ((!strncmp(device_str, "audio-jack", MAX_NAME_LEN)) && (direction == AUDIO_DIRECTION_IN)) { + device = AUDIO_DEVICE_IN_JACK; + } else if ((!strncmp(device_str, "bt", MAX_NAME_LEN)) && (direction == AUDIO_DIRECTION_IN)) { + device = AUDIO_DEVICE_IN_BT_SCO; + } else { + device = AUDIO_DEVICE_NONE; + } + AUDIO_LOG_INFO("device type(%s), enum(0x%x)", device_str, device); + return device; +} + +static audio_return_t set_devices(audio_hal_t *ah, device_info_t *devices, uint32_t num_of_devices) +{ + audio_return_t audio_ret = AUDIO_RET_OK; + uint32_t new_device = 0; + const char *active_devices[MAX_DEVICES] = {NULL,}; + int i = 0, j = 0, dev_idx = 0; + + AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(devices, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(num_of_devices, AUDIO_ERR_PARAMETER); + + if (num_of_devices > MAX_DEVICES) { + num_of_devices = MAX_DEVICES; + AUDIO_LOG_ERROR("error: num_of_devices"); + return AUDIO_ERR_PARAMETER; + } + + if (devices[0].direction == AUDIO_DIRECTION_OUT) { + ah->device.active_out &= 0x0; + if (ah->device.active_in) { + /* check the active in devices */ + for (j = 0; j < inDeviceTypes[j].type; j++) { + if (((ah->device.active_in & (~AUDIO_DEVICE_IN)) & inDeviceTypes[j].type)) + active_devices[dev_idx++] = inDeviceTypes[j].name; + } + } + } else if (devices[0].direction == AUDIO_DIRECTION_IN) { + ah->device.active_in &= 0x0; + if (ah->device.active_out) { + /* check the active out devices */ + for (j = 0; j < outDeviceTypes[j].type; j++) { + if (ah->device.active_out & outDeviceTypes[j].type) + active_devices[dev_idx++] = outDeviceTypes[j].name; + } + } + } + + for (i = 0; i < num_of_devices; i++) { + new_device = convert_device_string_to_enum(devices[i].type, devices[i].direction); + if (new_device & AUDIO_DEVICE_IN) { + for (j = 0; j < inDeviceTypes[j].type; j++) { + if (new_device == inDeviceTypes[j].type) { + active_devices[dev_idx++] = inDeviceTypes[j].name; + ah->device.active_in |= new_device; + } + } + } else { + for (j = 0; j < outDeviceTypes[j].type; j++) { + if (new_device == outDeviceTypes[j].type) { + active_devices[dev_idx++] = outDeviceTypes[j].name; + ah->device.active_out |= new_device; + } + } + } + } + + if (active_devices[0] == NULL) { + AUDIO_LOG_ERROR("Failed to set device: active device is NULL"); + return AUDIO_ERR_PARAMETER; + } + + return audio_ret; +} + +audio_return_t _audio_device_init(audio_hal_t *ah) +{ + AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER); + + ah->device.active_in = 0x0; + ah->device.active_out = 0x0; + ah->device.pcm_in = NULL; + ah->device.pcm_out = NULL; + pthread_mutex_init(&ah->device.pcm_lock, NULL); + ah->device.pcm_count = 0; + + return AUDIO_RET_OK; +} + +audio_return_t _audio_device_deinit(audio_hal_t *ah) +{ + AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER); + + return AUDIO_RET_OK; +} + +static audio_return_t _do_route_ap_playback_capture(audio_hal_t *ah, audio_route_info_t *route_info) +{ + audio_return_t audio_ret = AUDIO_RET_OK; + device_info_t *devices = NULL; + + AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(route_info, AUDIO_ERR_PARAMETER); + + devices = route_info->device_infos; + + AUDIO_LOG_INFO("do_route_ap_playback_capture++ "); + + audio_ret = set_devices(ah, devices, route_info->num_of_devices); + if (audio_ret) { + AUDIO_LOG_ERROR("Failed to set devices: error = 0x%x", audio_ret); + return audio_ret; + } + + return audio_ret; +} + +static audio_return_t _do_route_voip(audio_hal_t *ah, device_info_t *devices, int32_t num_of_devices) +{ + audio_return_t audio_ret = AUDIO_RET_OK; + + AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(devices, AUDIO_ERR_PARAMETER); + + AUDIO_LOG_INFO("do_route_voip++"); + + audio_ret = set_devices(ah, devices, num_of_devices); + if (audio_ret) { + AUDIO_LOG_ERROR("Failed to set devices: error = 0x%x", audio_ret); + return audio_ret; + } + + return audio_ret; +} + +static audio_return_t _do_route_reset(audio_hal_t *ah, uint32_t direction) +{ + audio_return_t audio_ret = AUDIO_RET_OK; + const char *active_devices[MAX_DEVICES] = {NULL,}; + int i = 0, dev_idx = 0; + + AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER); + + AUDIO_LOG_INFO("do_route_reset++, direction(0x%x)", direction); + + if (direction == AUDIO_DIRECTION_OUT) { + ah->device.active_out &= 0x0; + if (ah->device.active_in) { + /* check the active in devices */ + for (i = 0; i < inDeviceTypes[i].type; i++) { + if (((ah->device.active_in & (~AUDIO_DEVICE_IN)) & inDeviceTypes[i].type)) { + active_devices[dev_idx++] = inDeviceTypes[i].name; + AUDIO_LOG_INFO("added for in : %s", inDeviceTypes[i].name); + } + } + } + } else { + ah->device.active_in &= 0x0; + if (ah->device.active_out) { + /* check the active out devices */ + for (i = 0; i < outDeviceTypes[i].type; i++) { + if (ah->device.active_out & outDeviceTypes[i].type) { + active_devices[dev_idx++] = outDeviceTypes[i].name; + AUDIO_LOG_INFO("added for out : %s", outDeviceTypes[i].name); + } + } + } + } + + if (active_devices[0] == NULL) { + AUDIO_LOG_DEBUG("active device is NULL, no need to update."); + return AUDIO_RET_OK; + } + + return audio_ret; +} + +#define LOOPBACK_ARG_LATENCY_MSEC 30 +#define LOOPBACK_ARG_ADJUST_TIME_SEC 3 +audio_return_t audio_do_route(void *audio_handle, audio_route_info_t *info) +{ + audio_return_t audio_ret = AUDIO_RET_OK; + audio_hal_t *ah = (audio_hal_t *)audio_handle; + device_info_t *devices = NULL; + + AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(info, AUDIO_ERR_PARAMETER); + + AUDIO_LOG_INFO("role:%s", info->role); + + devices = info->device_infos; + + if (!strncmp("voip", info->role, MAX_NAME_LEN)) { + audio_ret = _do_route_voip(ah, devices, info->num_of_devices); + if (AUDIO_IS_ERROR(audio_ret)) { + AUDIO_LOG_WARN("set voip route return 0x%x", audio_ret); + } + } else if (!strncmp("reset", info->role, MAX_NAME_LEN)) { + audio_ret = _do_route_reset(ah, devices->direction); + if (AUDIO_IS_ERROR(audio_ret)) { + AUDIO_LOG_WARN("set reset return 0x%x", audio_ret); + } + } else { + /* send latency and adjust time for loopback */ + if (!strncmp("loopback", info->role, MAX_NAME_LEN)) { + _audio_comm_send_message(ah, "loopback::latency", LOOPBACK_ARG_LATENCY_MSEC); + _audio_comm_send_message(ah, "loopback::adjust_time", LOOPBACK_ARG_ADJUST_TIME_SEC); + } + /* need to prepare for "alarm","notification","emergency","voice-information","voice-recognition","ringtone" */ + audio_ret = _do_route_ap_playback_capture(ah, info); + if (AUDIO_IS_ERROR(audio_ret)) { + AUDIO_LOG_WARN("set playback route return 0x%x", audio_ret); + } + } + return audio_ret; +} + +audio_return_t audio_update_stream_connection_info(void *audio_handle, audio_stream_info_t *info, uint32_t is_connected) +{ + audio_return_t audio_ret = AUDIO_RET_OK; + audio_hal_t *ah = (audio_hal_t *)audio_handle; + + AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(info, AUDIO_ERR_PARAMETER); + + AUDIO_LOG_INFO("role:%s, direction:%u, idx:%u, is_connected:%d", info->role, info->direction, info->idx, is_connected); + + return audio_ret; +} + +audio_return_t audio_update_route_option(void *audio_handle, audio_route_option_t *option) +{ + audio_return_t audio_ret = AUDIO_RET_OK; + audio_hal_t *ah = (audio_hal_t *)audio_handle; + + AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(option, AUDIO_ERR_PARAMETER); + + AUDIO_LOG_INFO("role:%s, name:%s, value:%d", option->role, option->name, option->value); + + return audio_ret; +} + +#ifdef __USE_TINYALSA__ +static struct pcm *__tinyalsa_open_device(audio_pcm_sample_spec_t *ss, size_t period_size, size_t period_count, uint32_t direction) +{ + struct pcm *pcm = NULL; + struct pcm_config config; + + AUDIO_RETURN_NULL_IF_FAIL(ss); + + config.channels = ss->channels; + config.rate = ss->rate; + config.period_size = period_size; + config.period_count = period_count; + config.format = ss->format; + config.start_threshold = period_size; + config.stop_threshold = 0xFFFFFFFF; + config.silence_threshold = 0; + + 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); + + pcm = pcm_open((direction == AUDIO_DIRECTION_OUT) ? PLAYBACK_CARD_ID : CAPTURE_CARD_ID, + (direction == AUDIO_DIRECTION_OUT) ? PLAYBACK_PCM_DEVICE_ID : CAPTURE_PCM_DEVICE_ID, + (direction == AUDIO_DIRECTION_OUT) ? PCM_OUT : PCM_IN, + &config); + if (!pcm || !pcm_is_ready(pcm)) { + AUDIO_LOG_ERROR("Unable to open device (%s)", pcm_get_error(pcm)); + pcm_close(pcm); + return NULL; + } + + return pcm; +} +#endif + +audio_return_t audio_pcm_open(void *audio_handle, void **pcm_handle, uint32_t direction, void *sample_spec, uint32_t period_size, uint32_t periods) +{ +#ifdef __USE_TINYALSA__ + audio_hal_t *ah; + audio_pcm_sample_spec_t *ss; + int err; + + AUDIO_RETURN_VAL_IF_FAIL(audio_handle, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(pcm_handle, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(sample_spec, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL((period_size > 0), AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL((periods > 0), AUDIO_ERR_PARAMETER); + + ah = (audio_hal_t *)audio_handle; + ss = (audio_pcm_sample_spec_t *)sample_spec; + ss->format = _convert_format((audio_sample_format_t)ss->format); + + *pcm_handle = __tinyalsa_open_device(ss, (size_t)period_size, (size_t)periods, direction); + if (*pcm_handle == NULL) { + AUDIO_LOG_ERROR("Error opening PCM device"); + return AUDIO_ERR_RESOURCE; + } + + if ((err = pcm_prepare((struct pcm *)*pcm_handle)) != 0) { + AUDIO_LOG_ERROR("Error prepare PCM device : %d", err); + } + + ah->device.pcm_count++; + AUDIO_LOG_INFO("Opening PCM handle 0x%x", *pcm_handle); +#else /* alsa-lib */ + audio_hal_t *ah; + int err, mode; + char *device_name = NULL; + + AUDIO_RETURN_VAL_IF_FAIL(audio_handle, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(pcm_handle, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(sample_spec, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL((period_size > 0), AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL((periods > 0), AUDIO_ERR_PARAMETER); + + ah = (audio_hal_t *)audio_handle; + mode = SND_PCM_NONBLOCK | SND_PCM_NO_AUTO_RESAMPLE | SND_PCM_NO_AUTO_CHANNELS | SND_PCM_NO_AUTO_FORMAT; + + if (direction == AUDIO_DIRECTION_OUT) + device_name = PLAYBACK_PCM_DEVICE; + else if (direction == AUDIO_DIRECTION_IN) + device_name = CAPTURE_PCM_DEVICE; + else { + AUDIO_LOG_ERROR("Error get device_name, direction : %d", direction); + return AUDIO_ERR_RESOURCE; + } + + 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) { + AUDIO_LOG_ERROR("Error opening PCM device %s : %s", device_name, snd_strerror(err)); + return AUDIO_ERR_RESOURCE; + } + + if ((err = audio_pcm_set_params(audio_handle, *pcm_handle, direction, sample_spec, period_size, periods)) != AUDIO_RET_OK) { + AUDIO_LOG_ERROR("Failed to set pcm parameters : %d", err); + return err; + } + + ah->device.pcm_count++; + AUDIO_LOG_INFO("Opening PCM handle 0x%x, PCM device %s", *pcm_handle, device_name); +#endif + + return AUDIO_RET_OK; +} + +audio_return_t audio_pcm_start(void *audio_handle, void *pcm_handle) +{ + int err; + + AUDIO_RETURN_VAL_IF_FAIL(audio_handle, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(pcm_handle, AUDIO_ERR_PARAMETER); + +#ifdef __USE_TINYALSA__ + if ((err = pcm_start(pcm_handle)) < 0) { + AUDIO_LOG_ERROR("Error starting PCM handle : %d", err); + return AUDIO_ERR_RESOURCE; + } +#else /* alsa-lib */ + if ((err = snd_pcm_start(pcm_handle)) < 0) { + AUDIO_LOG_ERROR("Error starting PCM handle : %s", snd_strerror(err)); + return AUDIO_ERR_RESOURCE; + } +#endif + + AUDIO_LOG_INFO("PCM handle 0x%x start", pcm_handle); + return AUDIO_RET_OK; +} + +audio_return_t audio_pcm_stop(void *audio_handle, void *pcm_handle) +{ + int err; + + AUDIO_RETURN_VAL_IF_FAIL(audio_handle, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(pcm_handle, AUDIO_ERR_PARAMETER); + +#ifdef __USE_TINYALSA__ + if ((err = pcm_stop(pcm_handle)) < 0) { + AUDIO_LOG_ERROR("Error stopping PCM handle : %d", err); + return AUDIO_ERR_RESOURCE; + } +#else /* alsa-lib */ + if ((err = snd_pcm_drop(pcm_handle)) < 0) { + AUDIO_LOG_ERROR("Error stopping PCM handle : %s", snd_strerror(err)); + return AUDIO_ERR_RESOURCE; + } +#endif + + AUDIO_LOG_INFO("PCM handle 0x%x stop", pcm_handle); + return AUDIO_RET_OK; +} + +audio_return_t audio_pcm_close(void *audio_handle, void *pcm_handle) +{ + audio_hal_t *ah = (audio_hal_t *)audio_handle; + int err; + + AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(pcm_handle, AUDIO_ERR_PARAMETER); + + AUDIO_LOG_INFO("Try to close PCM handle 0x%x", pcm_handle); + +#ifdef __USE_TINYALSA__ + if ((err = pcm_close(pcm_handle)) < 0) { + AUDIO_LOG_ERROR("Error closing PCM handle : %d", err); + return AUDIO_ERR_RESOURCE; + } +#else /* alsa-lib */ + if ((err = snd_pcm_close(pcm_handle)) < 0) { + AUDIO_LOG_ERROR("Error closing PCM handle : %s", snd_strerror(err)); + return AUDIO_ERR_RESOURCE; + } +#endif + + pcm_handle = NULL; + ah->device.pcm_count--; + AUDIO_LOG_INFO("PCM handle close success (count:%d)", ah->device.pcm_count); + + return AUDIO_RET_OK; +} + +audio_return_t audio_pcm_avail(void *audio_handle, void *pcm_handle, uint32_t *avail) +{ +#ifdef __USE_TINYALSA__ + struct timespec tspec; + unsigned int frames_avail = 0; + int err; + + AUDIO_RETURN_VAL_IF_FAIL(audio_handle, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(pcm_handle, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(avail, AUDIO_ERR_PARAMETER); + + err = pcm_get_htimestamp(pcm_handle, &frames_avail, &tspec); + if (err < 0) { + AUDIO_LOG_ERROR("Could not get avail and timespec at PCM handle 0x%x : %d", pcm_handle, err); + return AUDIO_ERR_IOCTL; + } + +#ifdef DEBUG_TIMING + AUDIO_LOG_DEBUG("avail = %d", frames_avail); +#endif + + *avail = (uint32_t)frames_avail; +#else /* alsa-lib */ + snd_pcm_sframes_t frames_avail; + + AUDIO_RETURN_VAL_IF_FAIL(audio_handle, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(pcm_handle, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(avail, AUDIO_ERR_PARAMETER); + + if ((frames_avail = snd_pcm_avail(pcm_handle)) < 0) { + AUDIO_LOG_ERROR("Could not get avail at PCM handle 0x%x : %d", pcm_handle, frames_avail); + return AUDIO_ERR_IOCTL; + } + +#ifdef DEBUG_TIMING + AUDIO_LOG_DEBUG("avail = %d", frames_avail); +#endif + + *avail = (uint32_t)frames_avail; +#endif + + return AUDIO_RET_OK; +} + +audio_return_t audio_pcm_write(void *audio_handle, void *pcm_handle, const void *buffer, uint32_t frames) +{ +#ifdef __USE_TINYALSA__ + int err; + + AUDIO_RETURN_VAL_IF_FAIL(audio_handle, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(pcm_handle, AUDIO_ERR_PARAMETER); + + err = pcm_write(pcm_handle, buffer, pcm_frames_to_bytes(pcm_handle, (unsigned int)frames)); + if (err < 0) { + AUDIO_LOG_ERROR("Failed to write pcm : %d", err); + return AUDIO_ERR_IOCTL; + } + +#ifdef DEBUG_TIMING + AUDIO_LOG_DEBUG("audio_pcm_write = %d", frames); +#endif +#else /* alsa-lib */ + snd_pcm_sframes_t frames_written; + + AUDIO_RETURN_VAL_IF_FAIL(pcm_handle, AUDIO_ERR_PARAMETER); + + frames_written = snd_pcm_writei(pcm_handle, buffer, (snd_pcm_uframes_t) frames); + if (frames_written < 0) { + AUDIO_LOG_ERROR("Failed to write pcm : %d", frames_written); + return AUDIO_ERR_IOCTL; + } + +#ifdef DEBUG_TIMING + AUDIO_LOG_DEBUG("audio_pcm_write = (%d / %d)", frames_written, frames); +#endif +#endif + + return AUDIO_RET_OK; +} + +audio_return_t audio_pcm_read(void *audio_handle, void *pcm_handle, void *buffer, uint32_t frames) +{ +#ifdef __USE_TINYALSA__ + int err; + + AUDIO_RETURN_VAL_IF_FAIL(audio_handle, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(pcm_handle, AUDIO_ERR_PARAMETER); + + err = pcm_read(pcm_handle, buffer, pcm_frames_to_bytes(pcm_handle, (unsigned int)frames)); + if (err < 0) { + AUDIO_LOG_ERROR("Failed to read pcm : %d", err); + return AUDIO_ERR_IOCTL; + } + +#ifdef DEBUG_TIMING + AUDIO_LOG_DEBUG("audio_pcm_read = %d", frames); +#endif +#else /* alsa-lib */ + snd_pcm_sframes_t frames_read; + + AUDIO_RETURN_VAL_IF_FAIL(audio_handle, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(pcm_handle, AUDIO_ERR_PARAMETER); + + frames_read = snd_pcm_readi(pcm_handle, buffer, (snd_pcm_uframes_t)frames); + if (frames_read < 0) { + AUDIO_LOG_ERROR("Failed to read pcm : %d", frames_read); + return AUDIO_ERR_IOCTL; + } + +#ifdef DEBUG_TIMING + AUDIO_LOG_DEBUG("audio_pcm_read = (%d / %d)", frames_read, frames); +#endif +#endif + + return AUDIO_RET_OK; +} + +audio_return_t audio_pcm_get_fd(void *audio_handle, void *pcm_handle, int *fd) +{ + AUDIO_RETURN_VAL_IF_FAIL(audio_handle, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(pcm_handle, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(fd, AUDIO_ERR_PARAMETER); + /* we use an internal API of the (tiny)alsa library, so it causes warning message during compile */ +#ifdef __USE_TINYALSA__ + *fd = _pcm_poll_descriptor((struct pcm *)pcm_handle); +#else /* alsa-lib */ + *fd = _snd_pcm_poll_descriptor((snd_pcm_t *)pcm_handle); +#endif + return AUDIO_RET_OK; +} + +#ifdef __USE_TINYALSA__ +static int __tinyalsa_pcm_recover(struct pcm *pcm, int err) +{ + if (err > 0) + err = -err; + if (err == -EINTR) /* nothing to do, continue */ + return 0; + if (err == -EPIPE) { + AUDIO_LOG_INFO("XRUN occurred"); + err = pcm_prepare(pcm); + if (err < 0) { + AUDIO_LOG_ERROR("Could not recover from XRUN occurred, prepare failed : %d", err); + return err; + } + return 0; + } + if (err == -ESTRPIPE) { + /* tinyalsa does not support pcm resume, dont't care suspend case */ + AUDIO_LOG_ERROR("Could not recover from suspend : %d", err); + return err; + } + return err; +} +#endif + +audio_return_t audio_pcm_recover(void *audio_handle, void *pcm_handle, int revents) +{ + int state, err; + + AUDIO_RETURN_VAL_IF_FAIL(audio_handle, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(pcm_handle, AUDIO_ERR_PARAMETER); + + if (revents & POLLERR) + AUDIO_LOG_DEBUG("Got POLLERR from ALSA"); + if (revents & POLLNVAL) + AUDIO_LOG_DEBUG("Got POLLNVAL from ALSA"); + if (revents & POLLHUP) + AUDIO_LOG_DEBUG("Got POLLHUP from ALSA"); + if (revents & POLLPRI) + AUDIO_LOG_DEBUG("Got POLLPRI from ALSA"); + if (revents & POLLIN) + AUDIO_LOG_DEBUG("Got POLLIN from ALSA"); + if (revents & POLLOUT) + AUDIO_LOG_DEBUG("Got POLLOUT from ALSA"); + +#ifdef __USE_TINYALSA__ + state = pcm_state(pcm_handle); + AUDIO_LOG_DEBUG("PCM state is %d", state); + + switch (state) { + case PCM_STATE_XRUN: + if ((err = __tinyalsa_pcm_recover(pcm_handle, -EPIPE)) != 0) { + AUDIO_LOG_ERROR("Could not recover from POLLERR|POLLNVAL|POLLHUP and XRUN : %d", err); + return AUDIO_ERR_IOCTL; + } + break; + + case PCM_STATE_SUSPENDED: + if ((err = __tinyalsa_pcm_recover(pcm_handle, -ESTRPIPE)) != 0) { + AUDIO_LOG_ERROR("Could not recover from POLLERR|POLLNVAL|POLLHUP and SUSPENDED : %d", err); + return AUDIO_ERR_IOCTL; + } + break; + + default: + pcm_stop(pcm_handle); + if ((err = pcm_prepare(pcm_handle)) < 0) { + AUDIO_LOG_ERROR("Could not recover from POLLERR|POLLNVAL|POLLHUP with pcm_prepare() : %d", err); + return AUDIO_ERR_IOCTL; + } + } +#else /* alsa-lib */ + state = snd_pcm_state(pcm_handle); + AUDIO_LOG_DEBUG("PCM state is %s", snd_pcm_state_name(state)); + + /* Try to recover from this error */ + + switch (state) { + case SND_PCM_STATE_XRUN: + if ((err = snd_pcm_recover(pcm_handle, -EPIPE, 1)) != 0) { + AUDIO_LOG_ERROR("Could not recover from POLLERR|POLLNVAL|POLLHUP and XRUN : %d", err); + return AUDIO_ERR_IOCTL; + } + break; + + case SND_PCM_STATE_SUSPENDED: + if ((err = snd_pcm_recover(pcm_handle, -ESTRPIPE, 1)) != 0) { + AUDIO_LOG_ERROR("Could not recover from POLLERR|POLLNVAL|POLLHUP and SUSPENDED : %d", err); + return AUDIO_ERR_IOCTL; + } + break; + + default: + snd_pcm_drop(pcm_handle); + if ((err = snd_pcm_prepare(pcm_handle)) < 0) { + AUDIO_LOG_ERROR("Could not recover from POLLERR|POLLNVAL|POLLHUP with snd_pcm_prepare() : %d", err); + return AUDIO_ERR_IOCTL; + } + break; + } +#endif + + AUDIO_LOG_DEBUG("audio_pcm_recover"); + return AUDIO_RET_OK; +} + +audio_return_t audio_pcm_get_params(void *audio_handle, 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; + unsigned int _period_size, _buffer_size, _periods, _format, _rate, _channels; + unsigned int _start_threshold, _stop_threshold, _silence_threshold; + struct pcm_config *config; + + AUDIO_RETURN_VAL_IF_FAIL(audio_handle, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(pcm_handle, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(sample_spec, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(period_size, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(periods, AUDIO_ERR_PARAMETER); + ss = (audio_pcm_sample_spec_t *)*sample_spec; + + /* we use an internal API of the tiny alsa library, so it causes warning message during compile */ + _pcm_config(pcm_handle, &config); + + *period_size = config->period_size; + *periods = config->period_count; + _buffer_size = config->period_size * config->period_count; + ss->format = config->format; + ss->rate = config->rate; + ss->channels = config->channels; + _start_threshold = config->start_threshold; + _stop_threshold = config->stop_threshold; + _silence_threshold = config->silence_threshold; + + AUDIO_LOG_DEBUG("audio_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); +#else /* alsa-lib */ + int err; + audio_pcm_sample_spec_t *ss; + int dir; + snd_pcm_uframes_t _period_size, _buffer_size; + snd_pcm_format_t _format; + unsigned int _rate, _channels; + snd_pcm_uframes_t _start_threshold, _stop_threshold, _silence_threshold, _avail_min; + unsigned int _periods; + snd_pcm_hw_params_t *hwparams; + snd_pcm_sw_params_t *swparams; + + AUDIO_RETURN_VAL_IF_FAIL(audio_handle, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(pcm_handle, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(sample_spec, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(period_size, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(periods, AUDIO_ERR_PARAMETER); + ss = (audio_pcm_sample_spec_t *)*sample_spec; + + snd_pcm_hw_params_alloca(&hwparams); + snd_pcm_sw_params_alloca(&swparams); + + if ((err = snd_pcm_hw_params_current(pcm_handle, hwparams)) < 0) { + AUDIO_LOG_ERROR("snd_pcm_hw_params_current() failed : %d", err); + return AUDIO_ERR_PARAMETER; + } + + if ((err = snd_pcm_hw_params_get_period_size(hwparams, &_period_size, &dir)) < 0 || + (err = snd_pcm_hw_params_get_buffer_size(hwparams, &_buffer_size)) < 0 || + (err = snd_pcm_hw_params_get_periods(hwparams, &_periods, &dir)) < 0 || + (err = snd_pcm_hw_params_get_format(hwparams, &_format)) < 0 || + (err = snd_pcm_hw_params_get_rate(hwparams, &_rate, &dir)) < 0 || + (err = snd_pcm_hw_params_get_channels(hwparams, &_channels)) < 0) { + AUDIO_LOG_ERROR("snd_pcm_hw_params_get_{period_size|buffer_size|periods|format|rate|channels}() failed : %s", err); + return AUDIO_ERR_PARAMETER; + } + + *period_size = _period_size; + *periods = _periods; + ss->format = _format; + ss->rate = _rate; + ss->channels = _channels; + + if ((err = snd_pcm_sw_params_current(pcm_handle, swparams)) < 0) { + AUDIO_LOG_ERROR("snd_pcm_sw_params_current() failed : %d", err); + return AUDIO_ERR_PARAMETER; + } + + if ((err = snd_pcm_sw_params_get_start_threshold(swparams, &_start_threshold)) < 0 || + (err = snd_pcm_sw_params_get_stop_threshold(swparams, &_stop_threshold)) < 0 || + (err = snd_pcm_sw_params_get_silence_threshold(swparams, &_silence_threshold)) < 0 || + (err = snd_pcm_sw_params_get_avail_min(swparams, &_avail_min)) < 0) { + AUDIO_LOG_ERROR("snd_pcm_sw_params_get_{start_threshold|stop_threshold|silence_threshold|avail_min}() failed : %s", err); + } + + AUDIO_LOG_DEBUG("audio_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); +#endif + + return AUDIO_RET_OK; +} + +audio_return_t audio_pcm_set_params(void *audio_handle, 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("audio_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; + + AUDIO_RETURN_VAL_IF_FAIL(audio_handle, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(pcm_handle, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(sample_spec, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(period_size, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(periods, AUDIO_ERR_PARAMETER); + ss = *(audio_pcm_sample_spec_t *)sample_spec; + + snd_pcm_hw_params_alloca(&hwparams); + snd_pcm_sw_params_alloca(&swparams); + + /* 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; + } + + if ((err = snd_pcm_hw_params_set_rate_resample(pcm_handle, hwparams, 0)) < 0) { + AUDIO_LOG_ERROR("snd_pcm_hw_params_set_rate_resample() failed : %d", err); + return AUDIO_ERR_PARAMETER; + } + + if ((err = snd_pcm_hw_params_set_access(pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) { + AUDIO_LOG_ERROR("snd_pcm_hw_params_set_access() 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); + return AUDIO_ERR_PARAMETER; + } + + if ((err = snd_pcm_hw_params_set_rate(pcm_handle, hwparams, ss.rate, 0)) < 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", err); + return AUDIO_ERR_PARAMETER; + } + + 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", 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); + return AUDIO_ERR_PARAMETER; + } + + _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(%u) failed : %d", periods * periods, err); + return AUDIO_ERR_PARAMETER; + } + + if ((err = snd_pcm_hw_params(pcm_handle, hwparams)) < 0) { + AUDIO_LOG_ERROR("snd_pcm_hw_params failed : %d", err); + return AUDIO_ERR_IOCTL; + } + + /* Set sw params */ + if ((err = snd_pcm_sw_params_current(pcm_handle, swparams)) < 0) { + AUDIO_LOG_ERROR("Unable to determine current swparams : %d", err); + return AUDIO_ERR_PARAMETER; + } + + if ((err = snd_pcm_sw_params_set_tstamp_mode(pcm_handle, swparams, SND_PCM_TSTAMP_ENABLE)) < 0) { + AUDIO_LOG_ERROR("Unable to enable time stamping : %d", err); + return AUDIO_ERR_PARAMETER; + } + + if ((err = snd_pcm_sw_params_set_stop_threshold(pcm_handle, swparams, 0xFFFFFFFF)) < 0) { + AUDIO_LOG_ERROR("Unable to set stop threshold : %d", err); + return AUDIO_ERR_PARAMETER; + } + + if ((err = snd_pcm_sw_params_set_start_threshold(pcm_handle, swparams, period_size / 2)) < 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) { + AUDIO_LOG_ERROR("snd_pcm_sw_params_set_avail_min() failed : %d", err); + return AUDIO_ERR_PARAMETER; + } + + if ((err = snd_pcm_sw_params(pcm_handle, swparams)) < 0) { + AUDIO_LOG_ERROR("Unable to set sw params : %d", err); + return AUDIO_ERR_IOCTL; + } + + /* Prepare device */ + if ((err = snd_pcm_prepare(pcm_handle)) < 0) { + AUDIO_LOG_ERROR("snd_pcm_prepare() failed : %d", err); + return AUDIO_ERR_IOCTL; + } + + AUDIO_LOG_DEBUG("audio_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); +#endif + + return AUDIO_RET_OK; +} diff --git a/tizen-audio-internal.h b/tizen-audio-internal.h new file mode 100644 index 0000000..1ded637 --- /dev/null +++ b/tizen-audio-internal.h @@ -0,0 +1,311 @@ +#ifndef footizenaudiointernalfoo +#define footizenaudiointernalfoo + +/* + * audio-hal + * + * Copyright (c) 2000 - 2013 Samsung Electronics Co., Ltd. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include +#include +#include +#ifdef __USE_TINYALSA__ +#include +#endif +#include +#include +#include "tizen-audio.h" + +/* Debug */ + +//#define AUDIO_DEBUG +#define PROPERTY_VALUE_MAX 92 +#define BUF_SIZE 1024 +#define AUDIO_DUMP_STR_LEN 256 +#define AUDIO_DEVICE_INFO_LIST_MAX 16 +#ifdef USE_DLOG +#ifdef DLOG_TAG +#undef DLOG_TAG +#endif +#define DLOG_TAG "AUDIO_HAL" +#define AUDIO_LOG_ERROR(...) SLOG(LOG_ERROR, DLOG_TAG, __VA_ARGS__) +#define AUDIO_LOG_WARN(...) SLOG(LOG_WARN, DLOG_TAG, __VA_ARGS__) +#define AUDIO_LOG_INFO(...) SLOG(LOG_INFO, DLOG_TAG, __VA_ARGS__) +#define AUDIO_LOG_DEBUG(...) SLOG(LOG_DEBUG, DLOG_TAG, __VA_ARGS__) +#define AUDIO_LOG_VERBOSE(...) SLOG(LOG_DEBUG, DLOG_TAG, __VA_ARGS__) +#else +#define AUDIO_LOG_ERROR(...) fprintf(stderr, __VA_ARGS__) +#define AUDIO_LOG_WARN(...) fprintf(stderr, __VA_ARGS__) +#define AUDIO_LOG_INFO(...) fprintf(stdout, __VA_ARGS__) +#define AUDIO_LOG_DEBUG(...) fprintf(stdout, __VA_ARGS__) +#define AUDIO_LOG_VERBOSE(...) fprintf(stdout, __VA_ARGS__) +#endif + +#define AUDIO_RETURN_IF_FAIL(expr) do { \ + if (!expr) { \ + AUDIO_LOG_ERROR("%s failed", #expr); \ + return; \ + } \ +} while (0) +#define AUDIO_RETURN_VAL_IF_FAIL(expr, val) do { \ + if (!expr) { \ + AUDIO_LOG_ERROR("%s failed", #expr); \ + return val; \ + } \ +} while (0) +#define AUDIO_RETURN_NULL_IF_FAIL(expr) do { \ + if (!expr) { \ + AUDIO_LOG_ERROR("%s failed", #expr); \ + return NULL; \ + } \ +} while (0) + +/* Devices : Normal */ +#define AUDIO_DEVICE_OUT 0x00000000 +#define AUDIO_DEVICE_IN 0x80000000 +enum audio_device_type { + AUDIO_DEVICE_NONE = 0, + + /* output devices */ + AUDIO_DEVICE_OUT_SPEAKER = AUDIO_DEVICE_OUT | 0x00000001, + AUDIO_DEVICE_OUT_RECEIVER = AUDIO_DEVICE_OUT | 0x00000002, + AUDIO_DEVICE_OUT_JACK = AUDIO_DEVICE_OUT | 0x00000004, + AUDIO_DEVICE_OUT_BT_SCO = AUDIO_DEVICE_OUT | 0x00000008, + AUDIO_DEVICE_OUT_AUX = AUDIO_DEVICE_OUT | 0x00000010, + AUDIO_DEVICE_OUT_HDMI = AUDIO_DEVICE_OUT | 0x00000020, + AUDIO_DEVICE_OUT_ALL = (AUDIO_DEVICE_OUT_SPEAKER | + AUDIO_DEVICE_OUT_RECEIVER | + AUDIO_DEVICE_OUT_JACK | + AUDIO_DEVICE_OUT_BT_SCO | + AUDIO_DEVICE_OUT_AUX | + AUDIO_DEVICE_OUT_HDMI), + /* input devices */ + AUDIO_DEVICE_IN_MAIN_MIC = AUDIO_DEVICE_IN | 0x00000001, + AUDIO_DEVICE_IN_SUB_MIC = AUDIO_DEVICE_IN | 0x00000002, + AUDIO_DEVICE_IN_JACK = AUDIO_DEVICE_IN | 0x00000004, + AUDIO_DEVICE_IN_BT_SCO = AUDIO_DEVICE_IN | 0x00000008, + AUDIO_DEVICE_IN_ALL = (AUDIO_DEVICE_IN_MAIN_MIC | + AUDIO_DEVICE_IN_SUB_MIC | + AUDIO_DEVICE_IN_JACK | + AUDIO_DEVICE_IN_BT_SCO), +}; + +typedef struct device_type { + uint32_t type; + const char *name; +} device_type_t; + +#define streq !strcmp +#define strneq strcmp + +#define ALSA_DEFAULT_CARD "sdp_scard_audio" +#define ALSA_CARD0 "hw:0" +#define ALSA_CARD1 "hw:1" +#define PLAYBACK_PCM_DEVICE "hw:0,0" +#define CAPTURE_PCM_DEVICE "hw:0,0" + +/* hw:0,0 */ +#define PLAYBACK_CARD_ID 0 +#define PLAYBACK_PCM_DEVICE_ID 0 + +/* hw:0,0 */ +#define CAPTURE_CARD_ID 0 +#define CAPTURE_PCM_DEVICE_ID 0 + +#define MAX_DEVICES 5 +#define MAX_NAME_LEN 32 + +/* type definitions */ +typedef signed char int8_t; + +/* pcm */ +typedef struct { + snd_pcm_format_t format; + uint32_t rate; + uint8_t channels; +} audio_pcm_sample_spec_t; + +/* Device */ +typedef enum audio_device_api { + AUDIO_DEVICE_API_UNKNOWN, + AUDIO_DEVICE_API_ALSA, + AUDIO_DEVICE_API_BLUEZ, +} audio_device_api_t; + +typedef struct audio_device_alsa_info { + char *card_name; + uint32_t card_idx; + uint32_t device_idx; +} audio_device_alsa_info_t; + +typedef struct audio_device_bluz_info { + char *protocol; + uint32_t nrec; +} audio_device_bluez_info_t; + +typedef struct audio_device_info { + audio_device_api_t api; + audio_direction_t direction; + char *name; + uint8_t is_default_device; + union { + audio_device_alsa_info_t alsa; + audio_device_bluez_info_t bluez; + }; +} audio_device_info_t; + +typedef struct audio_hal_device { + uint32_t active_in; + uint32_t active_out; + snd_pcm_t *pcm_in; + snd_pcm_t *pcm_out; + pthread_mutex_t pcm_lock; + uint32_t pcm_count; +} audio_hal_device_t; + +/* Stream */ +#define AUDIO_VOLUME_LEVEL_MAX 16 + +typedef enum audio_volume { + AUDIO_VOLUME_TYPE_SYSTEM, /**< System volume type */ + AUDIO_VOLUME_TYPE_NOTIFICATION, /**< Notification volume type */ + AUDIO_VOLUME_TYPE_ALARM, /**< Alarm volume type */ + AUDIO_VOLUME_TYPE_RINGTONE, /**< Ringtone volume type */ + AUDIO_VOLUME_TYPE_MEDIA, /**< Media volume type */ + AUDIO_VOLUME_TYPE_CALL, /**< Call volume type */ + AUDIO_VOLUME_TYPE_VOIP, /**< VOIP volume type */ + AUDIO_VOLUME_TYPE_VOICE, /**< Voice volume type */ + AUDIO_VOLUME_TYPE_MAX, /**< Volume type count */ +} audio_volume_t; + +typedef enum audio_gain { + AUDIO_GAIN_TYPE_DEFAULT, + AUDIO_GAIN_TYPE_DIALER, + AUDIO_GAIN_TYPE_TOUCH, + AUDIO_GAIN_TYPE_AF, + AUDIO_GAIN_TYPE_SHUTTER1, + AUDIO_GAIN_TYPE_SHUTTER2, + AUDIO_GAIN_TYPE_CAMCODING, + AUDIO_GAIN_TYPE_MIDI, + AUDIO_GAIN_TYPE_BOOTING, + AUDIO_GAIN_TYPE_VIDEO, + AUDIO_GAIN_TYPE_TTS, + AUDIO_GAIN_TYPE_MAX, +} audio_gain_t; + +typedef struct audio_volume_value_table { + double volume[AUDIO_VOLUME_TYPE_MAX][AUDIO_VOLUME_LEVEL_MAX]; + uint32_t volume_level_max[AUDIO_VOLUME_LEVEL_MAX]; + double gain[AUDIO_GAIN_TYPE_MAX]; +} audio_volume_value_table_t; + +enum { + AUDIO_VOLUME_DEVICE_DEFAULT, + AUDIO_VOLUME_DEVICE_MAX, +}; + +typedef struct audio_hal_volume { + uint32_t volume_level[AUDIO_VOLUME_TYPE_MAX]; + audio_volume_value_table_t *volume_value_table; +} audio_hal_volume_t; + +typedef struct audio_hal_mixer { + snd_mixer_t *mixer; + pthread_mutex_t mutex; + struct { + snd_ctl_elem_value_t *value; + snd_ctl_elem_id_t *id; + snd_ctl_elem_info_t *info; + } control; +} audio_hal_mixer_t; + +/* Audio format */ +typedef enum audio_sample_format { + AUDIO_SAMPLE_U8, + AUDIO_SAMPLE_ALAW, + AUDIO_SAMPLE_ULAW, + AUDIO_SAMPLE_S16LE, + AUDIO_SAMPLE_S16BE, + AUDIO_SAMPLE_FLOAT32LE, + AUDIO_SAMPLE_FLOAT32BE, + AUDIO_SAMPLE_S32LE, + AUDIO_SAMPLE_S32BE, + AUDIO_SAMPLE_S24LE, + AUDIO_SAMPLE_S24BE, + AUDIO_SAMPLE_S24_32LE, + AUDIO_SAMPLE_S24_32BE, + AUDIO_SAMPLE_MAX, + AUDIO_SAMPLE_INVALID = -1 +} audio_sample_format_t; + +typedef struct audio_hal_comm { + message_cb msg_cb; + void *user_data; +} audio_hal_comm_t; + +/* Overall */ +typedef struct audio_hal { + audio_hal_device_t device; + audio_hal_volume_t volume; + audio_hal_mixer_t mixer; + audio_hal_comm_t comm; +} audio_hal_t; + +typedef struct { + unsigned short is_open; /* if is_open is true, open device; else close device.*/ + unsigned short is_headphone; + unsigned int is_downlink_mute; + unsigned int is_uplink_mute; +} device_ctrl_t; + +typedef struct samplerate_ctrl { + unsigned int samplerate; /* change samplerate.*/ +} set_samplerate_t; + +audio_return_t _audio_volume_init(audio_hal_t *ah); +audio_return_t _audio_volume_deinit(audio_hal_t *ah); +audio_return_t _audio_device_init(audio_hal_t *ah); +audio_return_t _audio_device_deinit(audio_hal_t *ah); +audio_return_t _audio_comm_init(audio_hal_t *ah); +audio_return_t _audio_comm_deinit(audio_hal_t *ah); +audio_return_t _audio_util_init(audio_hal_t *ah); +audio_return_t _audio_util_deinit(audio_hal_t *ah); +audio_return_t _audio_comm_send_message(audio_hal_t *ah, const char *name, int value); +audio_return_t _audio_comm_set_message_callback(audio_hal_t *ah, message_cb callback, void *user_data); +audio_return_t _audio_comm_unset_message_callback(audio_hal_t *ah); +audio_return_t _audio_mixer_control_set_param(audio_hal_t *ah, const char* ctl_name, snd_ctl_elem_value_t* value, int size); +audio_return_t _audio_mixer_control_set_value(audio_hal_t *ah, const char *card, const char *ctl_name, int val); +audio_return_t _audio_mixer_control_set_value_string(audio_hal_t *ah, const char* ctl_name, const char* value); +audio_return_t _audio_mixer_control_get_value(audio_hal_t *ah, const char *card, const char *ctl_name, int *val); +audio_return_t _audio_mixer_control_get_element(audio_hal_t *ah, const char *ctl_name, snd_hctl_elem_t **elem); +audio_return_t _audio_pcm_set_sw_params(snd_pcm_t *pcm, snd_pcm_uframes_t avail_min, uint8_t period_event); +audio_return_t _audio_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); +uint32_t _convert_format(audio_sample_format_t format); + +typedef struct _dump_data { + char *strbuf; + int left; + char *p; +} dump_data_t; + +dump_data_t* dump_new(int length); +void dump_add_str(dump_data_t *dump, const char *fmt, ...); +char* dump_get_str(dump_data_t *dump); +void dump_free(dump_data_t *dump); + +#endif diff --git a/tizen-audio-util.c b/tizen-audio-util.c new file mode 100644 index 0000000..e480bd1 --- /dev/null +++ b/tizen-audio-util.c @@ -0,0 +1,509 @@ +/* + * audio-hal + * + * Copyright (c) 2000 - 2013 Samsung Electronics Co., Ltd. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include "tizen-audio-internal.h" + +audio_return_t _audio_util_init(audio_hal_t *ah) +{ + AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER); + + pthread_mutex_init(&(ah->mixer.mutex), NULL); + return AUDIO_RET_OK; +} + +audio_return_t _audio_util_deinit(audio_hal_t *ah) +{ + AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER); + + pthread_mutex_destroy(&(ah->mixer.mutex)); + return AUDIO_RET_OK; +} + +#ifdef __MIXER_PARAM_DUMP + +static void __dump_mixer_param(char *dump, long *param, int size) +{ + int i, len; + + for (i = 0; i < size; i++) { + len = sprintf(dump, "%ld", *param); + if (len > 0) + dump += len; + if (i != size -1) { + *dump++ = ','; + } + + param++; + } + *dump = '\0'; +} + +#endif + +audio_return_t _audio_mixer_control_set_param(audio_hal_t *ah, const char* ctl_name, snd_ctl_elem_value_t* param, int size) +{ + AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER); + + /* TODO. */ + return AUDIO_RET_OK; +} + +audio_return_t audio_mixer_control_get_value(audio_hal_t *ah, const char *ctl_name, int *val) +{ + audio_return_t audio_ret = AUDIO_RET_OK; + + AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(ctl_name, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(val, AUDIO_ERR_PARAMETER); + + audio_ret = _audio_mixer_control_get_value(ah, ALSA_DEFAULT_CARD, ctl_name, val); + return audio_ret; +} + +audio_return_t _audio_mixer_control_get_value(audio_hal_t *ah, const char *card, const char *ctl_name, int *val) +{ + snd_ctl_t *handle; + snd_ctl_elem_value_t *control; + snd_ctl_elem_id_t *id; + snd_ctl_elem_info_t *info; + snd_ctl_elem_type_t type; + + int ret = 0, count = 0, i = 0; + + AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(card, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(ctl_name, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(val, AUDIO_ERR_PARAMETER); + + pthread_mutex_lock(&(ah->mixer.mutex)); + + ret = snd_ctl_open(&handle, card, 0); + if (ret < 0) { + AUDIO_LOG_ERROR("snd_ctl_open error, %s\n", snd_strerror(ret)); + pthread_mutex_unlock(&(ah->mixer.mutex)); + return AUDIO_ERR_IOCTL; + } + + // Get Element Info + + snd_ctl_elem_id_alloca(&id); + snd_ctl_elem_info_alloca(&info); + snd_ctl_elem_value_alloca(&control); + + snd_ctl_elem_id_set_interface(id, SND_CTL_ELEM_IFACE_MIXER); + snd_ctl_elem_id_set_name(id, ctl_name); + + snd_ctl_elem_info_set_id(info, id); + if (snd_ctl_elem_info(handle, info) < 0) { + AUDIO_LOG_ERROR("Cannot find control element: %s\n", ctl_name); + goto close; + } + snd_ctl_elem_info_get_id(info, id); + + type = snd_ctl_elem_info_get_type(info); + count = snd_ctl_elem_info_get_count(info); + + snd_ctl_elem_value_set_id(control, id); + + if (snd_ctl_elem_read(handle, control) < 0) { + AUDIO_LOG_ERROR("snd_ctl_elem_read failed \n"); + goto close; +} + + switch (type) { + case SND_CTL_ELEM_TYPE_BOOLEAN: + *val = snd_ctl_elem_value_get_boolean(control, i); + break; + case SND_CTL_ELEM_TYPE_INTEGER: + for (i = 0; i < count; i++) + *val = snd_ctl_elem_value_get_integer(control, i); + break; + case SND_CTL_ELEM_TYPE_ENUMERATED: + for (i = 0; i < count; i++) + *val = snd_ctl_elem_value_get_enumerated(control, i); + break; + default: + AUDIO_LOG_WARN("unsupported control element type\n"); + goto close; + } + + snd_ctl_close(handle); + +#ifdef AUDIO_DEBUG + AUDIO_LOG_INFO("get mixer(%s) = %d success", ctl_name, *val); +#endif + + pthread_mutex_unlock(&(ah->mixer.mutex)); + return AUDIO_RET_OK; + +close: + AUDIO_LOG_ERROR("Error\n"); + snd_ctl_close(handle); + pthread_mutex_unlock(&(ah->mixer.mutex)); + return AUDIO_ERR_UNDEFINED; +} + +audio_return_t _audio_mixer_control_set_value(audio_hal_t *ah, const char *card, const char *ctl_name, int val) +{ + snd_ctl_t *handle; + snd_ctl_elem_value_t *control; + snd_ctl_elem_id_t *id; + snd_ctl_elem_info_t *info; + snd_ctl_elem_type_t type; + + char *card_name = NULL; + int ret = 0, count = 0, i = 0; + + AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(card, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(ctl_name, AUDIO_ERR_PARAMETER); + + pthread_mutex_lock(&(ah->mixer.mutex)); + + ret = snd_ctl_open(&handle, card, 0); + if (ret < 0) { + AUDIO_LOG_ERROR("snd_ctl_open error, card: %s: %s", card_name, snd_strerror(ret)); + pthread_mutex_unlock(&(ah->mixer.mutex)); + return AUDIO_ERR_IOCTL; + } + + // Get Element Info + + snd_ctl_elem_id_alloca(&id); + snd_ctl_elem_info_alloca(&info); + snd_ctl_elem_value_alloca(&control); + + snd_ctl_elem_id_set_interface(id, SND_CTL_ELEM_IFACE_MIXER); + snd_ctl_elem_id_set_name(id, ctl_name); + + snd_ctl_elem_info_set_id(info, id); + if (snd_ctl_elem_info(handle, info) < 0) { + AUDIO_LOG_ERROR("Cannot find control element: %s", ctl_name); + goto close; + } + snd_ctl_elem_info_get_id(info, id); + + type = snd_ctl_elem_info_get_type(info); + count = snd_ctl_elem_info_get_count(info); + + snd_ctl_elem_value_set_id(control, id); + + snd_ctl_elem_read(handle, control); + + switch (type) { + case SND_CTL_ELEM_TYPE_BOOLEAN: + for (i = 0; i < count; i++) + snd_ctl_elem_value_set_boolean(control, i, val); + break; + case SND_CTL_ELEM_TYPE_INTEGER: + for (i = 0; i < count; i++) + snd_ctl_elem_value_set_integer(control, i, val); + break; + case SND_CTL_ELEM_TYPE_ENUMERATED: + for (i = 0; i < count; i++) + snd_ctl_elem_value_set_enumerated(control, i, val); + break; + + default: + AUDIO_LOG_WARN("unsupported control element type"); + goto close; + } + + snd_ctl_elem_write(handle, control); + + snd_ctl_close(handle); + + AUDIO_LOG_INFO("set mixer(%s) = %d success", ctl_name, val); + + pthread_mutex_unlock(&(ah->mixer.mutex)); + return AUDIO_RET_OK; + +close: + AUDIO_LOG_ERROR("Error"); + snd_ctl_close(handle); + pthread_mutex_unlock(&(ah->mixer.mutex)); + return AUDIO_ERR_UNDEFINED; +} + +audio_return_t _audio_mixer_control_set_value_string(audio_hal_t *ah, const char* ctl_name, const char* value) +{ + AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(ctl_name, AUDIO_ERR_PARAMETER); + + /* TODO. */ + return AUDIO_RET_OK; +} + + +audio_return_t _audio_mixer_control_get_element(audio_hal_t *ah, const char *ctl_name, snd_hctl_elem_t **elem) +{ + AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(ctl_name, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(elem, AUDIO_ERR_PARAMETER); + + /* TODO. */ + return AUDIO_RET_OK; +} + +#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 */ +/* 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 + +uint32_t _convert_format(audio_sample_format_t format) +{ + return g_format_convert_table[format]; +} + +/* Generic snd pcm interface APIs */ +audio_return_t _audio_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_t ret = AUDIO_RET_OK; + snd_pcm_hw_params_t *hwparams; + int err = 0; + int dir; + unsigned int val = 0; + snd_pcm_uframes_t _period_size = period_size ? *period_size : 0; + snd_pcm_uframes_t _buffer_size = buffer_size ? *buffer_size : 0; + uint8_t _use_mmap = use_mmap && *use_mmap; + uint32_t channels = 0; + + AUDIO_RETURN_VAL_IF_FAIL(pcm, AUDIO_ERR_PARAMETER); + + snd_pcm_hw_params_alloca(&hwparams); + + /* Skip parameter setting to null device. */ + if (snd_pcm_type(pcm) == SND_PCM_TYPE_NULL) + return AUDIO_ERR_IOCTL; + + /* Allocate a hardware parameters object. */ + snd_pcm_hw_params_alloca(&hwparams); + + /* Fill it in with default values. */ + if (snd_pcm_hw_params_any(pcm, hwparams) < 0) { + AUDIO_LOG_ERROR("snd_pcm_hw_params_any() : failed! - %s\n", snd_strerror(err)); + goto error; + } + + /* Set the desired hardware parameters. */ + + if (_use_mmap) { + + if (snd_pcm_hw_params_set_access(pcm, hwparams, SND_PCM_ACCESS_MMAP_INTERLEAVED) < 0) { + + /* mmap() didn't work, fall back to interleaved */ + + if ((ret = snd_pcm_hw_params_set_access(pcm, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) { + AUDIO_LOG_DEBUG("snd_pcm_hw_params_set_access() failed: %s", snd_strerror(ret)); + goto error; + } + + _use_mmap = 0; + } + + } else if ((ret = snd_pcm_hw_params_set_access(pcm, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) { + AUDIO_LOG_DEBUG("snd_pcm_hw_params_set_access() failed: %s", snd_strerror(ret)); + goto error; + } + AUDIO_LOG_DEBUG("setting rate - %d", sample_spec->rate); + err = snd_pcm_hw_params_set_rate(pcm, hwparams, sample_spec->rate, 0); + if (err < 0) { + AUDIO_LOG_ERROR("snd_pcm_hw_params_set_rate() : failed! - %s\n", snd_strerror(err)); + } + + err = snd_pcm_hw_params(pcm, hwparams); + if (err < 0) { + AUDIO_LOG_ERROR("snd_pcm_hw_params() : failed! - %s\n", snd_strerror(err)); + goto error; + } + + /* Dump current param */ + + if ((ret = snd_pcm_hw_params_current(pcm, hwparams)) < 0) { + AUDIO_LOG_INFO("snd_pcm_hw_params_current() failed: %s", snd_strerror(ret)); + goto error; + } + + if ((ret = snd_pcm_hw_params_get_period_size(hwparams, &_period_size, &dir)) < 0 || + (ret = snd_pcm_hw_params_get_buffer_size(hwparams, &_buffer_size)) < 0) { + AUDIO_LOG_INFO("snd_pcm_hw_params_get_{period|buffer}_size() failed: %s", snd_strerror(ret)); + goto error; + } + + snd_pcm_hw_params_get_access(hwparams, (snd_pcm_access_t *) &val); + AUDIO_LOG_DEBUG("access type = %s\n", snd_pcm_access_name((snd_pcm_access_t)val)); + + snd_pcm_hw_params_get_format(hwparams, &sample_spec->format); + AUDIO_LOG_DEBUG("format = '%s' (%s)\n", + snd_pcm_format_name((snd_pcm_format_t)sample_spec->format), + snd_pcm_format_description((snd_pcm_format_t)sample_spec->format)); + + snd_pcm_hw_params_get_subformat(hwparams, (snd_pcm_subformat_t *)&val); + AUDIO_LOG_DEBUG("subformat = '%s' (%s)\n", + snd_pcm_subformat_name((snd_pcm_subformat_t)val), + snd_pcm_subformat_description((snd_pcm_subformat_t)val)); + + snd_pcm_hw_params_get_channels(hwparams, &channels); + sample_spec->channels = (uint8_t)channels; + AUDIO_LOG_DEBUG("channels = %d\n", sample_spec->channels); + + if (buffer_size) + *buffer_size = _buffer_size; + + if (period_size) + *period_size = _period_size; + + if (use_mmap) + *use_mmap = _use_mmap; + + return AUDIO_RET_OK; + +error: + return AUDIO_ERR_RESOURCE; +} + +audio_return_t _audio_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; + int err; + + AUDIO_RETURN_VAL_IF_FAIL(pcm, AUDIO_ERR_PARAMETER); + + snd_pcm_sw_params_alloca(&swparams); + + if ((err = snd_pcm_sw_params_current(pcm, swparams)) < 0) { + AUDIO_LOG_WARN("Unable to determine current swparams: %s\n", snd_strerror(err)); + goto error; + } + if ((err = snd_pcm_sw_params_set_period_event(pcm, swparams, period_event)) < 0) { + AUDIO_LOG_WARN("Unable to disable period event: %s\n", snd_strerror(err)); + goto error; + } + if ((err = snd_pcm_sw_params_set_tstamp_mode(pcm, swparams, SND_PCM_TSTAMP_ENABLE)) < 0) { + AUDIO_LOG_WARN("Unable to enable time stamping: %s\n", snd_strerror(err)); + goto error; + } + if ((err = snd_pcm_sw_params_get_boundary(swparams, &boundary)) < 0) { + AUDIO_LOG_WARN("Unable to get boundary: %s\n", snd_strerror(err)); + goto error; + } + if ((err = snd_pcm_sw_params_set_stop_threshold(pcm, swparams, boundary)) < 0) { + AUDIO_LOG_WARN("Unable to set stop threshold: %s\n", snd_strerror(err)); + goto error; + } + if ((err = snd_pcm_sw_params_set_start_threshold(pcm, swparams, (snd_pcm_uframes_t) avail_min)) < 0) { + AUDIO_LOG_WARN("Unable to set start threshold: %s\n", snd_strerror(err)); + goto error; + } + if ((err = snd_pcm_sw_params_set_avail_min(pcm, swparams, avail_min)) < 0) { + AUDIO_LOG_WARN("snd_pcm_sw_params_set_avail_min() failed: %s", snd_strerror(err)); + goto error; + } + if ((err = snd_pcm_sw_params(pcm, swparams)) < 0) { + AUDIO_LOG_WARN("Unable to set sw params: %s\n", snd_strerror(err)); + goto error; + } + return AUDIO_RET_OK; +error: + return err; +} + +/* ------ dump helper -------- */ +#define MAX(a, b) ((a) > (b) ? (a) : (b)) + +dump_data_t* dump_new(int length) +{ + dump_data_t* dump = NULL; + + if ((dump = malloc(sizeof(dump_data_t)))) { + memset(dump, 0, sizeof(dump_data_t)); + if ((dump->strbuf = malloc(length))) { + dump->p = &dump->strbuf[0]; + dump->left = length; + } else { + free(dump); + dump = NULL; + } + } + + return dump; +} + +void dump_add_str(dump_data_t *dump, const char *fmt, ...) +{ + int len; + va_list ap; + + if (!dump) + return; + + va_start(ap, fmt); + len = vsnprintf(dump->p, dump->left, fmt, ap); + va_end(ap); + + dump->p += MAX(0, len); + dump->left -= MAX(0, len); +} + +char* dump_get_str(dump_data_t *dump) +{ + return (dump) ? dump->strbuf : NULL; +} + +void dump_free(dump_data_t *dump) +{ + if (dump) { + if (dump->strbuf) + free(dump->strbuf); + free(dump); + } +} +/* ------ dump helper -------- */ diff --git a/tizen-audio-volume.c b/tizen-audio-volume.c new file mode 100644 index 0000000..f3e3d66 --- /dev/null +++ b/tizen-audio-volume.c @@ -0,0 +1,399 @@ +/* + * audio-hal + * + * Copyright (c) 2000 - 2013 Samsung Electronics Co., Ltd. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include "tizen-audio-internal.h" + +#define VOLUME_INI_DEFAULT_PATH "/usr/etc/mmfw_audio_volume.ini" +#define VOLUME_INI_TEMP_PATH "/opt/system/mmfw_audio_volume.ini" +#define VOLUME_VALUE_MAX (1.0f) +#define GAIN_VALUE_MAX (1.0f) + +static const char *g_volume_vconf[AUDIO_VOLUME_TYPE_MAX] = { + "file/private/sound/volume/system", /* AUDIO_VOLUME_TYPE_SYSTEM */ + "file/private/sound/volume/notification", /* AUDIO_VOLUME_TYPE_NOTIFICATION */ + "file/private/sound/volume/alarm", /* AUDIO_VOLUME_TYPE_ALARM */ + "file/private/sound/volume/ringtone", /* AUDIO_VOLUME_TYPE_RINGTONE */ + "file/private/sound/volume/media", /* AUDIO_VOLUME_TYPE_MEDIA */ + "file/private/sound/volume/call", /* AUDIO_VOLUME_TYPE_CALL */ + "file/private/sound/volume/voip", /* AUDIO_VOLUME_TYPE_VOIP */ + "file/private/sound/volume/voice", /* AUDIO_VOLUME_TYPE_VOICE */ +}; + +static const char *__get_volume_type_string_by_idx(uint32_t vol_type_idx) +{ + switch (vol_type_idx) { + case AUDIO_VOLUME_TYPE_SYSTEM: return "system"; + case AUDIO_VOLUME_TYPE_NOTIFICATION: return "notification"; + case AUDIO_VOLUME_TYPE_ALARM: return "alarm"; + case AUDIO_VOLUME_TYPE_RINGTONE: return "ringtone"; + case AUDIO_VOLUME_TYPE_MEDIA: return "media"; + case AUDIO_VOLUME_TYPE_CALL: return "call"; + case AUDIO_VOLUME_TYPE_VOIP: return "voip"; + case AUDIO_VOLUME_TYPE_VOICE: return "voice"; + default: return "invalid"; + } +} + +static uint32_t __get_volume_idx_by_string_type(const char *vol_type) +{ + if (!strncmp(vol_type, "system", strlen(vol_type)) || !strncmp(vol_type, "0", strlen(vol_type))) + return AUDIO_VOLUME_TYPE_SYSTEM; + else if (!strncmp(vol_type, "notification", strlen(vol_type)) || !strncmp(vol_type, "1", strlen(vol_type))) + return AUDIO_VOLUME_TYPE_NOTIFICATION; + else if (!strncmp(vol_type, "alarm", strlen(vol_type)) || !strncmp(vol_type, "2", strlen(vol_type))) + return AUDIO_VOLUME_TYPE_ALARM; + else if (!strncmp(vol_type, "ringtone", strlen(vol_type)) || !strncmp(vol_type, "3", strlen(vol_type))) + return AUDIO_VOLUME_TYPE_RINGTONE; + else if (!strncmp(vol_type, "media", strlen(vol_type)) || !strncmp(vol_type, "4", strlen(vol_type))) + return AUDIO_VOLUME_TYPE_MEDIA; + else if (!strncmp(vol_type, "call", strlen(vol_type)) || !strncmp(vol_type, "5", strlen(vol_type))) + return AUDIO_VOLUME_TYPE_CALL; + else if (!strncmp(vol_type, "voip", strlen(vol_type)) || !strncmp(vol_type, "6", strlen(vol_type))) + return AUDIO_VOLUME_TYPE_VOIP; + else if (!strncmp(vol_type, "voice", strlen(vol_type)) || !strncmp(vol_type, "7", strlen(vol_type))) + return AUDIO_VOLUME_TYPE_VOICE; + else + return AUDIO_VOLUME_TYPE_MEDIA; +} + +static const char *__get_gain_type_string_by_idx(uint32_t gain_type_idx) +{ + switch (gain_type_idx) { + case AUDIO_GAIN_TYPE_DEFAULT: return "default"; + case AUDIO_GAIN_TYPE_DIALER: return "dialer"; + case AUDIO_GAIN_TYPE_TOUCH: return "touch"; + case AUDIO_GAIN_TYPE_AF: return "af"; + case AUDIO_GAIN_TYPE_SHUTTER1: return "shutter1"; + case AUDIO_GAIN_TYPE_SHUTTER2: return "shutter2"; + case AUDIO_GAIN_TYPE_CAMCODING: return "camcording"; + case AUDIO_GAIN_TYPE_MIDI: return "midi"; + case AUDIO_GAIN_TYPE_BOOTING: return "booting"; + case AUDIO_GAIN_TYPE_VIDEO: return "video"; + case AUDIO_GAIN_TYPE_TTS: return "tts"; + default: return "invalid"; + } +} + +static void __dump_tb(audio_hal_t *ah) +{ + audio_volume_value_table_t *volume_value_table = ah->volume.volume_value_table; + uint32_t vol_type_idx, vol_level_idx, gain_type_idx; + const char *gain_type_str[] = { + "def", /* AUDIO_GAIN_TYPE_DEFAULT */ + "dial", /* AUDIO_GAIN_TYPE_DIALER */ + "touch", /* AUDIO_GAIN_TYPE_TOUCH */ + "af", /* AUDIO_GAIN_TYPE_AF */ + "shut1", /* AUDIO_GAIN_TYPE_SHUTTER1 */ + "shut2", /* AUDIO_GAIN_TYPE_SHUTTER2 */ + "cam", /* AUDIO_GAIN_TYPE_CAMCODING */ + "midi", /* AUDIO_GAIN_TYPE_MIDI */ + "boot", /* AUDIO_GAIN_TYPE_BOOTING */ + "video", /* AUDIO_GAIN_TYPE_VIDEO */ + "tts", /* AUDIO_GAIN_TYPE_TTS */ + }; + char dump_str[AUDIO_DUMP_STR_LEN], *dump_str_ptr; + + /* Dump volume table */ + AUDIO_LOG_INFO("<<<<< volume table >>>>>"); + + const char *table_str = "volumes"; + + AUDIO_LOG_INFO("<< %s >>", table_str); + + for (vol_type_idx = 0; vol_type_idx < AUDIO_VOLUME_TYPE_MAX; vol_type_idx++) { + const char *vol_type_str = __get_volume_type_string_by_idx(vol_type_idx); + + dump_str_ptr = &dump_str[0]; + memset(dump_str, 0x00, sizeof(char) * sizeof(dump_str)); + snprintf(dump_str_ptr, 8, "%6s:", vol_type_str); + dump_str_ptr += strlen(dump_str_ptr); + + for (vol_level_idx = 0; vol_level_idx < volume_value_table->volume_level_max[vol_type_idx]; vol_level_idx++) { + snprintf(dump_str_ptr, 6, "%01.2f ", volume_value_table->volume[vol_type_idx][vol_level_idx]); + dump_str_ptr += strlen(dump_str_ptr); + } + AUDIO_LOG_INFO("%s", dump_str); + } + + volume_value_table = ah->volume.volume_value_table; + + /* Dump gain table */ + AUDIO_LOG_INFO("<<<<< gain table >>>>>"); + + dump_str_ptr = &dump_str[0]; + memset(dump_str, 0x00, sizeof(char) * sizeof(dump_str)); + + snprintf(dump_str_ptr, 11, "%10s", " "); + dump_str_ptr += strlen(dump_str_ptr); + + for (gain_type_idx = 0; gain_type_idx < AUDIO_GAIN_TYPE_MAX; gain_type_idx++) { + snprintf(dump_str_ptr, 7, "%5s ", gain_type_str[gain_type_idx]); + dump_str_ptr += strlen(dump_str_ptr); + } + AUDIO_LOG_INFO("%s", dump_str); + + dump_str_ptr = &dump_str[0]; + memset(dump_str, 0x00, sizeof(char) * sizeof(dump_str)); + + snprintf(dump_str_ptr, 11, "%9s:", table_str); + dump_str_ptr += strlen(dump_str_ptr); + + for (gain_type_idx = 0; gain_type_idx < AUDIO_GAIN_TYPE_MAX; gain_type_idx++) { + snprintf(dump_str_ptr, 7, "%01.3f ", volume_value_table->gain[gain_type_idx]); + dump_str_ptr += strlen(dump_str_ptr); + } + AUDIO_LOG_INFO("%s", dump_str); + +} + +static audio_return_t __load_volume_value_table_from_ini(audio_hal_t *ah) +{ + dictionary * dict = NULL; + uint32_t vol_type_idx, vol_level_idx, gain_type_idx; + audio_volume_value_table_t *volume_value_table = ah->volume.volume_value_table; + int size = 0; + + dict = iniparser_load(VOLUME_INI_TEMP_PATH); + if (!dict) { + AUDIO_LOG_DEBUG("Use default volume&gain ini file"); + dict = iniparser_load(VOLUME_INI_DEFAULT_PATH); + if (!dict) { + AUDIO_LOG_WARN("Loading volume&gain table from ini file failed"); + return AUDIO_ERR_UNDEFINED; + } + } + + const char delimiter[] = ", "; + char *key, *list_str, *token, *ptr = NULL; + const char *table_str = "volumes"; + + /* Load volume table */ + for (vol_type_idx = 0; vol_type_idx < AUDIO_VOLUME_TYPE_MAX; vol_type_idx++) { + const char *vol_type_str = __get_volume_type_string_by_idx(vol_type_idx); + + volume_value_table->volume_level_max[vol_type_idx] = 0; + size = strlen(table_str) + strlen(vol_type_str) + 2; + key = malloc(size); + if (key) { + snprintf(key, size, "%s:%s", table_str, vol_type_str); + list_str = iniparser_getstring(dict, key, NULL); + if (list_str) { + token = strtok_r(list_str, delimiter, &ptr); + while (token) { + /* convert dB volume to linear volume */ + double vol_value = 0.0f; + if (strncmp(token, "0", strlen(token))) + vol_value = pow(10.0, (atof(token) - 100) / 20.0); + volume_value_table->volume[vol_type_idx][volume_value_table->volume_level_max[vol_type_idx]++] = vol_value; + token = strtok_r(NULL, delimiter, &ptr); + } + } else { + volume_value_table->volume_level_max[vol_type_idx] = 1; + for (vol_level_idx = 0; vol_level_idx < AUDIO_VOLUME_LEVEL_MAX; vol_level_idx++) { + volume_value_table->volume[vol_type_idx][vol_level_idx] = VOLUME_VALUE_MAX; + } + } + free(key); + } + } + + /* Load gain table */ + volume_value_table->gain[AUDIO_GAIN_TYPE_DEFAULT] = GAIN_VALUE_MAX; + for (gain_type_idx = AUDIO_GAIN_TYPE_DEFAULT + 1; gain_type_idx < AUDIO_GAIN_TYPE_MAX; gain_type_idx++) { + const char *gain_type_str = __get_gain_type_string_by_idx(gain_type_idx); + + size = strlen(table_str) + strlen("gain") + strlen(gain_type_str) + 3; + key = malloc(size); + if (key) { + snprintf(key, size, "%s:gain_%s", table_str, gain_type_str); + token = iniparser_getstring(dict, key, NULL); + if (token) { + volume_value_table->gain[gain_type_idx] = atof(token); + } else { + volume_value_table->gain[gain_type_idx] = GAIN_VALUE_MAX; + } + free(key); + } else { + volume_value_table->gain[gain_type_idx] = GAIN_VALUE_MAX; + } + } + + iniparser_freedict(dict); + + __dump_tb(ah); + + return AUDIO_RET_OK; +} + +audio_return_t _audio_volume_init(audio_hal_t *ah) +{ + int i; + int val = 0; + audio_return_t audio_ret = AUDIO_RET_OK; + int init_value[AUDIO_VOLUME_TYPE_MAX] = {9, 11, 7, 11, 7, 4, 4, 7}; + + AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER); + + for (i = 0; i < AUDIO_VOLUME_TYPE_MAX; i++) { + ah->volume.volume_level[i] = init_value[i]; + } + + for (i = 0; i < AUDIO_VOLUME_TYPE_MAX; i++) { + /* Get volume value string from VCONF */ + if (vconf_get_int(g_volume_vconf[i], &val) < 0) { + AUDIO_LOG_ERROR("vconf_get_int(%s) failed", g_volume_vconf[i]); + continue; + } + + AUDIO_LOG_INFO("read vconf. %s = %d", g_volume_vconf[i], val); + ah->volume.volume_level[i] = val; + } + + if (!(ah->volume.volume_value_table = malloc(AUDIO_VOLUME_DEVICE_MAX * sizeof(audio_volume_value_table_t)))) { + AUDIO_LOG_ERROR("volume_value_table malloc failed"); + return AUDIO_ERR_RESOURCE; + } + + audio_ret = __load_volume_value_table_from_ini(ah); + if (audio_ret != AUDIO_RET_OK) { + AUDIO_LOG_ERROR("gain table load error"); + return AUDIO_ERR_UNDEFINED; + } + + return audio_ret; +} + +audio_return_t _audio_volume_deinit(audio_hal_t *ah) +{ + AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER); + + if (ah->volume.volume_value_table) { + free(ah->volume.volume_value_table); + ah->volume.volume_value_table = NULL; + } + + return AUDIO_RET_OK; +} + +audio_return_t audio_get_volume_level_max(void *audio_handle, audio_volume_info_t *info, uint32_t *level) +{ + audio_hal_t *ah = (audio_hal_t *)audio_handle; + audio_volume_value_table_t *volume_value_table; + + AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(info, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(level, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(ah->volume.volume_value_table, AUDIO_ERR_PARAMETER); + + /* Get max volume level by device & type */ + volume_value_table = ah->volume.volume_value_table; + *level = volume_value_table->volume_level_max[__get_volume_idx_by_string_type(info->type)]; + + AUDIO_LOG_DEBUG("get_[%s] volume_level_max: %d", info->type, *level); + + return AUDIO_RET_OK; +} + +audio_return_t audio_get_volume_level(void *audio_handle, audio_volume_info_t *info, uint32_t *level) +{ + audio_hal_t *ah = (audio_hal_t *)audio_handle; + + AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(info, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(level, AUDIO_ERR_PARAMETER); + + *level = ah->volume.volume_level[__get_volume_idx_by_string_type(info->type)]; + + AUDIO_LOG_INFO("get [%s] volume_level: %d, direction(%d)", info->type, *level, info->direction); + + return AUDIO_RET_OK; +} + +audio_return_t audio_get_volume_value(void *audio_handle, audio_volume_info_t *info, uint32_t level, double *value) +{ + audio_hal_t *ah = (audio_hal_t *)audio_handle; + audio_volume_value_table_t *volume_value_table; + char dump_str[AUDIO_DUMP_STR_LEN] = {0,}; + + AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(info, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(value, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(ah->volume.volume_value_table, AUDIO_ERR_PARAMETER); + + /* Get basic volume by device & type & level */ + volume_value_table = ah->volume.volume_value_table; + if (volume_value_table->volume_level_max[__get_volume_idx_by_string_type(info->type)] < level) + *value = VOLUME_VALUE_MAX; + else + *value = volume_value_table->volume[__get_volume_idx_by_string_type(info->type)][level]; + *value *= volume_value_table->gain[AUDIO_GAIN_TYPE_DEFAULT]; /* need to fix getting gain via audio_info_t */ + + AUDIO_LOG_DEBUG("get_volume_value:%d(%s)=>%f %s", level, info->type, *value, &dump_str[0]); + + return AUDIO_RET_OK; +} + +audio_return_t audio_set_volume_level(void *audio_handle, audio_volume_info_t *info, uint32_t level) +{ + audio_return_t audio_ret = AUDIO_RET_OK; + audio_hal_t *ah = (audio_hal_t *)audio_handle; + + AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(info, AUDIO_ERR_PARAMETER); + + /* Update volume level */ + ah->volume.volume_level[__get_volume_idx_by_string_type(info->type)] = level; + AUDIO_LOG_INFO("set [%s] volume_level: %d, direction(%d)", info->type, level, info->direction); + + return audio_ret; +} + +audio_return_t audio_get_volume_mute(void *audio_handle, audio_volume_info_t *info, uint32_t *mute) +{ + audio_return_t audio_ret = AUDIO_RET_OK; + audio_hal_t *ah = (audio_hal_t *)audio_handle; + + AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(info, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(mute, AUDIO_ERR_PARAMETER); + + return audio_ret; +} + +audio_return_t audio_set_volume_mute(void *audio_handle, audio_volume_info_t *info, uint32_t mute) +{ + audio_return_t audio_ret = AUDIO_RET_OK; + audio_hal_t *ah = (audio_hal_t *)audio_handle; + + AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(info, AUDIO_ERR_PARAMETER); + + return audio_ret; +} diff --git a/tizen-audio.c b/tizen-audio.c new file mode 100644 index 0000000..8cb58aa --- /dev/null +++ b/tizen-audio.c @@ -0,0 +1,263 @@ +/* + * audio-hal + * + * Copyright (c) 2000 - 2013 Samsung Electronics Co., Ltd. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "tizen-audio-internal.h" + +/* audio latency */ +static const char* AUDIO_LATENCY_LOW = "low"; +static const char* AUDIO_LATENCY_MID = "mid"; +static const char* AUDIO_LATENCY_HIGH = "high"; +static const char* AUDIO_LATENCY_VOIP = "voip"; + +audio_return_t audio_set_message_cb(void *audio_handle, message_cb callback, void *user_data) +{ + audio_return_t ret = AUDIO_RET_OK; + + AUDIO_RETURN_VAL_IF_FAIL(audio_handle, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(callback, AUDIO_ERR_PARAMETER); + + ret = _audio_comm_set_message_callback((audio_hal_t *)audio_handle, callback, user_data); + + return ret; +} + +audio_return_t audio_init(void **audio_handle) +{ + audio_hal_t *ah; + audio_return_t ret = AUDIO_RET_OK; + + AUDIO_RETURN_VAL_IF_FAIL(audio_handle, AUDIO_ERR_PARAMETER); + + if (!(ah = malloc(sizeof(audio_hal_t)))) { + AUDIO_LOG_ERROR("am malloc failed"); + return AUDIO_ERR_RESOURCE; + } + if (AUDIO_IS_ERROR((ret = _audio_device_init(ah)))) { + AUDIO_LOG_ERROR("device init failed"); + goto error_exit; + } + if (AUDIO_IS_ERROR((ret = _audio_volume_init(ah)))) { + AUDIO_LOG_ERROR("stream init failed"); + goto error_exit; + } + if (AUDIO_IS_ERROR((ret = _audio_util_init(ah)))) { + AUDIO_LOG_ERROR("util init failed"); + goto error_exit; + } + if (AUDIO_IS_ERROR((ret = _audio_comm_init(ah)))) { + AUDIO_LOG_ERROR("comm init failed"); + goto error_exit; + } + + *audio_handle = (void *)ah; + return AUDIO_RET_OK; + +error_exit: + if (ah) + free(ah); + + return ret; +} + +audio_return_t audio_deinit(void *audio_handle) +{ + audio_hal_t *ah = (audio_hal_t *)audio_handle; + + AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER); + + _audio_device_deinit(ah); + _audio_volume_deinit(ah); + _audio_util_deinit(ah); + _audio_comm_deinit(ah); + free(ah); + ah = NULL; + + return AUDIO_RET_OK; +} + +/* Latency msec */ +static const unsigned int PERIOD_TIME_FOR_ULOW_LATENCY_MSEC = 20; +static const unsigned int PERIOD_TIME_FOR_LOW_LATENCY_MSEC = 25; +static const unsigned int PERIOD_TIME_FOR_MID_LATENCY_MSEC = 50; +static const unsigned int PERIOD_TIME_FOR_HIGH_LATENCY_MSEC = 75; +static const unsigned int PERIOD_TIME_FOR_UHIGH_LATENCY_MSEC = 150; +static const unsigned int PERIOD_TIME_FOR_VOIP_LATENCY_MSEC = 20; + +static const uint32_t g_size_table[] = { + [AUDIO_SAMPLE_U8] = 1, + [AUDIO_SAMPLE_ULAW] = 1, + [AUDIO_SAMPLE_ALAW] = 1, + [AUDIO_SAMPLE_S16LE] = 2, + [AUDIO_SAMPLE_S16BE] = 2, + [AUDIO_SAMPLE_FLOAT32LE] = 4, + [AUDIO_SAMPLE_FLOAT32BE] = 4, + [AUDIO_SAMPLE_S32LE] = 4, + [AUDIO_SAMPLE_S32BE] = 4, + [AUDIO_SAMPLE_S24LE] = 3, + [AUDIO_SAMPLE_S24BE] = 3, + [AUDIO_SAMPLE_S24_32LE] = 4, + [AUDIO_SAMPLE_S24_32BE] = 4 +}; + +int _sample_spec_valid(uint32_t rate, audio_sample_format_t format, uint32_t channels) +{ + if ((rate <= 0 || + rate > (48000U*4U) || + channels <= 0 || + channels > 32U || + format >= AUDIO_SAMPLE_MAX || + format < AUDIO_SAMPLE_U8)) + return 0; + + AUDIO_LOG_ERROR("hal-latency - _sample_spec_valid() -> return true"); + + return 1; +} + +uint32_t _audio_usec_to_bytes(uint64_t t, uint32_t rate, audio_sample_format_t format, uint32_t channels) +{ + uint32_t ret = (uint32_t) (((t * rate) / 1000000ULL)) * (g_size_table[format] * channels); + AUDIO_LOG_DEBUG("hal-latency - return %d", ret); + return ret; +} + +uint32_t _audio_sample_size(audio_sample_format_t format) +{ + return g_size_table[format]; +} +audio_return_t audio_get_buffer_attr(void *audio_handle, + uint32_t direction, + const char *latency, + uint32_t samplerate, + audio_sample_format_t format, + uint32_t channels, + uint32_t *maxlength, + uint32_t *tlength, + uint32_t *prebuf, + uint32_t *minreq, + uint32_t *fragsize) +{ + AUDIO_RETURN_VAL_IF_FAIL(audio_handle, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(latency, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(maxlength, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(tlength, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(prebuf, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(minreq, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(fragsize, AUDIO_ERR_PARAMETER); + + AUDIO_LOG_DEBUG("hal-latency - audio_get_buffer_attr(direction:%d, latency:%s, samplerate:%d, format:%d, channels:%d)", direction, latency, samplerate, format, channels); + + uint32_t period_time = 0, + sample_per_period = 0; + + if (_sample_spec_valid(samplerate, format, channels) == 0) { + return AUDIO_ERR_PARAMETER; + } + + if (direction == AUDIO_DIRECTION_IN) { + if (!strcmp(latency, AUDIO_LATENCY_LOW)) { + AUDIO_LOG_DEBUG("AUDIO_DIRECTION_IN, AUDIO_LATENCY_LOW"); + period_time = PERIOD_TIME_FOR_LOW_LATENCY_MSEC; + sample_per_period = (samplerate * period_time) / 1000; + *prebuf = 0; + *minreq = -1; + *tlength = -1; + *maxlength = -1; + *fragsize = sample_per_period * _audio_sample_size(format); + } else if (!strcmp(latency, AUDIO_LATENCY_MID)) { + AUDIO_LOG_DEBUG("AUDIO_DIRECTION_IN, AUDIO_LATENCY_MID"); + period_time = PERIOD_TIME_FOR_MID_LATENCY_MSEC; + sample_per_period = (samplerate * period_time) / 1000; + *prebuf = 0; + *minreq = -1; + *tlength = -1; + *maxlength = -1; + *fragsize = sample_per_period * _audio_sample_size(format); + } else if (!strcmp(latency, AUDIO_LATENCY_HIGH)) { + AUDIO_LOG_DEBUG("AUDIO_DIRECTION_IN, AUDIO_LATENCY_HIGH"); + period_time = PERIOD_TIME_FOR_HIGH_LATENCY_MSEC; + sample_per_period = (samplerate * period_time) / 1000; + *prebuf = 0; + *minreq = -1; + *tlength = -1; + *maxlength = -1; + *fragsize = sample_per_period * _audio_sample_size(format); + } else if (!strcmp(latency, AUDIO_LATENCY_VOIP)) { + AUDIO_LOG_DEBUG("AUDIO_DIRECTION_IN, AUDIO_LATENCY_VOIP"); + period_time = PERIOD_TIME_FOR_VOIP_LATENCY_MSEC; + sample_per_period = (samplerate * period_time) / 1000; + *prebuf = 0; + *minreq = -1; + *tlength = -1; + *maxlength = -1; + *fragsize = sample_per_period * _audio_sample_size(format); + } else { + AUDIO_LOG_ERROR("hal-latency - The latency(%s) is undefined", latency); + return AUDIO_ERR_UNDEFINED; + } + } else { /* AUDIO_DIRECTION_OUT */ + if (!strcmp(latency, AUDIO_LATENCY_LOW)) { + AUDIO_LOG_DEBUG("AUDIO_DIRECTION_OUT, AUDIO_LATENCY_LOW"); + period_time = PERIOD_TIME_FOR_LOW_LATENCY_MSEC; + sample_per_period = (samplerate * period_time) / 1000; + *prebuf = 0; + *minreq = -1; + *tlength = (samplerate / 10) * _audio_sample_size(format) * channels; /* 100ms */ + *maxlength = -1; + *fragsize = 0; + } else if (!strcmp(latency, AUDIO_LATENCY_MID)) { + AUDIO_LOG_DEBUG("AUDIO_DIRECTION_OUT, AUDIO_LATENCY_MID"); + period_time = PERIOD_TIME_FOR_MID_LATENCY_MSEC; + sample_per_period = (samplerate * period_time) / 1000; + *prebuf = 0; + *minreq = -1; + *tlength = (uint32_t) _audio_usec_to_bytes(200000, samplerate, format, channels); + *maxlength = -1; + *fragsize = -1; + } else if (!strcmp(latency, AUDIO_LATENCY_HIGH)) { + AUDIO_LOG_DEBUG("AUDIO_DIRECTION_OUT, AUDIO_LATENCY_HIGH"); + period_time = PERIOD_TIME_FOR_HIGH_LATENCY_MSEC; + sample_per_period = (samplerate * period_time) / 1000; + *prebuf = 0; + *minreq = -1; + *tlength = (uint32_t) _audio_usec_to_bytes(400000, samplerate, format, channels); + *maxlength = -1; + *fragsize = -1; + } else if (!strcmp(latency, AUDIO_LATENCY_VOIP)) { + AUDIO_LOG_DEBUG("AUDIO_DIRECTION_OUT, AUDIO_LATENCY_VOIP"); + period_time = PERIOD_TIME_FOR_VOIP_LATENCY_MSEC; + sample_per_period = (samplerate * period_time) / 1000; + *prebuf = 0; + *minreq = _audio_usec_to_bytes(20000, samplerate, format, channels); + *tlength = _audio_usec_to_bytes(100000, samplerate, format, channels); + *maxlength = -1; + *fragsize = 0; + } else { + AUDIO_LOG_ERROR("hal-latency - The latency(%s) is undefined", latency); + return AUDIO_ERR_UNDEFINED; + } + } + + AUDIO_LOG_INFO("hal-latency - return attr --> prebuf:%d, minreq:%d, tlength:%d, maxlength:%d, fragsize:%d", *prebuf, *minreq, *tlength, *maxlength, *fragsize); + return AUDIO_RET_OK; +} diff --git a/tizen-audio.h b/tizen-audio.h new file mode 100644 index 0000000..794cdd7 --- /dev/null +++ b/tizen-audio.h @@ -0,0 +1,135 @@ +#ifndef footizenaudiofoo +#define footizenaudiofoo + +/* + * audio-hal + * + * Copyright (c) 2000 - 2013 Samsung Electronics Co., Ltd. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +/* Error code */ +#define AUDIO_IS_ERROR(ret) (ret < 0) +typedef enum audio_return { + AUDIO_RET_OK = 0, + AUDIO_ERR_UNDEFINED = (int32_t)0x80001000, + AUDIO_ERR_RESOURCE = (int32_t)0x80001001, + AUDIO_ERR_PARAMETER = (int32_t)0x80001002, + AUDIO_ERR_IOCTL = (int32_t)0x80001003, + AUDIO_ERR_INVALID_STATE = (int32_t)0x80001004, + AUDIO_ERR_INTERNAL = (int32_t)0x80001005, + + AUDIO_ERR_NOT_IMPLEMENTED = (int32_t)0x80001100, +} audio_return_t ; + +typedef enum audio_direction { + AUDIO_DIRECTION_IN, /**< Capture */ + AUDIO_DIRECTION_OUT, /**< Playback */ +} audio_direction_t; + +typedef struct device_info { + const char *type; + uint32_t direction; + uint32_t id; +} device_info_t; + +typedef struct audio_volume_info { + const char *type; + const char *gain; + uint32_t direction; +} audio_volume_info_t ; + +typedef struct audio_route_info { + const char *role; + device_info_t *device_infos; + uint32_t num_of_devices; +} audio_route_info_t; + +typedef struct audio_route_option { + const char *role; + const char *name; + int32_t value; +} audio_route_option_t; + +typedef struct audio_stream_info { + const char *role; + uint32_t direction; + uint32_t idx; +} audio_stream_info_t ; + +typedef void (*message_cb)(const char *name, int value, void *user_data); + +/* Overall */ +typedef struct audio_interface { + audio_return_t (*init)(void **audio_handle); + audio_return_t (*deinit)(void *audio_handle); + /* Volume */ + audio_return_t (*get_volume_level_max)(void *audio_handle, audio_volume_info_t *info, uint32_t *level); + audio_return_t (*get_volume_level)(void *audio_handle, audio_volume_info_t *info, uint32_t *level); + audio_return_t (*set_volume_level)(void *audio_handle, audio_volume_info_t *info, uint32_t level); + audio_return_t (*get_volume_value)(void *audio_handle, audio_volume_info_t *info, uint32_t level, double *value); + audio_return_t (*get_volume_mute)(void *audio_handle, audio_volume_info_t *info, uint32_t *mute); + audio_return_t (*set_volume_mute)(void *audio_handle, audio_volume_info_t *info, uint32_t mute); + /* Routing */ + audio_return_t (*do_route)(void *audio_handle, audio_route_info_t *info); + audio_return_t (*update_route_option)(void *audio_handle, audio_route_option_t *option); + audio_return_t (*update_stream_connection_info) (void *audio_handle, audio_stream_info_t *info, uint32_t is_connected); + /* Buffer Attribute */ + audio_return_t (*get_buffer_attr)(void *audio_handle, uint32_t direction, const char *latency, uint32_t samplerate, int format, uint32_t channels, + uint32_t *maxlength, uint32_t *tlength, uint32_t *prebuf, uint32_t* minreq, uint32_t *fragsize); + /* Interface of PCM device */ + audio_return_t (*pcm_open)(void *audio_handle, void **pcm_handle, uint32_t direction, void *sample_spec, uint32_t period_size, uint32_t periods); + audio_return_t (*pcm_start)(void *audio_handle, void *pcm_handle); + audio_return_t (*pcm_stop)(void *audio_handle, void *pcm_handle); + audio_return_t (*pcm_close)(void *audio_handle, void *pcm_handle); + audio_return_t (*pcm_avail)(void *audio_handle, void *pcm_handle, uint32_t *avail); + audio_return_t (*pcm_write)(void *audio_handle, void *pcm_handle, const void *buffer, uint32_t frames); + audio_return_t (*pcm_read)(void *audio_handle, void *pcm_handle, void *buffer, uint32_t frames); + audio_return_t (*pcm_get_fd)(void *audio_handle, void *pcm_handle, int *fd); + audio_return_t (*pcm_recover)(void *audio_handle, void *pcm_handle, int revents); + audio_return_t (*pcm_get_params)(void *audio_handle, void *pcm_handle, uint32_t direction, void **sample_spec, uint32_t *period_size, uint32_t *periods); + audio_return_t (*pcm_set_params)(void *audio_handle, void *pcm_handle, uint32_t direction, void *sample_spec, uint32_t period_size, uint32_t periods); + /* Message callback */ + audio_return_t (*set_message_cb)(void *audio_handle, message_cb callback, void *user_data); +} audio_interface_t; + +audio_return_t audio_init(void **audio_handle); +audio_return_t audio_deinit(void *audio_handle); +audio_return_t audio_get_volume_level_max(void *audio_handle, audio_volume_info_t *info, uint32_t *level); +audio_return_t audio_get_volume_level(void *audio_handle, audio_volume_info_t *info, uint32_t *level); +audio_return_t audio_set_volume_level(void *audio_handle, audio_volume_info_t *info, uint32_t level); +audio_return_t audio_get_volume_value(void *audio_handle, audio_volume_info_t *info, uint32_t level, double *value); +audio_return_t audio_get_volume_mute(void *audio_handle, audio_volume_info_t *info, uint32_t *mute); +audio_return_t audio_set_volume_mute(void *audio_handle, audio_volume_info_t *info, uint32_t mute); +audio_return_t audio_do_route(void *audio_handle, audio_route_info_t *info); +audio_return_t audio_update_route_option(void *audio_handle, audio_route_option_t *option); +audio_return_t audio_update_stream_connection_info(void *audio_handle, audio_stream_info_t *info, uint32_t is_connected); +audio_return_t audio_get_buffer_attr(void *audio_handle, uint32_t direction, const char *latency, uint32_t samplerate, int format, uint32_t channels, + uint32_t *maxlength, uint32_t *tlength, uint32_t *prebuf, uint32_t* minreq, uint32_t *fragsize); +audio_return_t audio_pcm_open(void *audio_handle, void **pcm_handle, uint32_t direction, void *sample_spec, uint32_t period_size, uint32_t periods); +audio_return_t audio_pcm_start(void *audio_handle, void *pcm_handle); +audio_return_t audio_pcm_stop(void *audio_handle, void *pcm_handle); +audio_return_t audio_pcm_close(void *audio_handle, void *pcm_handle); +audio_return_t audio_pcm_avail(void *audio_handle, void *pcm_handle, uint32_t *avail); +audio_return_t audio_pcm_write(void *audio_handle, void *pcm_handle, const void *buffer, uint32_t frames); +audio_return_t audio_pcm_read(void *audio_handle, void *pcm_handle, void *buffer, uint32_t frames); +audio_return_t audio_pcm_get_fd(void *audio_handle, void *pcm_handle, int *fd); +audio_return_t audio_pcm_recover(void *audio_handle, void *pcm_handle, int revents); +audio_return_t audio_pcm_get_params(void *audio_handle, void *pcm_handle, uint32_t direction, void **sample_spec, uint32_t *period_size, uint32_t *periods); +audio_return_t audio_pcm_set_params(void *audio_handle, void *pcm_handle, uint32_t direction, void *sample_spec, uint32_t period_size, uint32_t periods); +audio_return_t audio_set_message_cb(void *audio_handle, message_cb callback, void *user_data); +#endif -- 2.7.4