diff options
author | Seungbae Shin <seungbae.shin@samsung.com> | 2016-03-15 14:00:45 +0900 |
---|---|---|
committer | Seungbae Shin <seungbae.shin@samsung.com> | 2016-03-15 14:02:13 +0900 |
commit | 4cebae06c28837dda0d1f8b434e6ded3f5d03c99 (patch) | |
tree | 43b83f78c0799f712ec2ae134f05e3411b8597f6 | |
parent | 97717e5b030be0e92aa18da96bc34517a3e6bc9e (diff) | |
download | audio-hal-wm1831-4cebae06c28837dda0d1f8b434e6ded3f5d03c99.tar.gz audio-hal-wm1831-4cebae06c28837dda0d1f8b434e6ded3f5d03c99.tar.bz2 audio-hal-wm1831-4cebae06c28837dda0d1f8b434e6ded3f5d03c99.zip |
Initial code for WM1831submit/tizen/20160315.083907accepted/tizen/wearable/20160315.124020accepted/tizen/tv/20160315.123954accepted/tizen/mobile/20160315.123930accepted/tizen/ivi/20160315.124041accepted/tizen/common/20160315.221801
Initial copied from U3 (max98090) audio-hal
Change-Id: Id0bf555d9609b29445d0d6adc1a4c245843f7fab
-rw-r--r-- | LICENSE.Apache-2.0 | 202 | ||||
-rw-r--r-- | Makefile.am | 17 | ||||
-rw-r--r-- | NOTICE | 3 | ||||
-rw-r--r-- | audio-hal-wm1831.manifest | 5 | ||||
-rwxr-xr-x | autogen.sh | 10 | ||||
-rw-r--r-- | configure.ac | 60 | ||||
-rw-r--r-- | packaging/audio-hal-wm1831.spec | 50 | ||||
-rw-r--r-- | tizen-audio-device.c | 932 | ||||
-rw-r--r-- | tizen-audio-internal.h | 335 | ||||
-rw-r--r-- | tizen-audio-ucm.c | 719 | ||||
-rw-r--r-- | tizen-audio-util.c | 503 | ||||
-rw-r--r-- | tizen-audio-volume.c | 394 | ||||
-rw-r--r-- | tizen-audio.c | 251 | ||||
-rw-r--r-- | tizen-audio.h | 124 |
14 files changed, 3605 insertions, 0 deletions
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..1145f10 --- /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-ucm.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 + @@ -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-wm1831.manifest b/audio-hal-wm1831.manifest new file mode 100644 index 0000000..86dbb26 --- /dev/null +++ b/audio-hal-wm1831.manifest @@ -0,0 +1,5 @@ +<manifest> + <request> + <domain name="_" /> + </request> +</manifest> 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..acac33a --- /dev/null +++ b/configure.ac @@ -0,0 +1,60 @@ +AC_PREREQ([2.67]) + +AC_INIT([audio-hal-wm1831], [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-wm1831.spec b/packaging/audio-hal-wm1831.spec new file mode 100644 index 0000000..7e36e0b --- /dev/null +++ b/packaging/audio-hal-wm1831.spec @@ -0,0 +1,50 @@ +Name: audio-hal-wm1831 +Summary: TIZEN Audio HAL for WM1831 +Version: 0.1.0 +Release: 0 +Group: System/Libraries +License: Apache-2.0 +URL: http://tizen.org +Source0: audio-hal-wm1831-%{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 WM1831 + +%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-wm1831.manifest +%defattr(-,root,root,-) +%{_libdir}/libtizen-audio.so +%{_datadir}/license/%{name} diff --git a/tizen-audio-device.c b/tizen-audio-device.c new file mode 100644 index 0000000..b39790a --- /dev/null +++ b/tizen-audio-device.c @@ -0,0 +1,932 @@ +/* + * 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 <config.h> +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdbool.h> + +#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, const char *verb, 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); + + 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_in) { + /* check the active in devices */ + for (j = 0; j < inDeviceTypes[j].type; j++) { + if (((ah->device.active_in & (~0x80000000)) & inDeviceTypes[j].type)) + active_devices[dev_idx++] = inDeviceTypes[j].name; + } + } else if ((devices[0].direction == AUDIO_DIRECTION_IN) && 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 & 0x80000000) { + 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; + } + + audio_ret = _audio_ucm_set_devices(ah, verb, active_devices); + if (audio_ret) { + AUDIO_LOG_ERROR("Failed to set device: error = %d", audio_ret); + return audio_ret; + } + 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; + ah->device.mode = VERB_NORMAL; + 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; + const char *verb = 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; + + /* To Do: Set modifiers */ + /* int mod_idx = 0; */ + /* const char *modifiers[MAX_MODIFIERS] = {NULL,}; */ + + verb = AUDIO_USE_CASE_VERB_HIFI; + AUDIO_LOG_INFO("do_route_ap_playback_capture++ "); + + audio_ret = set_devices(ah, verb, devices, route_info->num_of_devices); + if (audio_ret) { + AUDIO_LOG_ERROR("Failed to set devices: error = 0x%x", audio_ret); + return audio_ret; + } + ah->device.mode = VERB_NORMAL; + + /* To Do: Set modifiers */ + /* + if (!strncmp("voice_recognition", route_info->role, MAX_NAME_LEN)) { + modifiers[mod_idx++] = AUDIO_USE_CASE_MODIFIER_VOICESEARCH; + } else if ((!strncmp("alarm", route_info->role, MAX_NAME_LEN))||(!strncmp("notifiication", route_info->role, MAX_NAME_LEN))) { + if (ah->device.active_out &= AUDIO_DEVICE_OUT_JACK) + modifiers[mod_idx++] = AUDIO_USE_CASE_MODIFIER_DUAL_MEDIA; + else + modifiers[mod_idx++] = AUDIO_USE_CASE_MODIFIER_MEDIA; + } else { + if (ah->device.active_in) + modifiers[mod_idx++] = AUDIO_USE_CASE_MODIFIER_CAMCORDING; + else + modifiers[mod_idx++] = AUDIO_USE_CASE_MODIFIER_MEDIA; + } + audio_ret = _audio_ucm_set_modifiers (ah, verb, modifiers); + */ + + return audio_ret; +} + +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; + const char *verb = NULL; + verb = AUDIO_USE_CASE_VERB_HIFI; + + 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, verb, devices, num_of_devices); + if (audio_ret) { + AUDIO_LOG_ERROR("Failed to set devices: error = 0x%x", audio_ret); + return audio_ret; + } + /* FIXME. If necessary, set VERB_VOIP */ + ah->device.mode = VERB_NORMAL; + + /* TO DO: Set modifiers */ + return audio_ret; +} + +audio_return_t _do_route_reset(audio_hal_t *ah, uint32_t direction) +{ + audio_return_t audio_ret = AUDIO_RET_OK; + + /* FIXME: If you need to reset, set verb inactive */ + /* const char *verb = NULL; */ + /* verb = AUDIO_USE_CASE_VERB_INACTIVE; */ + + AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER); + + AUDIO_LOG_INFO("do_route_reset++, direction(%p)", direction); + + if (direction == AUDIO_DIRECTION_OUT) { + ah->device.active_out &= 0x0; + } else { + ah->device.active_in &= 0x0; + } + + /* TO DO: Set Inactive */ + return audio_ret; +} + +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); + + 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 { + /* 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..cf4b1e8 --- /dev/null +++ b/tizen-audio-internal.h @@ -0,0 +1,335 @@ +#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 <dlog.h> +#include <time.h> +#include <sys/types.h> +#include <asoundlib.h> +#ifdef __USE_TINYALSA__ +#include <tinyalsa/asoundlib.h> +#endif +#include <pthread.h> +#include <use-case.h> +#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 */ +enum audio_device_type { + AUDIO_DEVICE_NONE = 0, + + /* output devices */ + AUDIO_DEVICE_OUT_SPEAKER = 0x00000001, + AUDIO_DEVICE_OUT_RECEIVER = 0x00000002, + AUDIO_DEVICE_OUT_JACK = 0x00000004, + AUDIO_DEVICE_OUT_BT_SCO = 0x00000008, + AUDIO_DEVICE_OUT_AUX = 0x00000010, + AUDIO_DEVICE_OUT_HDMI = 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 = 0x80000001, + AUDIO_DEVICE_IN_SUB_MIC = 0x80000002, + AUDIO_DEVICE_IN_JACK = 0x80000004, + AUDIO_DEVICE_IN_BT_SCO = 0x80000008, + 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; + +/* Verbs */ +#define AUDIO_USE_CASE_VERB_INACTIVE "Inactive" +#define AUDIO_USE_CASE_VERB_HIFI "HiFi" + +/* Modifiers */ +#define AUDIO_USE_CASE_MODIFIER_VOICESEARCH "VoiceSearch" +#define AUDIO_USE_CASE_MODIFIER_CAMCORDING "Camcording" +#define AUDIO_USE_CASE_MODIFIER_MEDIA "Media" +#define AUDIO_USE_CASE_MODIFIER_DUAL_MEDIA "DualMedia" + +#define streq !strcmp +#define strneq strcmp + +#define ALSA_DEFAULT_CARD "ORBIS" +#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_MODIFIERS 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 enum audio_route_mode { + VERB_NORMAL, + +/* Add verb mode if you need */ + +/* VERB_CALL, */ +/* VERB_VOIP */ + +} audio_route_mode_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_route_mode_t mode; +} 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_ucm { + snd_use_case_mgr_t* uc_mgr; +} audio_hal_ucm_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; + +/* Overall */ +typedef struct audio_hal { + audio_hal_device_t device; + audio_hal_volume_t volume; + audio_hal_ucm_t ucm; + audio_hal_mixer_t mixer; +} 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_ucm_init(audio_hal_t *ah); +audio_return_t _audio_ucm_deinit(audio_hal_t *ah); +void _audio_ucm_get_device_name(audio_hal_t *ah, const char *use_case, audio_direction_t direction, const char **value); +#define _audio_ucm_update_use_case _audio_ucm_set_use_case +audio_return_t _audio_ucm_set_use_case(audio_hal_t *ah, const char *verb, const char *devices[], const char *modifiers[]); +audio_return_t _audio_ucm_set_devices(audio_hal_t *ah, const char *verb, const char *devices[]); +audio_return_t _audio_ucm_set_modifiers(audio_hal_t *ah, const char *verb, const char *modifiers[]); +int _audio_ucm_fill_device_info_list(audio_hal_t *ah, audio_device_info_t *device_info_list, const char *verb); +audio_return_t _audio_ucm_get_verb(audio_hal_t *ah, const char **value); +audio_return_t _audio_ucm_reset_use_case(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_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 *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 *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-ucm.c b/tizen-audio-ucm.c new file mode 100644 index 0000000..2bc1aa4 --- /dev/null +++ b/tizen-audio-ucm.c @@ -0,0 +1,719 @@ +/* + * 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 <config.h> +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#ifdef ALSA_UCM_DEBUG_TIME +#include <sys/time.h> +#include <time.h> +#endif + +#include "tizen-audio-internal.h" + +#ifdef ALSA_UCM_DEBUG_TIME +#define SND_USE_CASE_SET __set_use_case_with_time +#else +#define SND_USE_CASE_SET snd_use_case_set +#endif + +#define UCM_PREFIX_CURRENT ">>> UCM current" +#define UCM_PREFIX_REQUESTED "> UCM requested" +#define UCM_PREFIX_CHANGED "<<< UCM changed" + +audio_return_t _audio_ucm_init(audio_hal_t *ah) +{ + AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER); + + snd_use_case_mgr_open(&ah->ucm.uc_mgr, ALSA_DEFAULT_CARD); + + if (!ah->ucm.uc_mgr) { + AUDIO_LOG_ERROR("uc_mgr open failed"); + return AUDIO_ERR_RESOURCE; + } + return AUDIO_RET_OK; +} + +audio_return_t _audio_ucm_deinit(audio_hal_t *ah) +{ + AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(ah->ucm.uc_mgr, AUDIO_ERR_PARAMETER); + + snd_use_case_mgr_close(ah->ucm.uc_mgr); + ah->ucm.uc_mgr = NULL; + + return AUDIO_RET_OK; +} + +void _audio_ucm_get_device_name(audio_hal_t *ah, const char *use_case, audio_direction_t direction, const char **value) +{ + char identifier[70] = { 0, }; + + AUDIO_RETURN_IF_FAIL(ah); + AUDIO_RETURN_IF_FAIL(ah->ucm.uc_mgr); + + snprintf(identifier, sizeof(identifier), "%sPCM//%s", + (direction == AUDIO_DIRECTION_IN) ? "Capture" : "Playback", use_case); + + snd_use_case_get(ah->ucm.uc_mgr, identifier, value); +} + +static inline void __add_ucm_device_info(audio_hal_t *ah, const char *use_case, audio_direction_t direction, audio_device_info_t *device_info_list, int *device_info_count) +{ + audio_device_info_t *device_info; + const char *device_name = NULL; + char *needle = NULL; + + AUDIO_RETURN_IF_FAIL(ah); + AUDIO_RETURN_IF_FAIL(ah->ucm.uc_mgr); + AUDIO_RETURN_IF_FAIL(device_info_list); + AUDIO_RETURN_IF_FAIL(device_info_count); + + _audio_ucm_get_device_name(ah, use_case, direction, &device_name); + if (device_name) { + device_info = &device_info_list[(*device_info_count)++]; + + memset(device_info, 0x00, sizeof(audio_device_info_t)); + device_info->api = AUDIO_DEVICE_API_ALSA; + device_info->direction = direction; + needle = strstr(&device_name[3], ","); + if (needle) { + device_info->alsa.device_idx = *(needle+1) - '0'; + device_info->alsa.card_name = strndup(&device_name[3], needle - (device_name+3)); + device_info->alsa.card_idx = snd_card_get_index(device_info->alsa.card_name); + AUDIO_LOG_DEBUG("Card name: %s", device_info->alsa.card_name); + } + + free((void *)device_name); + } +} + +int _audio_ucm_fill_device_info_list(audio_hal_t *ah, audio_device_info_t *device_info_list, const char *verb) +{ + int device_info_count = 0; + const char *curr_verb = NULL; + + AUDIO_RETURN_VAL_IF_FAIL(ah, device_info_count); + AUDIO_RETURN_VAL_IF_FAIL(ah->ucm.uc_mgr, device_info_count); + AUDIO_RETURN_VAL_IF_FAIL(device_info_list, device_info_count); + + if (!verb) { + snd_use_case_get(ah->ucm.uc_mgr, "_verb", &curr_verb); + verb = curr_verb; + } + + if (verb) { + __add_ucm_device_info(ah, verb, AUDIO_DIRECTION_IN, device_info_list, &device_info_count); + __add_ucm_device_info(ah, verb, AUDIO_DIRECTION_OUT, device_info_list, &device_info_count); + + if (curr_verb) + free((void *)curr_verb); + } + + return device_info_count; +} + +#define DUMP_LEN 512 + +static void __dump_use_case(const char* prefix, const char *verb, const char *devices[], int dev_count, const char *modifiers[], int mod_count) +{ + int i; + dump_data_t* dump = NULL; + + if (!(dump = dump_new(DUMP_LEN))) { + AUDIO_LOG_ERROR("Failed to create dump string..."); + return; + } + + /* Verb */ + dump_add_str(dump, "Verb [ %s ] Devices [ ", verb ? verb : AUDIO_USE_CASE_VERB_INACTIVE); + + /* Devices */ + if (devices) { + for (i = 0; i < dev_count; i++) { + dump_add_str(dump, (i != dev_count - 1) ? "%s, " : "%s", devices[i]); + } + } + dump_add_str(dump, " ] Modifier [ "); + + /* Modifiers */ + if (modifiers) { + for (i = 0; i < mod_count; i++) { + dump_add_str(dump, (i != mod_count - 1) ? "%s, " : "%s", modifiers[i]); + } + } + dump_add_str(dump, " ]"); + + AUDIO_LOG_INFO("TEST %s : %s", prefix, dump_get_str(dump)); + + dump_free(dump); +} + +#ifdef ALSA_UCM_DEBUG_TIME +static inline int __set_use_case_with_time(snd_use_case_mgr_t *uc_mgr, const char *identifier, const char *value) +{ + int ret = 0; + struct timeval t_start, t_stop; + unsigned long long t_diff = 0; + + gettimeofday(&t_start, NULL); + ret = snd_use_case_set(uc_mgr, identifier, value); + gettimeofday(&t_stop, NULL); + if (t_start.tv_sec < t_stop.tv_sec) + t_diff = (t_stop.tv_sec - t_start.tv_sec) * 1000000; + t_diff += (t_stop.tv_usec - t_start.tv_usec); + AUDIO_LOG_DEBUG("identifier %s value %s takes %lluusec", identifier, value, t_diff); + + return ret; +} +#endif + +/* UCM sequence + 1) If verb is null or verb is not changed + 1-1) If device is changed + (If there is request for same device, it will be ignored) + -> Set "Inactive" verb, disable modifiers & devices, set current verb again, enable devices & modifiers + (playback/capture device will be enabled again if there is no request for playback/capture device) + 1-2) If device is not changed + 1-2-1) If modifier is changed + (If there is request for same modifier, it will be ignored) + -> Disable modifiers, enable modifiers + 2) If verb is changed + -> Reset, set new verb, enable devices & modifiers + */ +audio_return_t _audio_ucm_set_use_case(audio_hal_t *ah, const char *verb, const char *devices[], const char *modifiers[]) +{ + audio_return_t audio_ret = AUDIO_RET_OK; + int is_verb_changed = 0, is_dev_changed = 0, is_mod_changed = 0; + const char *old_verb = NULL, **old_dev_list = NULL, **old_mod_list = NULL; + int old_dev_count = 0, dev_count = 0; + int old_mod_count = 0, mod_count = 0; + const char **dis_dev_list = NULL, **ena_dev_list = NULL; + const char **dis_mod_list = NULL, **ena_mod_list = NULL; + int dis_dev_count = 0, ena_dev_count = 0; + int dis_mod_count = 0, ena_mod_count = 0; + int i = 0, j = 0; + + AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(ah->ucm.uc_mgr, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(verb, AUDIO_ERR_PARAMETER); + + snd_use_case_get(ah->ucm.uc_mgr, "_verb", &old_verb); + old_dev_count = snd_use_case_get_list(ah->ucm.uc_mgr, "_enadevs", &old_dev_list); + old_mod_count = snd_use_case_get_list(ah->ucm.uc_mgr, "_enamods", &old_mod_list); + __dump_use_case(UCM_PREFIX_CURRENT, old_verb, old_dev_list, old_dev_count, old_mod_list, old_mod_count); + + if (devices) { + for (dev_count = 0; devices[dev_count]; dev_count++); + } + if (modifiers) { + for (mod_count = 0; modifiers[mod_count]; mod_count++); + } + + __dump_use_case(UCM_PREFIX_REQUESTED, verb, devices, dev_count, modifiers, mod_count); + + if (old_verb && streq(verb, old_verb)) { + AUDIO_LOG_DEBUG("current verb and new verb is same. No need to change verb, disable devices explicitely"); + + if (old_dev_count > 0) { + dis_dev_list = (const char **)malloc(sizeof(const char *) * old_dev_count); + for (i = 0; i < old_dev_count; i++) { + dis_dev_list[i] = NULL; + } + } + if (dev_count > 0) { + ena_dev_list = (const char **)malloc(sizeof(const char *) * dev_count); + for (i = 0; i < dev_count; i++) { + ena_dev_list[i] = NULL; + } + } + if (old_mod_count > 0) { + dis_mod_list = (const char **)malloc(sizeof(const char *) * old_mod_count); + for (i = 0; i < old_mod_count; i++) { + dis_mod_list[i] = NULL; + } + } + if (mod_count > 0) { + ena_mod_list = (const char **)malloc(sizeof(const char *) * mod_count); + for (i = 0; i < mod_count; i++) { + ena_mod_list[i] = NULL; + } + } + + /* update disable modifiers list which are not present in new modifier list */ + for (i = 0; i < old_mod_count; i++) { + int need_disable_mod = 1; + + for (j = 0; j < mod_count; j++) { + if (streq(old_mod_list[i], modifiers[j])) { + need_disable_mod = 0; + break; + } + } + if (need_disable_mod) { + if (is_mod_changed == 0) + is_mod_changed = 1; + dis_mod_list[dis_mod_count++] = old_mod_list[i]; + } + } + + /* update disable devices list which are not present in new device list */ + for (i = 0; i < old_dev_count; i++) { + int need_disable_dev = 1; + + for (j = 0; j < dev_count; j++) { + if (streq(old_dev_list[i], devices[j])) { + need_disable_dev = 0; + break; + } + } + if (need_disable_dev) { + if (is_dev_changed == 0) + is_dev_changed = 1; + dis_dev_list[dis_dev_count++] = old_dev_list[i]; + } + } + + /* update enable devices list which are not present in old device list */ + for (i = 0; i < dev_count; i++) { + int need_enable_dev = 1; + + for (j = 0; j < old_dev_count; j++) { + if (streq(devices[i], old_dev_list[j])) { + need_enable_dev = 0; + break; + } + } + if (need_enable_dev) { + if (is_dev_changed == 0) + is_dev_changed = 1; + ena_dev_list[ena_dev_count++] = devices[i]; + } + } + + /* update enable modifiers list which are not present in old modifier list */ + for (i = 0; i < mod_count; i++) { + int need_enable_mod = 1; + + for (j = 0; j < old_mod_count; j++) { + if (streq(modifiers[i], old_mod_list[j])) { + need_enable_mod = 0; + break; + } + } + if (need_enable_mod) { + if (is_mod_changed == 0) + is_mod_changed = 1; + ena_mod_list[ena_mod_count++] = modifiers[i]; + } + } + + /* disable modifiers */ + for (i = 0; i < dis_mod_count; i++) { + AUDIO_LOG_INFO("Disable modifier : %s", dis_mod_list[i]); + if (snd_use_case_set(ah->ucm.uc_mgr, "_dismod", dis_mod_list[i]) < 0) + AUDIO_LOG_ERROR("disable %s modifier failed", dis_mod_list[i]); + } + + /* disable devices */ + for (i = 0; i < dis_dev_count; i++) { + AUDIO_LOG_INFO("Disable device : %s", dis_dev_list[i]); + if (snd_use_case_set(ah->ucm.uc_mgr, "_disdev", dis_dev_list[i]) < 0) + AUDIO_LOG_ERROR("disable %s device failed", dis_dev_list[i]); + } + + /* enable devices */ + for (i = 0; i < ena_dev_count; i++) { + AUDIO_LOG_INFO("Enable device : %s", ena_dev_list[i]); + if (snd_use_case_set(ah->ucm.uc_mgr, "_enadev", ena_dev_list[i]) < 0) + AUDIO_LOG_ERROR("enable %s device failed", ena_dev_list[i]); + } + + /* enable modifiers */ + for (i = 0; i < ena_mod_count; i++) { + AUDIO_LOG_INFO("Enable modifier : %s", ena_mod_list[i]); + if (snd_use_case_set(ah->ucm.uc_mgr, "_enamod", ena_mod_list[i]) < 0) + AUDIO_LOG_ERROR("enable %s modifier failed", ena_mod_list[i]); + } + } else { + is_verb_changed = 1; + + AUDIO_LOG_DEBUG("Setting new verb: %s", verb); + /* set new verb */ + if (snd_use_case_set(ah->ucm.uc_mgr, "_verb", verb) < 0) { + AUDIO_LOG_ERROR("Setting verb %s failed", verb); + audio_ret = AUDIO_ERR_UNDEFINED; + goto exit; + } + /* enable devices */ + for (i = 0; i < dev_count; i++) { + AUDIO_LOG_DEBUG("Enable device : %s", devices[i]); + if (snd_use_case_set(ah->ucm.uc_mgr, "_enadev", devices[i]) < 0) + AUDIO_LOG_ERROR("Enable %s device failed", devices[i]); + } + /* enable modifiers */ + for (i = 0; i < mod_count; i++) { + AUDIO_LOG_DEBUG("Enable modifier : %s", modifiers[i]); + if (snd_use_case_set(ah->ucm.uc_mgr, "_enamod", modifiers[i]) < 0) + AUDIO_LOG_ERROR("Enable %s modifier failed", modifiers[i]); + } + } + +exit: + if (old_verb) + free((void *)old_verb); + if (old_dev_list) + snd_use_case_free_list(old_dev_list, old_dev_count); + if (old_mod_list) + snd_use_case_free_list(old_mod_list, old_mod_count); + if (dis_dev_list) + free((void *)dis_dev_list); + if (ena_dev_list) + free((void *)ena_dev_list); + if (dis_mod_list) + free((void *)dis_mod_list); + if (ena_mod_list) + free((void *)ena_mod_list); + + if (is_verb_changed == 1 || is_dev_changed == 1 || is_mod_changed == 1) { + const char *new_verb = NULL, **new_dev_list = NULL, **new_mod_list = NULL; + int new_dev_count = 0, new_mod_count = 0; + + snd_use_case_get(ah->ucm.uc_mgr, "_verb", &new_verb); + new_dev_count = snd_use_case_get_list(ah->ucm.uc_mgr, "_enadevs", &new_dev_list); + new_mod_count = snd_use_case_get_list(ah->ucm.uc_mgr, "_enamods", &new_mod_list); + __dump_use_case(UCM_PREFIX_CHANGED, new_verb, new_dev_list, new_dev_count, new_mod_list, new_mod_count); + + if (new_verb) + free((void *)new_verb); + if (new_dev_list) + snd_use_case_free_list(new_dev_list, new_dev_count); + if (new_mod_list) + snd_use_case_free_list(new_mod_list, new_mod_count); + } + + return audio_ret; +} + +audio_return_t _audio_ucm_set_devices(audio_hal_t *ah, const char *verb, const char *devices[]) +{ + audio_return_t audio_ret = AUDIO_RET_OK; + int is_verb_changed = 0, is_dev_changed = 0; + const char *old_verb = NULL, **old_dev_list = NULL; + int old_dev_count = 0, dev_count = 0; + const char **dis_dev_list = NULL, **ena_dev_list = NULL; + int dis_dev_count = 0, ena_dev_count = 0; + int i = 0, j = 0; + + AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(ah->ucm.uc_mgr, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(verb, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(devices, AUDIO_ERR_PARAMETER); + + snd_use_case_get(ah->ucm.uc_mgr, "_verb", &old_verb); + old_dev_count = snd_use_case_get_list(ah->ucm.uc_mgr, "_enadevs", &old_dev_list); + __dump_use_case(UCM_PREFIX_CURRENT, old_verb, old_dev_list, old_dev_count, NULL, 0); + + if (devices) { + for (dev_count = 0; devices[dev_count]; dev_count++); + } + + __dump_use_case(UCM_PREFIX_REQUESTED, verb, devices, dev_count, NULL, 0); + + if (old_verb && streq(verb, old_verb)) { + AUDIO_LOG_DEBUG("current verb and new verb is same. No need to change verb, disable devices explicitely"); + + if (old_dev_count > 0) { + dis_dev_list = (const char **)malloc(sizeof(const char *) * old_dev_count); + for (i = 0; i < old_dev_count; i++) { + dis_dev_list[i] = NULL; + } + } + if (dev_count > 0) { + ena_dev_list = (const char **)malloc(sizeof(const char *) * dev_count); + for (i = 0; i < dev_count; i++) { + ena_dev_list[i] = NULL; + } + } + + /* update disable devices list which are not present in new device list */ + for (i = 0; i < old_dev_count; i++) { + int need_disable_dev = 1; + + for (j = 0; j < dev_count; j++) { + if (streq(old_dev_list[i], devices[j])) { + need_disable_dev = 0; + break; + } + } + if (need_disable_dev) { + if (is_dev_changed == 0) + is_dev_changed = 1; + dis_dev_list[dis_dev_count++] = old_dev_list[i]; + } + } + + /* update enable devices list which are not present in old device list */ + for (i = 0; i < dev_count; i++) { + int need_enable_dev = 1; + + for (j = 0; j < old_dev_count; j++) { + if (streq(devices[i], old_dev_list[j])) { + need_enable_dev = 0; + break; + } + } + if (need_enable_dev) { + if (is_dev_changed == 0) + is_dev_changed = 1; + ena_dev_list[ena_dev_count++] = devices[i]; + } + } + + /* disable devices */ + for (i = 0; i < dis_dev_count; i++) { + AUDIO_LOG_INFO("Disable device : %s", dis_dev_list[i]); + if (snd_use_case_set(ah->ucm.uc_mgr, "_disdev", dis_dev_list[i]) < 0) + AUDIO_LOG_ERROR("disable %s device failed", dis_dev_list[i]); + } + + /* enable devices */ + for (i = 0; i < ena_dev_count; i++) { + AUDIO_LOG_INFO("Enable device : %s", ena_dev_list[i]); + if (snd_use_case_set(ah->ucm.uc_mgr, "_enadev", ena_dev_list[i]) < 0) + AUDIO_LOG_ERROR("enable %s device failed", ena_dev_list[i]); + } + + } else { + is_verb_changed = 1; + + AUDIO_LOG_DEBUG("Setting new verb: %s", verb); + /* set new verb */ + if (snd_use_case_set(ah->ucm.uc_mgr, "_verb", verb) < 0) { + AUDIO_LOG_ERROR("Setting verb %s failed", verb); + audio_ret = AUDIO_ERR_UNDEFINED; + goto exit; + } + /* enable devices */ + for (i = 0; i < dev_count; i++) { + AUDIO_LOG_DEBUG("Enable device : %s", devices[i]); + if (snd_use_case_set(ah->ucm.uc_mgr, "_enadev", devices[i]) < 0) + AUDIO_LOG_ERROR("Enable %s device failed", devices[i]); + } + } + +exit: + if (old_verb) + free((void *)old_verb); + if (old_dev_list) + snd_use_case_free_list(old_dev_list, old_dev_count); + if (dis_dev_list) + free((void *)dis_dev_list); + if (ena_dev_list) + free((void *)ena_dev_list); + + if (is_verb_changed == 1 || is_dev_changed == 1) { + const char *new_verb = NULL, **new_dev_list = NULL; + int new_dev_count = 0; + + snd_use_case_get(ah->ucm.uc_mgr, "_verb", &new_verb); + new_dev_count = snd_use_case_get_list(ah->ucm.uc_mgr, "_enadevs", &new_dev_list); + __dump_use_case(UCM_PREFIX_CHANGED, new_verb, new_dev_list, new_dev_count, NULL, 0); + + if (new_verb) + free((void *)new_verb); + if (new_dev_list) + snd_use_case_free_list(new_dev_list, new_dev_count); + } + + return audio_ret; + +} + +audio_return_t _audio_ucm_set_modifiers(audio_hal_t *ah, const char *verb, const char *modifiers[]) +{ + audio_return_t audio_ret = AUDIO_RET_OK; + int is_verb_changed = 0, is_mod_changed = 0; + const char *old_verb = NULL, **old_mod_list = NULL; + int old_mod_count = 0, mod_count = 0; + const char **dis_mod_list = NULL, **ena_mod_list = NULL; + int dis_mod_count = 0, ena_mod_count = 0; + int i = 0, j = 0; + + AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(ah->ucm.uc_mgr, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(verb, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(modifiers, AUDIO_ERR_PARAMETER); + + snd_use_case_get(ah->ucm.uc_mgr, "_verb", &old_verb); + old_mod_count = snd_use_case_get_list(ah->ucm.uc_mgr, "_enamods", &old_mod_list); + __dump_use_case(UCM_PREFIX_CURRENT, old_verb, NULL, 0, old_mod_list, old_mod_count); + + if (modifiers) { + for (mod_count = 0; modifiers[mod_count]; mod_count++); + } + + __dump_use_case(UCM_PREFIX_REQUESTED, verb, NULL, 0, modifiers, mod_count); + + if (old_verb && streq(verb, old_verb)) { + AUDIO_LOG_DEBUG("current verb and new verb is same. No need to change verb, disable devices explicitely"); + + if (old_mod_count > 0) { + dis_mod_list = (const char **)malloc(sizeof(const char *) * old_mod_count); + for (i = 0; i < old_mod_count; i++) { + dis_mod_list[i] = NULL; + } + } + if (mod_count > 0) { + ena_mod_list = (const char **)malloc(sizeof(const char *) * mod_count); + for (i = 0; i < mod_count; i++) { + ena_mod_list[i] = NULL; + } + } + + /* update disable modifiers list which are not present in new modifier list */ + for (i = 0; i < old_mod_count; i++) { + int need_disable_mod = 1; + + for (j = 0; j < mod_count; j++) { + if (streq(old_mod_list[i], modifiers[j])) { + need_disable_mod = 0; + break; + } + } + if (need_disable_mod) { + if (is_mod_changed == 0) + is_mod_changed = 1; + dis_mod_list[dis_mod_count++] = old_mod_list[i]; + } + } + + /* update enable modifiers list which are not present in old modifier list */ + for (i = 0; i < mod_count; i++) { + int need_enable_mod = 1; + + for (j = 0; j < old_mod_count; j++) { + if (streq(modifiers[i], old_mod_list[j])) { + need_enable_mod = 0; + break; + } + } + if (need_enable_mod) { + if (is_mod_changed == 0) + is_mod_changed = 1; + ena_mod_list[ena_mod_count++] = modifiers[i]; + } + } + + /* disable modifiers */ + for (i = 0; i < dis_mod_count; i++) { + AUDIO_LOG_INFO("Disable modifier : %s", dis_mod_list[i]); + if (snd_use_case_set(ah->ucm.uc_mgr, "_dismod", dis_mod_list[i]) < 0) + AUDIO_LOG_ERROR("disable %s modifier failed", dis_mod_list[i]); + } + + /* enable modifiers */ + for (i = 0; i < ena_mod_count; i++) { + AUDIO_LOG_INFO("Enable modifier : %s", ena_mod_list[i]); + if (snd_use_case_set(ah->ucm.uc_mgr, "_enamod", ena_mod_list[i]) < 0) + AUDIO_LOG_ERROR("enable %s modifier failed", ena_mod_list[i]); + } + } else { + is_verb_changed = 1; + + AUDIO_LOG_DEBUG("Setting new verb: %s", verb); + /* set new verb */ + if (snd_use_case_set(ah->ucm.uc_mgr, "_verb", verb) < 0) { + AUDIO_LOG_ERROR("Setting verb %s failed", verb); + audio_ret = AUDIO_ERR_UNDEFINED; + goto exit; + } + /* enable modifiers */ + for (i = 0; i < mod_count; i++) { + AUDIO_LOG_DEBUG("Enable modifier : %s", modifiers[i]); + if (snd_use_case_set(ah->ucm.uc_mgr, "_enamod", modifiers[i]) < 0) + AUDIO_LOG_ERROR("Enable %s modifier failed", modifiers[i]); + } + } + +exit: + if (old_verb) + free((void *)old_verb); + if (old_mod_list) + snd_use_case_free_list(old_mod_list, old_mod_count); + if (dis_mod_list) + free((void *)dis_mod_list); + if (ena_mod_list) + free((void *)ena_mod_list); + + if (is_verb_changed == 1 || is_mod_changed == 1) { + const char *new_verb = NULL, **new_mod_list = NULL; + int new_mod_count = 0; + + snd_use_case_get(ah->ucm.uc_mgr, "_verb", &new_verb); + new_mod_count = snd_use_case_get_list(ah->ucm.uc_mgr, "_enamods", &new_mod_list); + __dump_use_case(UCM_PREFIX_CHANGED, new_verb, NULL, 0, new_mod_list, new_mod_count); + + if (new_verb) + free((void *)new_verb); + if (new_mod_list) + snd_use_case_free_list(new_mod_list, new_mod_count); + } + + return audio_ret; +} + +audio_return_t _audio_ucm_get_verb(audio_hal_t *ah, const char **value) +{ + audio_return_t ret = AUDIO_RET_OK; + + AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(ah->ucm.uc_mgr, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(value, AUDIO_ERR_PARAMETER); + + if ((ret = snd_use_case_get(ah->ucm.uc_mgr, "_verb", value)) < 0) { + AUDIO_LOG_ERROR("Getting current verb failed: Reason %d", ret); + ret = AUDIO_ERR_UNDEFINED; + } + + return ret; +} + + +audio_return_t _audio_ucm_reset_use_case(audio_hal_t *ah) +{ + audio_return_t ret = AUDIO_RET_OK; + + AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(ah->ucm.uc_mgr, AUDIO_ERR_PARAMETER); + + AUDIO_LOG_INFO(">>> UCM reset Verb [ %s ]", AUDIO_USE_CASE_VERB_INACTIVE); + + if ((ret = snd_use_case_set(ah->ucm.uc_mgr, "_verb", AUDIO_USE_CASE_VERB_INACTIVE)) < 0) { + AUDIO_LOG_ERROR("Reset use case failed: Reason %d", ret); + ret = AUDIO_ERR_UNDEFINED; + } + + return ret; +} + diff --git a/tizen-audio-util.c b/tizen-audio-util.c new file mode 100644 index 0000000..4e06923 --- /dev/null +++ b/tizen-audio-util.c @@ -0,0 +1,503 @@ +/* + * 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 <config.h> +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <pthread.h> + +#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_ret = _audio_mixer_control_get_value(ah, ctl_name, val); + return audio_ret; +} + +audio_return_t _audio_mixer_control_get_value(audio_hal_t *ah, 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); + + pthread_mutex_lock(&(ah->mixer.mutex)); + + ret = snd_ctl_open(&handle, ALSA_DEFAULT_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 *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(ctl_name, AUDIO_ERR_PARAMETER); + + pthread_mutex_lock(&(ah->mixer.mutex)); + + ret = snd_ctl_open(&handle, ALSA_DEFAULT_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..c8f9a42 --- /dev/null +++ b/tizen-audio-volume.c @@ -0,0 +1,394 @@ +/* + * 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 <config.h> +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <stdbool.h> +#include <string.h> +#include <math.h> +#include <vconf.h> +#include <iniparser.h> + +#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(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); + + *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(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); + + /* 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); + + /* set mixer related to H/W volume if needed */ + + 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); + + /* TODO. Not implemented */ + + 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); + /* TODO. Not implemented */ + + return audio_ret; +} diff --git a/tizen-audio.c b/tizen-audio.c new file mode 100644 index 0000000..882a9b4 --- /dev/null +++ b/tizen-audio.c @@ -0,0 +1,251 @@ +/* + * 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 <config.h> +#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_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_ucm_init(ah)))) { + AUDIO_LOG_ERROR("ucm init failed"); + goto error_exit; + } + if (AUDIO_IS_ERROR((ret = _audio_util_init(ah)))) { + AUDIO_LOG_ERROR("mixer 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_ucm_deinit(ah); + _audio_util_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..8dd3b44 --- /dev/null +++ b/tizen-audio.h @@ -0,0 +1,124 @@ +#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 <stdint.h> + +/* 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_NOT_IMPLEMENTED = (int32_t)0x80001004, +} 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 ; + +/* Overall */ +typedef struct audio_interface { + audio_return_t (*init)(void **audio_handle); + audio_return_t (*deinit)(void *audio_handle); + 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); + 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); + 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); +} 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); +#endif |