diff options
author | jk7744.park <jk7744.park@samsung.com> | 2015-09-08 21:29:19 +0900 |
---|---|---|
committer | jk7744.park <jk7744.park@samsung.com> | 2015-09-08 21:29:19 +0900 |
commit | 150956c658d39b46b86383f5bcc5d9af79f760d1 (patch) | |
tree | 0fecc994a3e11ac961aa1471de6d0d0f75092fce | |
parent | 15f9a36c03cdeb2987270680770f361c316ea0a6 (diff) | |
download | audio-hal-e3250-tizen_2.3.1.tar.gz audio-hal-e3250-tizen_2.3.1.tar.bz2 audio-hal-e3250-tizen_2.3.1.zip |
tizen 2.3.1 releasetizen_2.3.1_releasesubmit/tizen_2.3.1/20150915.072152tizen_2.3.1
-rw-r--r-- | AUTHORS | 1 | ||||
-rw-r--r-- | LICENSE.APLv2 | 204 | ||||
-rwxr-xr-x | Makefile.am | 13 | ||||
-rw-r--r-- | NOTICE | 3 | ||||
-rwxr-xr-x | autogen.sh | 10 | ||||
-rwxr-xr-x | configure.ac | 49 | ||||
-rw-r--r-- | packaging/audio-hal-e3250.spec | 47 | ||||
-rwxr-xr-x | tizen-audio-device.c | 369 | ||||
-rwxr-xr-x | tizen-audio-internal.h | 235 | ||||
-rwxr-xr-x | tizen-audio-session.c | 162 | ||||
-rwxr-xr-x | tizen-audio-stream.c | 473 | ||||
-rwxr-xr-x | tizen-audio-ucm.c | 447 | ||||
-rwxr-xr-x | tizen-audio-util.c | 357 | ||||
-rwxr-xr-x | tizen-audio.c | 125 | ||||
-rwxr-xr-x | tizen-audio.h | 274 |
15 files changed, 2769 insertions, 0 deletions
@@ -0,0 +1 @@ +Hyunseok Lee <hs7388.lee at samsung.com> diff --git a/LICENSE.APLv2 b/LICENSE.APLv2 new file mode 100644 index 0000000..76f9119 --- /dev/null +++ b/LICENSE.APLv2 @@ -0,0 +1,204 @@ +Copyright (c) 2000 - 2013 Samsung Electronics Co., Ltd. All rights reserved. + + 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 100755 index 0000000..af5fec7 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,13 @@ +lib_LTLIBRARIES = libtizen-audio.la + +libtizen_audio_la_SOURCES = tizen-audio.c \ + tizen-audio-session.c \ + tizen-audio-device.c \ + tizen-audio-stream.c \ + tizen-audio-ucm.c \ + tizen-audio-util.c +libtizen_audio_la_LDFLAGS = $(AM_LDFLAGS) -disable-static -avoid-version +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) +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/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 100755 index 0000000..6d82f53 --- /dev/null +++ b/configure.ac @@ -0,0 +1,49 @@ +AC_PREREQ([2.67]) + +AC_INIT([audio-hal-e3250], [0.1]) +AM_INIT_AUTOMAKE([-Wall -Werror foreign]) +AC_CONFIG_HEADERS([config.h]) + +AC_CONFIG_MACRO_DIR([m4]) + +# Checks for programs. +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) + +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-e3250.spec b/packaging/audio-hal-e3250.spec new file mode 100644 index 0000000..c6b0075 --- /dev/null +++ b/packaging/audio-hal-e3250.spec @@ -0,0 +1,47 @@ +Name: audio-hal-e3250 +Summary: TIZEN Audio HAL for Exynos3250 +Version: 0.2.19 +Release: 0 +Group: System/Libraries +License: Apache-2.0 +URL: http://tizen.org +Source0: audio-hal-e3250-%{version}.tar.gz +BuildRequires: pkgconfig(alsa) +BuildRequires: pkgconfig(vconf) +BuildRequires: pkgconfig(iniparser) +BuildRequires: pkgconfig(dlog) +Provides: libtizen-audio.so + +%description +TIZEN Audio HAL for Exynos3250 + +%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" + +%autogen +%configure + +make %{?jobs:-j%jobs} + +%install +rm -rf %{buildroot} +mkdir -p %{buildroot}/usr/share/license +cp LICENSE.APLv2 %{buildroot}/usr/share/license/%{name} + +%make_install + +%post +/sbin/ldconfig + +%postun +/sbin/ldconfig + +%files +%defattr(-,root,root,-) +/usr/lib/libtizen-audio.so +%{_datadir}/license/%{name} diff --git a/tizen-audio-device.c b/tizen-audio-device.c new file mode 100755 index 0000000..c8a6d93 --- /dev/null +++ b/tizen-audio-device.c @@ -0,0 +1,369 @@ +/* + * 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" + +audio_return_t _audio_device_init (audio_mgr_t *am) +{ + AUDIO_RETURN_VAL_IF_FAIL(am, AUDIO_ERR_PARAMETER); + + am->device.active_in = AUDIO_DEVICE_IN_NONE; + am->device.active_out = AUDIO_DEVICE_OUT_NONE; + am->device.route_flag = AUDIO_ROUTE_FLAG_NONE; + am->device.pcm_in = NULL; + am->device.pcm_out = NULL; + pthread_mutex_init(&am->device.pcm_lock, NULL); + am->device.pcm_count = 0; + + return AUDIO_RET_OK; +} + +audio_return_t _audio_device_deinit (audio_mgr_t *am) +{ + AUDIO_RETURN_VAL_IF_FAIL(am, AUDIO_ERR_PARAMETER); + + return AUDIO_RET_OK; +} + +static void __load_n_open_device_with_params (audio_mgr_t *am, audio_device_info_t *device_info, int load_only) +{ + audio_device_param_info_t params[AUDIO_DEVICE_PARAM_MAX]; + int dev_param_count = 0; + + AUDIO_RETURN_IF_FAIL(am); + AUDIO_RETURN_IF_FAIL(device_info); + AUDIO_RETURN_IF_FAIL(am->cb_intf.load_device); + AUDIO_RETURN_IF_FAIL(am->cb_intf.open_device); + + memset(¶ms[0], 0, sizeof(audio_device_param_info_t) * AUDIO_DEVICE_PARAM_MAX); + + if (device_info->api == AUDIO_DEVICE_API_ALSA) { + /* 6 = "hw: ,idx */ + if ((device_info->name = malloc(strlen(device_info->alsa.card_name) + 6)) != NULL) { + sprintf(device_info->name, "hw:%s,%d", device_info->alsa.card_name, device_info->alsa.device_idx); + } + if (device_info->direction == AUDIO_DIRECTION_OUT) { + /* ALSA playback */ + switch (device_info->alsa.device_idx) { + /* default device */ + case 0: + case 3: + device_info->is_default_device = 1; + params[dev_param_count].param = AUDIO_DEVICE_PARAM_SUSPEND_TIMEOUT; + params[dev_param_count++].u32_v = 1; + params[dev_param_count].param = AUDIO_DEVICE_PARAM_TSCHED_BUF_SIZE; + params[dev_param_count++].u32_v = 35280; + AUDIO_LOG_INFO("HiFi Device"); + break; + /* HDMI device */ + case 1: + /* VOICE PCM. */ + break; + default: + AUDIO_LOG_INFO("Unknown Playback Device"); + break; + } + } else if (device_info->direction == AUDIO_DIRECTION_IN) { + /* ALSA capture */ + switch (device_info->alsa.device_idx) { + /* default device */ + case 0: + device_info->is_default_device = 1; + /* use mmap */ + params[dev_param_count].param = AUDIO_DEVICE_PARAM_USE_MMAP; + params[dev_param_count++].u32_v = 1; + params[dev_param_count].param = AUDIO_DEVICE_PARAM_SAMPLERATE; + params[dev_param_count++].u32_v = 48000; + params[dev_param_count].param = AUDIO_DEVICE_PARAM_ALTERNATE_RATE; + params[dev_param_count++].u32_v = 48000; + break; + default: + AUDIO_LOG_INFO("Unknown Capture Device"); + break; + } + } + + AUDIO_LOG_INFO("open alsa %s device hw:%s,%d", (device_info->direction == AUDIO_DIRECTION_IN) ? "capture" : "playback", + device_info->alsa.card_name, device_info->alsa.device_idx); + } + + if (load_only) { + am->cb_intf.load_device(am->platform_data, device_info, ¶ms[0]); + } else { + am->cb_intf.open_device(am->platform_data, device_info, ¶ms[0]); + } +} + +static audio_return_t __set_route_ap_playback_capture (audio_mgr_t *am, uint32_t device_in, uint32_t device_out, uint32_t route_flag) +{ + audio_return_t audio_ret = AUDIO_RET_OK; + int dev_idx = 0; + const char *verb = NULL; + const char *devices[MAX_DEVICES] = {NULL,}; + const char *modifiers[MAX_MODIFIERS] = {NULL,}; + + verb = AUDIO_USE_CASE_VERB_HIFI; + + if (route_flag & AUDIO_ROUTE_FLAG_MUTE_POLICY) { + devices[dev_idx++] = AUDIO_USE_CASE_DEV_HEADSET; + } else if (route_flag & AUDIO_ROUTE_FLAG_DUAL_OUT) { + devices[dev_idx++] = AUDIO_USE_CASE_DEV_SPEAKER; + devices[dev_idx++] = AUDIO_USE_CASE_DEV_HEADSET; + if (device_out == AUDIO_DEVICE_OUT_MIRRORING) { + AUDIO_LOG_INFO("Skip WFD enable during DUAL path"); + } + } else { + switch (device_out) { + case AUDIO_DEVICE_OUT_SPEAKER: + devices[dev_idx++] = AUDIO_USE_CASE_DEV_SPEAKER; + break; + case AUDIO_DEVICE_OUT_RECEIVER: + devices[dev_idx++] = AUDIO_USE_CASE_DEV_HANDSET; + break; + case AUDIO_DEVICE_OUT_WIRED_ACCESSORY: + devices[dev_idx++] = AUDIO_USE_CASE_DEV_HEADSET; + break; + /* even BT SCO is opened by call app, we cannot use BT SCO on HiFi verb */ + case AUDIO_DEVICE_OUT_BT_SCO: + devices[dev_idx++] = AUDIO_USE_CASE_DEV_SPEAKER; + break; + default: + break; + } + } + + if (am->session.is_radio_on == 0 || am->session.is_recording == 1) { + switch (device_in) { + case AUDIO_DEVICE_IN_MIC: + devices[dev_idx++] = AUDIO_USE_CASE_DEV_MAIN_MIC; + break; + case AUDIO_DEVICE_IN_WIRED_ACCESSORY: + devices[dev_idx++] = AUDIO_USE_CASE_DEV_HEADSET_MIC; + break; + /* even BT SCO is opened by call app, we cannot use BT SCO on HiFi verb */ + case AUDIO_DEVICE_IN_BT_SCO: + devices[dev_idx++] = AUDIO_USE_CASE_DEV_MAIN_MIC; + break; + default: + break; + } + } + + /* TODO. Handle voice recognition when seperate devices are available */ + audio_ret = _audio_ucm_update_use_case(am, verb, devices, modifiers); + if (AUDIO_IS_ERROR(audio_ret)) { + return audio_ret; + } + return AUDIO_RET_OK; +} + +audio_return_t _set_route_voicecall (audio_mgr_t *am, uint32_t device_in, uint32_t device_out, uint32_t route_flag) +{ + audio_return_t audio_ret = AUDIO_RET_OK; + int dev_idx = 0; + const char *verb = NULL; + const char *devices[MAX_DEVICES] = {NULL,}; + + verb = AUDIO_USE_CASE_VERB_VOICECALL; + + switch (device_out) { + case AUDIO_DEVICE_OUT_SPEAKER: + /* FIXME: WB handling is needed */ + devices[dev_idx++] = AUDIO_USE_CASE_DEV_SPEAKER; + break; + case AUDIO_DEVICE_OUT_RECEIVER: + /* FIXME: WB handling is needed */ + devices[dev_idx++] = AUDIO_USE_CASE_DEV_HANDSET; + break; + case AUDIO_DEVICE_OUT_WIRED_ACCESSORY: + devices[dev_idx++] = AUDIO_USE_CASE_DEV_HEADSET; + break; + case AUDIO_DEVICE_OUT_BT_SCO: + devices[dev_idx++] = AUDIO_USE_CASE_DEV_BT_HEADSET; + break; + default: + break; + } + + switch (device_in) { + case AUDIO_DEVICE_IN_MIC: + devices[dev_idx++] = AUDIO_USE_CASE_DEV_MAIN_MIC; + break; + case AUDIO_DEVICE_IN_WIRED_ACCESSORY: + devices[dev_idx++] = AUDIO_USE_CASE_DEV_HEADSET_MIC; + break; + default: + break; + } + + /* FIXME. Get network info and configure rate in pcm device */ + audio_ret = _audio_ucm_update_use_case(am, verb, devices, NULL); + + return audio_ret; +} + +static audio_return_t __set_route_voip (audio_mgr_t *am, uint32_t device_in, uint32_t device_out, uint32_t route_flag) +{ + int dev_idx = 0; + const char *verb = NULL; + const char *devices[MAX_DEVICES] = {NULL,}; + + verb = AUDIO_USE_CASE_VERB_HIFI; /* Modify later to use VIRTUALAUDIO to enable echo cancellation */ + + switch (device_out) { + case AUDIO_DEVICE_OUT_SPEAKER: + devices[dev_idx++] = AUDIO_USE_CASE_DEV_SPEAKER; + break; + case AUDIO_DEVICE_OUT_RECEIVER: + devices[dev_idx++] = AUDIO_USE_CASE_DEV_HANDSET; + break; + case AUDIO_DEVICE_OUT_WIRED_ACCESSORY: + devices[dev_idx++] = AUDIO_USE_CASE_DEV_HEADSET; + break; + case AUDIO_DEVICE_OUT_BT_SCO: + devices[dev_idx++] = AUDIO_USE_CASE_DEV_BT_HEADSET; + break; + default: + break; + } + + switch (device_in) { + case AUDIO_DEVICE_IN_MIC: + devices[dev_idx++] = AUDIO_USE_CASE_DEV_MAIN_MIC; + break; + case AUDIO_DEVICE_IN_WIRED_ACCESSORY: + devices[dev_idx++] = AUDIO_USE_CASE_DEV_HEADSET_MIC; + break; + case AUDIO_DEVICE_IN_BT_SCO: + devices[dev_idx++] = AUDIO_USE_CASE_DEV_BT_MIC; + break; + default: + break; + } + + return _audio_ucm_update_use_case(am, verb, devices, NULL); +} + +audio_return_t _reset_route (audio_mgr_t *am, int need_inactive) +{ + const char *devices[MAX_DEVICES] = {NULL,}; + const char *modifiers[MAX_MODIFIERS] = {NULL,}; + + if(need_inactive) { + _audio_ucm_update_use_case(am, AUDIO_USE_CASE_VERB_INACTIVE, devices, modifiers); + } + _audio_ucm_update_use_case(am, AUDIO_USE_CASE_VERB_HIFI, devices, modifiers); + __set_route_ap_playback_capture(am, am->device.active_in, am->device.active_out, 0); + + return AUDIO_RET_OK; +} + +audio_return_t audio_set_route (void *userdata, uint32_t session, uint32_t subsession, uint32_t device_in, uint32_t device_out, uint32_t route_flag) +{ + audio_return_t audio_ret = AUDIO_RET_OK; + audio_mgr_t *am = (audio_mgr_t *)userdata; + int i, dev_info_count = 0; + audio_device_info_t device_info_list[AUDIO_DEVICE_INFO_LIST_MAX]; + + am->device.active_in = device_in; + am->device.active_out = device_out; + am->device.route_flag = route_flag; + + AUDIO_RETURN_VAL_IF_FAIL(am, AUDIO_ERR_PARAMETER); + + AUDIO_LOG_INFO("session:%d,%d in:%d out:%d flag:0x%x", session, subsession, device_in, device_out, route_flag); + + if ((session == AUDIO_SESSION_VOICECALL) && (subsession == AUDIO_SUBSESSION_VOICE)) { + audio_ret = _set_route_voicecall(am, device_in, device_out, route_flag); + if (AUDIO_IS_ERROR(audio_ret)) { + AUDIO_LOG_WARN("set voicecall route return 0x%x", audio_ret); + } + } else if ((session == AUDIO_SESSION_VOIP) && (subsession == AUDIO_SUBSESSION_VOICE)) { + audio_ret = __set_route_voip(am, device_in, device_out, route_flag); + if (AUDIO_IS_ERROR(audio_ret)) { + AUDIO_LOG_WARN("set voip route return 0x%x", audio_ret); + } + } else { + audio_ret = __set_route_ap_playback_capture(am, device_in, device_out, route_flag); + if (AUDIO_IS_ERROR(audio_ret)) { + AUDIO_LOG_WARN("set playback route return 0x%x", audio_ret); + } + } + + if (!AUDIO_IS_ERROR(audio_ret)) { + memset((void *)&device_info_list[0], 0, sizeof(audio_device_info_t) * AUDIO_DEVICE_INFO_LIST_MAX); + /* fill device params & open device */ + dev_info_count = _audio_ucm_fill_device_info_list(am, &device_info_list[0], NULL); + for (i = 0; i < dev_info_count; i++) { + __load_n_open_device_with_params(am, &device_info_list[i], 0); + } + } + return AUDIO_RET_OK; +} + +audio_return_t audio_alsa_pcm_open (void *userdata, void **pcm_handle, char *device_name, uint32_t direction, int mode) +{ + audio_return_t audio_ret = AUDIO_RET_OK; + audio_mgr_t *am = (audio_mgr_t *)userdata; + int err; + + AUDIO_RETURN_VAL_IF_FAIL(am, AUDIO_ERR_PARAMETER); + +// pthread_mutex_lock(&am->device.pcm_lock); + 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)); + pthread_mutex_unlock(&am->device.pcm_lock); + return AUDIO_ERR_RESOURCE; + } + am->device.pcm_count++; + AUDIO_LOG_INFO("PCM handle 0x%x(%s,%s) opened(count:%d)", *pcm_handle, device_name, (direction == AUDIO_DIRECTION_OUT) ? "playback" : "capture", am->device.pcm_count); +// pthread_mutex_unlock(&am->device.pcm_lock); + + return audio_ret; +} + +audio_return_t audio_alsa_pcm_close (void *userdata, void *pcm_handle) +{ + audio_return_t audio_ret = AUDIO_RET_OK; + audio_mgr_t *am = (audio_mgr_t *)userdata; + int err; + + AUDIO_LOG_INFO("Try to close PCM handle 0x%x", pcm_handle); +// pthread_mutex_lock(&am->device.pcm_lock); + if ((err = snd_pcm_close(pcm_handle)) < 0) { + AUDIO_LOG_ERROR("Error closing PCM handle : %s", snd_strerror(err)); + pthread_mutex_unlock(&am->device.pcm_lock); + return AUDIO_ERR_RESOURCE; + } + + am->device.pcm_count--; + AUDIO_LOG_INFO("PCM handle close success (count:%d)", am->device.pcm_count); +// pthread_mutex_unlock(&am->device.pcm_lock); + + return audio_ret; +} diff --git a/tizen-audio-internal.h b/tizen-audio-internal.h new file mode 100755 index 0000000..33e67e1 --- /dev/null +++ b/tizen-audio-internal.h @@ -0,0 +1,235 @@ +#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> +#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) + +/* Verbs */ +#define AUDIO_USE_CASE_VERB_INACTIVE "Inactive" +#define AUDIO_USE_CASE_VERB_HIFI "HiFi" +#define AUDIO_USE_CASE_VERB_VOICECALL "VoiceCall" +#define AUDIO_USE_CASE_VERB_LOOPBACK "Loopback" +#define AUDIO_USE_CASE_VERB_FMRADIO "FM_Radio" + +/* Devices : Normal */ +#define AUDIO_USE_CASE_DEV_SPEAKER "Speaker" +#define AUDIO_USE_CASE_DEV_HANDSET "Earpiece" +#define AUDIO_USE_CASE_DEV_HEADSET "Headphones" +#define AUDIO_USE_CASE_DEV_HEADSET_3POLE "Headphones" +#define AUDIO_USE_CASE_DEV_BT_HEADSET "Bluetooth" +#define AUDIO_USE_CASE_DEV_LINEOUT "Line" +#define AUDIO_USE_CASE_DEV_FMRADIO "FM" + +#define AUDIO_USE_CASE_DEV_MAIN_MIC "MainMic" +#define AUDIO_USE_CASE_DEV_SUB_MIC "SubMic" +#define AUDIO_USE_CASE_DEV_HEADSET_MIC "HeadsetMic" +#define AUDIO_USE_CASE_DEV_BT_MIC "BT Mic" + +/* Modifiers */ +#define AUDIO_USE_CASE_MODIFIER_VOICE "VoiceSearch" +#define AUDIO_USE_CASE_MODIFIER_CAMCORDING "Camcording" +#define AUDIO_USE_CASE_MODIFIER_RINGTONE "Ringtone" + + +#define streq !strcmp +#define strneq strcmp + +#define ALSA_DEFAULT_CARD "ymu831" + +#define MAX_DEVICES 5 +#define MAX_MODIFIERS 5 + +/* 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; + +/* Session */ +typedef struct audio_session_mgr { + audio_session_t session; + audio_subsession_t subsession; + uint32_t is_recording; + uint32_t is_radio_on; + uint32_t is_call_session; +} audio_session_mgr_t; + +/* Device */ + +typedef struct audio_device_mgr { + audio_device_in_t active_in; + audio_device_out_t active_out; + uint32_t route_flag; + snd_pcm_t *pcm_in; + snd_pcm_t *pcm_out; + pthread_mutex_t pcm_lock; + uint32_t pcm_count; +} audio_device_mgr_t; + + +/* Stream */ + +#define AUDIO_VOLUME_LEVEL_MAX 16 + +typedef struct audio_volume_gain_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_gain_table_t; + +enum { + AUDIO_VOLUME_DEVICE_SPEAKER, + AUDIO_VOLUME_DEVICE_RECEIVER, + AUDIO_VOLUME_DEVICE_EARJACK, + AUDIO_VOLUME_DEVICE_BT_SCO, + AUDIO_VOLUME_DEVICE_BT_A2DP, + AUDIO_VOLUME_DEVICE_DOCK, + AUDIO_VOLUME_DEVICE_HDMI, + AUDIO_VOLUME_DEVICE_MIRRORING, + AUDIO_VOLUME_DEVICE_USB, + AUDIO_VOLUME_DEVICE_MULTIMEDIA_DOCK, + AUDIO_VOLUME_DEVICE_MAX, +}; + +typedef struct audio_stream_mgr { + uint32_t volume_level[AUDIO_VOLUME_TYPE_MAX]; + audio_volume_gain_table_t *volume_gain_table; +} audio_stream_mgr_t; + +typedef struct audio_ucm_mgr { + snd_use_case_mgr_t* uc_mgr; +} audio_ucm_mgr_t; + +typedef struct audio_mixer_mgr { + 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_mixer_mgr_t; + +/* Overall */ + +typedef struct audio_mgr { + void *platform_data; + audio_cb_interface_t cb_intf; + audio_session_mgr_t session; + audio_device_mgr_t device; + audio_stream_mgr_t stream; + audio_ucm_mgr_t ucm; + audio_mixer_mgr_t mixer; +} audio_mgr_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_stream_init (audio_mgr_t *am); +audio_return_t _audio_stream_deinit (audio_mgr_t *am); + +audio_return_t _audio_device_init (audio_mgr_t *am); +audio_return_t _audio_device_deinit (audio_mgr_t * am); +audio_return_t _set_route_voicecall (audio_mgr_t *am, uint32_t device_in, uint32_t device_out, uint32_t route_flag); +audio_return_t _reset_route (audio_mgr_t *am, int need_inactive); + +audio_return_t _audio_session_init (audio_mgr_t *am); +audio_return_t _audio_session_deinit (audio_mgr_t *am); + +#define MIXER_MIC1_SWITCH "Mic1 Switch" +#define MIXER_MIC2_SWITCH "Mic2 Switch" + +audio_return_t _audio_ucm_init (audio_mgr_t *am); +audio_return_t _audio_ucm_deinit (audio_mgr_t *am); +void _audio_ucm_get_device_name (audio_mgr_t *am, 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_mgr_t *am, const char *verb, const char *devices[], const char *modifiers[]); +int _audio_ucm_fill_device_info_list (audio_mgr_t *am, audio_device_info_t *device_info_list, const char *verb); +audio_return_t _audio_ucm_get_verb (audio_mgr_t *am, const char **value); +audio_return_t _audio_ucm_reset_use_case (audio_mgr_t *am); + +audio_return_t _audio_util_init (audio_mgr_t *am); +audio_return_t _audio_util_deinit (audio_mgr_t *am); +audio_return_t _audio_mixer_control_set_param(audio_mgr_t *am, const char* ctl_name, snd_ctl_elem_value_t* value, int size); +audio_return_t _audio_mixer_control_set_value(audio_mgr_t *am, const char *ctl_name, int val); +audio_return_t _audio_mixer_control_set_value_string(audio_mgr_t *am, const char* ctl_name, const char* value); +audio_return_t _audio_mixer_control_get_value(audio_mgr_t *am, const char *ctl_name, int *val); +audio_return_t _audio_mixer_control_get_element(audio_mgr_t *am, 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, uint32_t start_threshold, uint32_t rate); +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); +#endif diff --git a/tizen-audio-session.c b/tizen-audio-session.c new file mode 100755 index 0000000..d323daa --- /dev/null +++ b/tizen-audio-session.c @@ -0,0 +1,162 @@ +/* + * 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 "tizen-audio-internal.h" + +static const char *__get_session_string_by_idx (uint32_t session_idx) +{ + switch (session_idx) { + case AUDIO_SESSION_MEDIA: return "media"; + case AUDIO_SESSION_VOICECALL: return "voicecall"; + case AUDIO_SESSION_VIDEOCALL: return "videocall"; + case AUDIO_SESSION_VOIP: return "voip"; + case AUDIO_SESSION_FMRADIO: return "fmradio"; + case AUDIO_SESSION_CAMCORDER: return "camcorder"; + case AUDIO_SESSION_NOTIFICATION: return "notification"; + case AUDIO_SESSION_ALARM: return "alarm"; + case AUDIO_SESSION_EMERGENCY: return "emergency"; + case AUDIO_SESSION_VOICE_RECOGNITION: return "voice_recognition"; + default: return "invalid"; + } +} + +static const char *__get_subsession_string_by_idx (uint32_t subsession_idx) +{ + switch (subsession_idx) { + case AUDIO_SUBSESSION_NONE: return "none"; + case AUDIO_SUBSESSION_VOICE: return "voice"; + case AUDIO_SUBSESSION_RINGTONE: return "ringtone"; + case AUDIO_SUBSESSION_MEDIA: return "media"; + case AUDIO_SUBSESSION_INIT: return "init"; + case AUDIO_SUBSESSION_VR_NORMAL: return "vr_normal"; + case AUDIO_SUBSESSION_VR_DRIVE: return "vr_drive"; + case AUDIO_SUBSESSION_STEREO_REC: return "stereo_rec"; + case AUDIO_SUBSESSION_MONO_REC: return "mono_rec"; + default: return "invalid"; + } +} + +static const char * __get_sessin_cmd_string (uint32_t cmd) +{ + switch (cmd) { + case AUDIO_SESSION_CMD_START: return "start"; + case AUDIO_SESSION_CMD_SUBSESSION: return "subsession"; + case AUDIO_SESSION_CMD_END: return "end"; + default: return "invalid"; + } +} + +audio_return_t _audio_session_init (audio_mgr_t *am) +{ + AUDIO_RETURN_VAL_IF_FAIL(am, AUDIO_ERR_PARAMETER); + + am->session.session = AUDIO_SESSION_MEDIA; + am->session.subsession = AUDIO_SUBSESSION_NONE; + am->session.is_recording = 0; + am->session.is_radio_on = 0; + am->session.is_call_session = 0; + + return AUDIO_RET_OK; +} + +audio_return_t _audio_session_deinit (audio_mgr_t *am) +{ + AUDIO_RETURN_VAL_IF_FAIL(am, AUDIO_ERR_PARAMETER); + + return AUDIO_RET_OK; +} + +audio_return_t audio_set_session (void *userdata, uint32_t session, uint32_t subsession, uint32_t cmd) +{ + audio_return_t audio_ret = AUDIO_RET_OK; + audio_mgr_t *am = (audio_mgr_t *)userdata; + uint32_t prev_subsession = am->session.subsession; + + AUDIO_RETURN_VAL_IF_FAIL(am, AUDIO_ERR_PARAMETER); + + AUDIO_LOG_INFO("session %s:%s(%s)->%s(%s)", __get_sessin_cmd_string(cmd), + __get_session_string_by_idx(am->session.session), __get_subsession_string_by_idx(am->session.subsession), + __get_session_string_by_idx(session), __get_subsession_string_by_idx(subsession)); + + if (cmd == AUDIO_SESSION_CMD_START) { + if (am->session.is_call_session) { + AUDIO_LOG_ERROR("call active its not possible to have any other session start now"); + return audio_ret; + } + am->session.session = session; + am->session.subsession = subsession; + + if ((session == AUDIO_SESSION_VIDEOCALL) || + (session == AUDIO_SESSION_VOICECALL) || + (session == AUDIO_SESSION_VOIP)) { + AUDIO_LOG_INFO("set call session"); + am->session.is_call_session = 1; + } + + } else if (cmd == AUDIO_SESSION_CMD_END) { + + if ((session == AUDIO_SESSION_VIDEOCALL) || + (session == AUDIO_SESSION_VOICECALL) || + (session == AUDIO_SESSION_VOIP)) { + AUDIO_LOG_INFO("unset call session"); + am->session.is_call_session = 0; + } + + if (am->session.is_call_session) { + AUDIO_LOG_ERROR("call active its not possible to have any other session end now"); + return audio_ret; + } + + if (session == AUDIO_SESSION_MEDIA && (prev_subsession == AUDIO_SUBSESSION_STEREO_REC || AUDIO_SUBSESSION_MONO_REC)) { + am->session.is_recording = 0; + } + am->session.session = AUDIO_SESSION_MEDIA; + am->session.subsession = AUDIO_SUBSESSION_NONE; + } else if (cmd == AUDIO_SESSION_CMD_SUBSESSION) { + + if (am->session.is_call_session) { + if ((subsession != AUDIO_SUBSESSION_VOICE) && + (subsession != AUDIO_SUBSESSION_MEDIA) && + (subsession != AUDIO_SUBSESSION_RINGTONE)) { + AUDIO_LOG_ERROR("call active we can only have one of AUDIO_SUBSESSION_VOICE AUDIO_SUBSESSION_MEDIA AUDIO_SUBSESSION_RINGTONE as a sub-session"); + return audio_ret; + } + } + am->session.subsession = subsession; + if (prev_subsession != subsession && subsession == AUDIO_SUBSESSION_VOICE) { + am->session.is_recording = 0; + } + + if (subsession == AUDIO_SUBSESSION_STEREO_REC || subsession == AUDIO_SUBSESSION_MONO_REC) { + am->session.is_recording = 1; + } else if (am->session.is_recording == 1 && subsession == AUDIO_SUBSESSION_INIT) { + am->session.is_recording = 0; + } + } + + return audio_ret; +} diff --git a/tizen-audio-stream.c b/tizen-audio-stream.c new file mode 100755 index 0000000..754f1c5 --- /dev/null +++ b/tizen-audio-stream.c @@ -0,0 +1,473 @@ +/* + * 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) + +enum { + STREAM_DEVICE_SPEAKER, + STREAM_DEVICE_HEADSET, + STREAM_DEVICE_BLUETOOTH, + STREAM_DEVICE_HDMI, + STREAM_DEVICE_DOCK, + STREAM_DEVICE_MAX, +}; + +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 */ + "file/private/sound/volume/fixed", /* AUDIO_VOLUME_TYPE_FIXED */ +}; + +static inline uint8_t __get_volume_dev_index(audio_mgr_t *am, uint32_t volume_type) +{ + + switch (am->device.active_out) { + case AUDIO_DEVICE_OUT_SPEAKER: return AUDIO_VOLUME_DEVICE_SPEAKER; + case AUDIO_DEVICE_OUT_RECEIVER: return AUDIO_VOLUME_DEVICE_RECEIVER; + case AUDIO_DEVICE_OUT_WIRED_ACCESSORY: return AUDIO_VOLUME_DEVICE_EARJACK; + case AUDIO_DEVICE_OUT_BT_SCO: return AUDIO_VOLUME_DEVICE_BT_SCO; + case AUDIO_DEVICE_OUT_BT_A2DP: return AUDIO_VOLUME_DEVICE_BT_A2DP; + case AUDIO_DEVICE_OUT_DOCK: return AUDIO_VOLUME_DEVICE_DOCK; + case AUDIO_DEVICE_OUT_HDMI: return AUDIO_VOLUME_DEVICE_HDMI; + case AUDIO_DEVICE_OUT_MIRRORING: return AUDIO_VOLUME_DEVICE_MIRRORING; + case AUDIO_DEVICE_OUT_USB_AUDIO: return AUDIO_VOLUME_DEVICE_USB; + case AUDIO_DEVICE_OUT_MULTIMEDIA_DOCK: return AUDIO_VOLUME_DEVICE_MULTIMEDIA_DOCK; + default: return AUDIO_VOLUME_DEVICE_SPEAKER; + } +} + +static const uint8_t __get_stream_dev_index (uint32_t device_out) +{ + switch (device_out) { + case AUDIO_DEVICE_OUT_SPEAKER: return STREAM_DEVICE_SPEAKER; + case AUDIO_DEVICE_OUT_RECEIVER: return STREAM_DEVICE_SPEAKER; + case AUDIO_DEVICE_OUT_WIRED_ACCESSORY: return STREAM_DEVICE_HEADSET; + case AUDIO_DEVICE_OUT_BT_SCO: return STREAM_DEVICE_BLUETOOTH; + case AUDIO_DEVICE_OUT_BT_A2DP: return STREAM_DEVICE_BLUETOOTH; + case AUDIO_DEVICE_OUT_DOCK: return STREAM_DEVICE_DOCK; + case AUDIO_DEVICE_OUT_HDMI: return STREAM_DEVICE_HDMI; + case AUDIO_DEVICE_OUT_MIRRORING: return STREAM_DEVICE_SPEAKER; + case AUDIO_DEVICE_OUT_USB_AUDIO: return STREAM_DEVICE_SPEAKER; + case AUDIO_DEVICE_OUT_MULTIMEDIA_DOCK: return STREAM_DEVICE_DOCK; + default: + AUDIO_LOG_DEBUG("invalid device_out:%d", device_out); + break; + } + + return STREAM_DEVICE_SPEAKER; +} + +static const char *__get_device_string_by_idx (uint32_t dev_idx) +{ + switch (dev_idx) { + case STREAM_DEVICE_SPEAKER: return "speaker"; + case STREAM_DEVICE_HEADSET: return "headset"; + case STREAM_DEVICE_BLUETOOTH: return "btheadset"; + case STREAM_DEVICE_HDMI: return "hdmi"; + case STREAM_DEVICE_DOCK: return "dock"; + default: return "invalid"; + } +} + +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"; + case AUDIO_VOLUME_TYPE_FIXED: return "fixed"; + default: return "invalid"; + } +} + +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_info(char *dump, audio_info_t *info) +{ + int len; + char name[64] = { '\0', }; + + if (info->device.api == AUDIO_DEVICE_API_ALSA) { + len = snprintf(dump, AUDIO_DUMP_STR_LEN, "device:alsa(%d.%d)", info->device.alsa.card_idx, info->device.alsa.device_idx); + } else if (info->device.api == AUDIO_DEVICE_API_ALSA) { + len = snprintf(dump, AUDIO_DUMP_STR_LEN, "device:bluez(%s,nrec:%d)", info->device.bluez.protocol, info->device.bluez.nrec); + } else { + len = snprintf(dump, AUDIO_DUMP_STR_LEN, "device:unknown"); + } + + if (len > 0) + dump += len; + + strncpy(name, info->stream.name ? info->stream.name : "null", sizeof(name)-1); + len = snprintf(dump, AUDIO_DUMP_STR_LEN, "stream:%s(%dhz,%dch,vol:%s,gain:%s)", + name, info->stream.samplerate, info->stream.channels, + __get_volume_type_string_by_idx(info->stream.volume_type), __get_gain_type_string_by_idx(info->stream.gain_type)); + + if (len > 0) + dump += len; + + *dump = '\0'; +} + +static void __dump_tb (audio_mgr_t *am) +{ + audio_volume_gain_table_t *volume_gain_table = am->stream.volume_gain_table; + uint32_t dev_idx, 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 >>>>>"); + + + 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_gain_table->volume_level_max[vol_type_idx]; vol_level_idx++) { + snprintf(dump_str_ptr, 6, "%01.2f ", volume_gain_table->volume[vol_type_idx][vol_level_idx]); + dump_str_ptr += strlen(dump_str_ptr); + } + AUDIO_LOG_INFO("%s", dump_str); + } + + + /* 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)); + + 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_gain_table->gain[gain_type_idx]); + dump_str_ptr += strlen(dump_str_ptr); + } + AUDIO_LOG_INFO("%s", dump_str); + +} + +static audio_return_t __load_volume_gain_table_from_ini (audio_mgr_t *am) +{ + dictionary * dict = NULL; + uint32_t vol_type_idx, vol_level_idx, gain_type_idx; + audio_volume_gain_table_t *volume_gain_table = am->stream.volume_gain_table; + int size = 0; + const char delimiter[] = ", "; + char *key, *list_str, *token, *ptr = NULL; + const char *section = "volumes"; + + 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; + } + } + + /* 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_gain_table->volume_level_max[vol_type_idx] = 0; + size = strlen(section) + strlen(vol_type_str) + 2; + key = malloc(size); + if (key) { + snprintf(key, size, "%s:%s", section, vol_type_str); + list_str = iniparser_getstr(dict, key); + 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_gain_table->volume[vol_type_idx][volume_gain_table->volume_level_max[vol_type_idx]++] = vol_value; + token = strtok_r(NULL, delimiter, &ptr); + } + } else { + volume_gain_table->volume_level_max[vol_type_idx] = 1; + for (vol_level_idx = 0; vol_level_idx < AUDIO_VOLUME_LEVEL_MAX; vol_level_idx++) { + volume_gain_table->volume[vol_type_idx][vol_level_idx] = VOLUME_VALUE_MAX; + } + } + free(key); + } + } + + /* Load gain table */ + volume_gain_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(section) + strlen("gain") + strlen(gain_type_str) + 3; + key = malloc(size); + if (key) { + snprintf(key, size, "%s:gain_%s", section, gain_type_str); + token = iniparser_getstr(dict, key); + if (token) { + volume_gain_table->gain[gain_type_idx] = atof(token); + } else { + volume_gain_table->gain[gain_type_idx] = GAIN_VALUE_MAX; + } + free(key); + } else { + volume_gain_table->gain[gain_type_idx] = GAIN_VALUE_MAX; + } + } + + iniparser_freedict(dict); + + __dump_tb(am); + + return AUDIO_RET_OK; +} + +audio_return_t _audio_stream_init (audio_mgr_t *am) +{ + int i, j, val; + audio_return_t audio_ret = AUDIO_RET_OK; + int init_value[AUDIO_VOLUME_TYPE_MAX] = { 9, 11, 7, 11, 7, 4, 4, 7, 4, 0 }; + + AUDIO_RETURN_VAL_IF_FAIL(am, AUDIO_ERR_PARAMETER); + + for (i = 0; i < AUDIO_VOLUME_TYPE_MAX; i++) { + am->stream.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); + + am->stream.volume_level[i] = val; + } + + if (!(am->stream.volume_gain_table = malloc(sizeof(audio_volume_gain_table_t)))) { + AUDIO_LOG_ERROR("volume_gain_table malloc failed"); + return AUDIO_ERR_RESOURCE; + } + + audio_ret = __load_volume_gain_table_from_ini(am); + if(audio_ret != AUDIO_RET_OK) { + AUDIO_LOG_ERROR("gain table load error"); + return AUDIO_ERR_UNDEFINED; + } + + return audio_ret; +} + +audio_return_t _audio_stream_deinit (audio_mgr_t *am) +{ + AUDIO_RETURN_VAL_IF_FAIL(am, AUDIO_ERR_PARAMETER); + + if (am->stream.volume_gain_table) { + free(am->stream.volume_gain_table); + am->stream.volume_gain_table = NULL; + } + + return AUDIO_RET_OK; +} + +audio_return_t audio_get_volume_level_max (void *userdata, uint32_t volume_type, uint32_t *level) +{ + audio_mgr_t *am = (audio_mgr_t *)userdata; + audio_volume_gain_table_t *volume_gain_table; + + AUDIO_RETURN_VAL_IF_FAIL(am, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(am->stream.volume_gain_table, AUDIO_ERR_PARAMETER); + + /* Get max volume level by device & type */ + volume_gain_table = am->stream.volume_gain_table; + *level = volume_gain_table->volume_level_max[volume_type]; + + AUDIO_LOG_DEBUG("get_volume_level_max:%s=>%d", __get_volume_type_string_by_idx(volume_type), *level); + + return AUDIO_RET_OK; +} + +audio_return_t audio_get_volume_level (void *userdata, uint32_t volume_type, uint32_t *level) +{ + audio_mgr_t *am = (audio_mgr_t *)userdata; + + AUDIO_RETURN_VAL_IF_FAIL(am, AUDIO_ERR_PARAMETER); + + *level = am->stream.volume_level[volume_type]; + + return AUDIO_RET_OK; +} + +audio_return_t audio_get_volume_value (void *userdata, audio_info_t *info, uint32_t volume_type, uint32_t level, double *value) +{ + if (info) { + audio_mgr_t *am = (audio_mgr_t *)userdata; + audio_volume_gain_table_t *volume_gain_table; + char dump_str[AUDIO_DUMP_STR_LEN]; + + AUDIO_RETURN_VAL_IF_FAIL(am, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(am->stream.volume_gain_table, AUDIO_ERR_PARAMETER); + __dump_info(&dump_str[0], info); + /* Get basic volume by device & type & level */ + volume_gain_table = am->stream.volume_gain_table; + if (volume_gain_table->volume_level_max[volume_type] < level) + *value = VOLUME_VALUE_MAX; + else + *value = volume_gain_table->volume[volume_type][level]; + *value *= volume_gain_table->gain[info->stream.gain_type]; + + AUDIO_LOG_DEBUG("get_volume_value:%d(%s)=>%f %s", level, __get_volume_type_string_by_idx(volume_type), *value, &dump_str[0]); + } + + return AUDIO_RET_OK; +} + +audio_return_t audio_set_volume_level (void *userdata, audio_info_t *info, uint32_t volume_type, uint32_t level) +{ + audio_return_t audio_ret = AUDIO_RET_OK; + audio_mgr_t *am = (audio_mgr_t *)userdata; + + AUDIO_RETURN_VAL_IF_FAIL(am, AUDIO_ERR_PARAMETER); + + if (info == NULL) { + + /* Update volume level */ + am->stream.volume_level[volume_type] = level; + AUDIO_LOG_INFO("set_volume_level:session(%d), %d(%s)", am->session.session, level, __get_volume_type_string_by_idx(volume_type)); + } + + return audio_ret; +} + +audio_return_t audio_get_gain_value (void *userdata, audio_info_t *info, uint32_t volume_type, double *value) +{ + audio_mgr_t *am = (audio_mgr_t *)userdata; + audio_volume_gain_table_t *volume_gain_table; + + AUDIO_RETURN_VAL_IF_FAIL(am, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(am->stream.volume_gain_table, AUDIO_ERR_PARAMETER); + + if (info != NULL) { + volume_gain_table = am->stream.volume_gain_table; + *value = volume_gain_table->gain[info->stream.gain_type]; + } + + return AUDIO_RET_OK; +} + +audio_return_t audio_get_mute (void *userdata, audio_info_t *info, uint32_t volume_type, uint32_t direction, uint32_t *mute) +{ + audio_return_t audio_ret = AUDIO_RET_OK; + audio_mgr_t *am = (audio_mgr_t *)userdata; + + AUDIO_RETURN_VAL_IF_FAIL(am, AUDIO_ERR_PARAMETER); + + /* TODO. Not implemented */ + + return audio_ret; +} + +audio_return_t audio_set_mute (void *userdata, audio_info_t *info, uint32_t volume_type, uint32_t direction, uint32_t mute) +{ + audio_return_t audio_ret = AUDIO_RET_OK; + audio_mgr_t *am = (audio_mgr_t *)userdata; + + AUDIO_RETURN_VAL_IF_FAIL(am, AUDIO_ERR_PARAMETER); + /* TODO. Not implemented */ + + return audio_ret; +} diff --git a/tizen-audio-ucm.c b/tizen-audio-ucm.c new file mode 100755 index 0000000..0d4f437 --- /dev/null +++ b/tizen-audio-ucm.c @@ -0,0 +1,447 @@ +/* + * 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 + +audio_return_t _audio_ucm_init (audio_mgr_t *am) +{ + snd_use_case_mgr_open(&am->ucm.uc_mgr, ALSA_DEFAULT_CARD); + + if (!am->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_mgr_t *am) +{ + if (am->ucm.uc_mgr != NULL) { + snd_use_case_mgr_close(am->ucm.uc_mgr); + am->ucm.uc_mgr = NULL; + } + + return AUDIO_RET_OK; +} + +void _audio_ucm_get_device_name (audio_mgr_t *am, const char *use_case, audio_direction_t direction, const char **value) +{ + char identifier[70] = {0}; + + if (direction == AUDIO_DIRECTION_IN) { + sprintf(identifier, "CapturePCM//%s", use_case); + } else { + sprintf(identifier, "PlaybackPCM//%s", use_case); + } + snd_use_case_get(am->ucm.uc_mgr, identifier, value); +} + +static inline void __add_ucm_device_info (audio_mgr_t *am, 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_ucm_get_device_name(am, 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_mgr_t *am, audio_device_info_t *device_info_list, const char *verb) +{ + int device_info_count = 0; + const char *curr_verb = NULL; + + if (!verb) { + snd_use_case_get(am->ucm.uc_mgr, "_verb", &curr_verb); + verb = curr_verb; + } + + /* prepare destination */ + /*If the devices are VOICECALL LOOPBACK or FMRADIO then pulseaudio need not get the device notification*/ + if (verb) { + if (strncmp(verb, AUDIO_USE_CASE_VERB_VOICECALL, strlen(AUDIO_USE_CASE_VERB_VOICECALL)) && + strncmp(verb, AUDIO_USE_CASE_VERB_LOOPBACK, strlen(AUDIO_USE_CASE_VERB_LOOPBACK))) { + __add_ucm_device_info(am, verb, AUDIO_DIRECTION_IN, device_info_list, &device_info_count); + if(strncmp(verb, AUDIO_USE_CASE_VERB_FMRADIO, strlen(AUDIO_USE_CASE_VERB_FMRADIO))) { + __add_ucm_device_info(am, verb, AUDIO_DIRECTION_OUT, device_info_list, &device_info_count); + } + } + + if (curr_verb) + free((void *)curr_verb); + + } + + return device_info_count; +} + +static void __dump_use_case(const char *verb, const char *devices[], int dev_count, const char *modifiers[], int mod_count, char *dump) +{ + int i, len; + + len = sprintf(dump, "Verb [ %s ] Devices [ ", verb ? verb : AUDIO_USE_CASE_VERB_INACTIVE); + if (len > 0) + dump += len; + + for (i = 0; i < dev_count; i++) { + if (i != dev_count - 1) { + len = sprintf(dump, "%s, ", devices[i]); + } else { + len = sprintf(dump, "%s", devices[i]); + } + if (len > 0) + dump += len; + } + + len = sprintf(dump, " ] Modifier [ "); + if (len > 0) + dump += len; + + for (i = 0; i < mod_count; i++) { + if (i != mod_count - 1) { + len = sprintf(dump, "%s, ", modifiers[i]); + } else { + len = sprintf(dump, "%s", modifiers[i]); + } + if (len > 0) + dump += len; + } + + len = sprintf(dump, " ]"); + if (len > 0) + dump += len; + + *dump = '\0'; +} + +#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_mgr_t *am, 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; + char dump_str[512]; + + if (!am->ucm.uc_mgr || !verb) + return AUDIO_ERR_PARAMETER; + + snd_use_case_get(am->ucm.uc_mgr, "_verb", &old_verb); + old_dev_count = snd_use_case_get_list(am->ucm.uc_mgr, "_enadevs", &old_dev_list); + old_mod_count = snd_use_case_get_list(am->ucm.uc_mgr, "_enamods", &old_mod_list); + __dump_use_case(old_verb, old_dev_list, old_dev_count, old_mod_list, old_mod_count, &dump_str[0]); + AUDIO_LOG_INFO(">>> UCM current %s", dump_str); + + 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(verb, devices, dev_count, modifiers, mod_count, &dump_str[0]); + AUDIO_LOG_INFO("> UCM requested %s", dump_str); + + 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(am->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(am->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(am->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(am->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(am->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(am->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(am->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(am->ucm.uc_mgr, "_verb", &new_verb); + new_dev_count = snd_use_case_get_list(am->ucm.uc_mgr, "_enadevs", &new_dev_list); + new_mod_count = snd_use_case_get_list(am->ucm.uc_mgr, "_enamods", &new_mod_list); + __dump_use_case(new_verb, new_dev_list, new_dev_count, new_mod_list, new_mod_count, &dump_str[0]); + AUDIO_LOG_INFO("<<< UCM changed %s", dump_str); + + 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_get_verb (audio_mgr_t *am, const char **value) +{ + audio_return_t ret = AUDIO_RET_OK; + + AUDIO_RETURN_VAL_IF_FAIL(am, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(value, AUDIO_ERR_PARAMETER); + + if ((ret = snd_use_case_get(am->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_mgr_t *am) +{ + audio_return_t ret = AUDIO_RET_OK; + + AUDIO_RETURN_VAL_IF_FAIL(am, AUDIO_ERR_PARAMETER); + + AUDIO_LOG_INFO(">>> UCM reset Verb [ %s ]", AUDIO_USE_CASE_VERB_INACTIVE); + + if ((ret = snd_use_case_set(am->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 100755 index 0000000..d459f36 --- /dev/null +++ b/tizen-audio-util.c @@ -0,0 +1,357 @@ +/* + * 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_mgr_t *am) +{ + pthread_mutex_init(&(am->mixer.mutex), NULL); + return AUDIO_RET_OK; + } + +audio_return_t _audio_util_deinit (audio_mgr_t *am) +{ + pthread_mutex_destroy(&(am->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_mgr_t *am, const char* ctl_name, snd_ctl_elem_value_t* param, int size) +{ + /* TODO. */ + return AUDIO_RET_OK; +} + +audio_return_t audio_mixer_control_get_value (void *userdata, const char *ctl_name, int *val) +{ + audio_return_t audio_ret = AUDIO_RET_OK; + audio_mgr_t *am = (audio_mgr_t *)userdata; + audio_ret = _audio_mixer_control_get_value(am, ctl_name, val); + return audio_ret; +} + +audio_return_t _audio_mixer_control_get_value(audio_mgr_t *am, 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; + + pthread_mutex_lock(&(am->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(&(am->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(&(am->mixer.mutex)); + return AUDIO_RET_USE_HW_CONTROL; + +close: + AUDIO_LOG_ERROR ("Error\n"); + snd_ctl_close(handle); + pthread_mutex_unlock(&(am->mixer.mutex)); + return AUDIO_ERR_UNDEFINED; +} + +audio_return_t _audio_mixer_control_set_value(audio_mgr_t *am, 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; + + pthread_mutex_lock(&(am->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(&(am->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(&(am->mixer.mutex)); + return AUDIO_RET_USE_HW_CONTROL; + +close: + AUDIO_LOG_ERROR("Error"); + snd_ctl_close(handle); + pthread_mutex_unlock(&(am->mixer.mutex)); + return -1; +} + +audio_return_t _audio_mixer_control_set_value_string(audio_mgr_t *am, const char* ctl_name, const char* value) +{ + /* TODO. */ + return AUDIO_RET_OK; +} + + +audio_return_t _audio_mixer_control_get_element(audio_mgr_t *am, const char *ctl_name, snd_hctl_elem_t **elem) +{ + /* TODO. */ + return AUDIO_RET_OK; +} + + +/* 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; + + 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_ERROR("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, uint32_t start_threshold, uint32_t rate) +{ + return AUDIO_ERR_NOT_IMPLEMENTED; +} diff --git a/tizen-audio.c b/tizen-audio.c new file mode 100755 index 0000000..db1638a --- /dev/null +++ b/tizen-audio.c @@ -0,0 +1,125 @@ +/* + * 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" + +int audio_get_revision (void) +{ + return AUDIO_REVISION; +} + +audio_return_t audio_init (void **userdata, void *platform_data) +{ + audio_mgr_t *am; + audio_return_t ret = AUDIO_RET_OK; + + if (!(am = malloc(sizeof(audio_mgr_t)))) { + AUDIO_LOG_ERROR("am malloc failed"); + return AUDIO_ERR_RESOURCE; + } + am->platform_data = platform_data; + memset(&am->cb_intf, 0, sizeof(audio_cb_interface_t)); + if (AUDIO_IS_ERROR((ret = _audio_session_init(am)))) { + AUDIO_LOG_ERROR("session init failed"); + goto error_exit; + } + if (AUDIO_IS_ERROR((ret = _audio_device_init(am)))) { + AUDIO_LOG_ERROR("device init failed"); + goto error_exit; + } + if (AUDIO_IS_ERROR((ret = _audio_stream_init(am)))) { + AUDIO_LOG_ERROR("stream init failed"); + goto error_exit; + } + if (AUDIO_IS_ERROR((ret = _audio_ucm_init(am)))) { + AUDIO_LOG_ERROR("ucm init failed"); + goto error_exit; + } + if (AUDIO_IS_ERROR((ret = _audio_util_init(am)))) { + AUDIO_LOG_ERROR("mixer init failed"); + goto error_exit; + } + + *userdata = (void *)am; + return AUDIO_RET_OK; + +error_exit: + if (am) + free(am); + + return ret; +} + +audio_return_t audio_deinit (void **userdata) +{ + audio_mgr_t *am = (audio_mgr_t *)*userdata; + + if (am) { + _audio_session_deinit(am); + _audio_device_deinit(am); + _audio_stream_deinit(am); + _audio_ucm_deinit(am); + _audio_util_deinit(am); + free(am); + *userdata = NULL; + } + + return AUDIO_RET_OK; +} + +/* this function is only called from audio tuning app for updating volume */ +audio_return_t audio_reset (void **userdata) +{ + audio_mgr_t *am = (audio_mgr_t *)*userdata; + audio_return_t ret = AUDIO_RET_OK; + + if (am) { + _audio_stream_deinit(am); + + if (AUDIO_IS_ERROR((ret = _audio_stream_init(am)))) { + AUDIO_LOG_ERROR("stream init failed"); + goto error_exit; + } + } + + return AUDIO_RET_OK; + +error_exit: + if (am) + free(am); + *userdata = NULL; + + return ret; +} + +audio_return_t audio_set_callback (void *userdata, audio_cb_interface_t *cb_interface) +{ + audio_mgr_t *am = (audio_mgr_t *)userdata; + + if (am) { + memcpy(&am->cb_intf, cb_interface, sizeof(audio_cb_interface_t)); + return AUDIO_RET_OK; + } else { + return AUDIO_ERR_PARAMETER; + } +} diff --git a/tizen-audio.h b/tizen-audio.h new file mode 100755 index 0000000..e29a08c --- /dev/null +++ b/tizen-audio.h @@ -0,0 +1,274 @@ +#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 <stdio.h> +#include <stdlib.h> +#include <stdint.h> + +#define AUDIO_REVISION 1 + +/* Error code */ + +#define AUDIO_IS_ERROR(ret) (ret < 0) + +typedef enum audio_return { + AUDIO_RET_OK = 0, + AUDIO_RET_USE_HW_CONTROL = (int32_t)0x40001000, + 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 ; + + +/* Session */ +typedef enum audio_session { + AUDIO_SESSION_MEDIA, + AUDIO_SESSION_VOICECALL, + AUDIO_SESSION_VIDEOCALL, + AUDIO_SESSION_VOIP, + AUDIO_SESSION_FMRADIO, + AUDIO_SESSION_CAMCORDER, + AUDIO_SESSION_NOTIFICATION, + AUDIO_SESSION_ALARM, + AUDIO_SESSION_EMERGENCY, + AUDIO_SESSION_VOICE_RECOGNITION, + AUDIO_SESSION_MAX +} audio_session_t; + +/* Sub session */ +typedef enum audio_subsession { + AUDIO_SUBSESSION_NONE, + AUDIO_SUBSESSION_VOICE, + AUDIO_SUBSESSION_RINGTONE, + AUDIO_SUBSESSION_MEDIA, + AUDIO_SUBSESSION_INIT, + AUDIO_SUBSESSION_VR_NORMAL, + AUDIO_SUBSESSION_VR_DRIVE, + AUDIO_SUBSESSION_STEREO_REC, + AUDIO_SUBSESSION_MONO_REC, + AUDIO_SUBSESSION_MAX +} audio_subsession_t; + +/* Session command */ +typedef enum audio_session_command { + AUDIO_SESSION_CMD_START, + AUDIO_SESSION_CMD_SUBSESSION, + AUDIO_SESSION_CMD_END, +} audio_session_command_t; + + +/* Direction */ +typedef enum audio_direction { + AUDIO_DIRECTION_NONE, + AUDIO_DIRECTION_IN, /**< Capture */ + AUDIO_DIRECTION_OUT, /**< Playback */ +} audio_direction_t; + + +/* Device */ + +typedef enum audio_device_in { + AUDIO_DEVICE_IN_NONE, + AUDIO_DEVICE_IN_MIC, /**< Device builtin mic. */ + AUDIO_DEVICE_IN_WIRED_ACCESSORY, /**< Wired input devices */ + AUDIO_DEVICE_IN_BT_SCO, /**< Bluetooth SCO device */ + AUDIO_DEVICE_IN_MAX, +} audio_device_in_t; + +typedef enum audio_device_out { + AUDIO_DEVICE_OUT_NONE, + AUDIO_DEVICE_OUT_SPEAKER, /**< Device builtin speaker */ + AUDIO_DEVICE_OUT_RECEIVER, /**< Device builtin receiver */ + AUDIO_DEVICE_OUT_WIRED_ACCESSORY, /**< Wired output devices such as headphone, headset, and so on. */ + AUDIO_DEVICE_OUT_BT_SCO, /**< Bluetooth SCO device */ + AUDIO_DEVICE_OUT_BT_A2DP, /**< Bluetooth A2DP device */ + AUDIO_DEVICE_OUT_DOCK, /**< DOCK device */ + AUDIO_DEVICE_OUT_HDMI, /**< HDMI device */ + AUDIO_DEVICE_OUT_MIRRORING, /**< MIRRORING device */ + AUDIO_DEVICE_OUT_USB_AUDIO, /**< USB Audio device */ + AUDIO_DEVICE_OUT_MULTIMEDIA_DOCK, /**< Multimedia DOCK device */ + AUDIO_DEVICE_OUT_MAX, +} audio_device_out_t; + +typedef enum audio_route_flag { + AUDIO_ROUTE_FLAG_NONE = 0, + AUDIO_ROUTE_FLAG_MUTE_POLICY = 0x00000001, + AUDIO_ROUTE_FLAG_DUAL_OUT = 0x00000002, + AUDIO_ROUTE_FLAG_NOISE_REDUCTION = 0x00000010, + AUDIO_ROUTE_FLAG_EXTRA_VOL = 0x00000020, + AUDIO_ROUTE_FLAG_NETWORK_WB = 0x00000040, + AUDIO_ROUTE_FLAG_BT_WB = 0x00000100, + AUDIO_ROUTE_FLAG_BT_NREC = 0x00000200, + AUDIO_ROUTE_FLAG_VOICE_COMMAND = 0x00040000, +} audio_route_flag_t; + +typedef enum audio_device_api { + AUDIO_DEVICE_API_UNKNOWN, + AUDIO_DEVICE_API_ALSA, + AUDIO_DEVICE_API_BLUEZ, +} audio_device_api_t; + +typedef enum audio_device_param { + AUDIO_DEVICE_PARAM_NONE, + AUDIO_DEVICE_PARAM_CHANNELS, + AUDIO_DEVICE_PARAM_SAMPLERATE, + AUDIO_DEVICE_PARAM_FRAGMENT_SIZE, + AUDIO_DEVICE_PARAM_FRAGMENT_NB, + AUDIO_DEVICE_PARAM_START_THRESHOLD, + AUDIO_DEVICE_PARAM_USE_MMAP, + AUDIO_DEVICE_PARAM_USE_TSCHED, + AUDIO_DEVICE_PARAM_TSCHED_BUF_SIZE, + AUDIO_DEVICE_PARAM_SUSPEND_TIMEOUT, + AUDIO_DEVICE_PARAM_ALTERNATE_RATE, + AUDIO_DEVICE_PARAM_MAX, +} audio_device_param_t; + +typedef struct audio_device_param_info { + audio_device_param_t param; + union { + int64_t s64_v; + uint64_t u64_v; + int32_t s32_v; + uint32_t u32_v; + }; +} audio_device_param_info_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; + + +/* Stream */ + +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_FIXED, /**< Volume type for fixed acoustic level */ + 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_stream_info { + char *name; + uint32_t samplerate; + uint8_t channels; + uint32_t volume_type; + uint32_t gain_type; +} audio_stream_info_t ; + + +/* Overall */ + +typedef struct audio_info { + audio_device_info_t device; + audio_stream_info_t stream; +} audio_info_t; + +typedef struct audio_cb_interface { + audio_return_t (*load_device)(void *platform_data, audio_device_info_t *device_info, audio_device_param_info_t *params); + audio_return_t (*open_device)(void *platform_data, audio_device_info_t *device_info, audio_device_param_info_t *params); + audio_return_t (*close_all_devices)(void *platform_data); + audio_return_t (*close_device)(void *platform_data, audio_device_info_t *device_info); + audio_return_t (*unload_device)(void *platform_data, audio_device_info_t *device_info); +} audio_cb_interface_t; + +typedef struct audio_interface { + audio_return_t (*init)(void **userdata, void *platform_data); + audio_return_t (*deinit)(void **userdata); + audio_return_t (*reset)(void **userdata); + audio_return_t (*set_callback)(void *userdata, audio_cb_interface_t *cb_interface); + audio_return_t (*get_volume_level_max)(void *userdata, uint32_t volume_type, uint32_t *level); + audio_return_t (*get_volume_level)(void *userdata, uint32_t volume_type, uint32_t *level); + audio_return_t (*get_volume_value)(void *userdata, audio_info_t *info, uint32_t volume_type, uint32_t level, double *value); + audio_return_t (*set_volume_level)(void *userdata, audio_info_t *info, uint32_t volume_type, uint32_t level); + audio_return_t (*set_volume_value)(void *userdata, audio_info_t *info, uint32_t volume_type, double* value); + audio_return_t (*get_gain_value)(void *userdata, audio_info_t *info, uint32_t volume_type, double *value); + audio_return_t (*get_mute)(void *userdata, audio_info_t *info, uint32_t volume_type, uint32_t direction, uint32_t *mute); + audio_return_t (*set_mute)(void *userdata, audio_info_t *info, uint32_t volume_type, uint32_t direction, uint32_t mute); + audio_return_t (*set_session)(void *userdata, uint32_t session, uint32_t subsession, uint32_t cmd); + audio_return_t (*set_route)(void *userdata, uint32_t session, uint32_t subsession, uint32_t device_in, uint32_t device_out, uint32_t route_flag); + audio_return_t (*alsa_pcm_open)(void *userdata, void **pcm_handle, char *device_name, uint32_t direction, int mode); + audio_return_t (*alsa_pcm_close)(void *userdata, void *pcm_handle); + audio_return_t (*set_mixer_value_string)(void *userdata, const char* ctl, const char* value); + +} audio_interface_t; + +int audio_get_revision (void); +audio_return_t audio_init (void **userdata, void *platform_data); +audio_return_t audio_deinit (void **userdata); +audio_return_t audio_reset (void **userdata); +audio_return_t audio_set_callback (void *userdata, audio_cb_interface_t *cb_interface); +audio_return_t audio_get_volume_level_max (void *userdata, uint32_t volume_type, uint32_t *level); +audio_return_t audio_get_volume_level (void *userdata, uint32_t volume_type, uint32_t *level); +audio_return_t audio_get_volume_value (void *userdata, audio_info_t *info, uint32_t volume_type, uint32_t level, double *value); +audio_return_t audio_set_volume_level (void *userdata, audio_info_t *info, uint32_t volume_type, uint32_t level); +audio_return_t audio_set_volume_value (void *userdata, audio_info_t *info, uint32_t volume_type, double* value); +audio_return_t audio_get_gain_value (void *userdata, audio_info_t *info, uint32_t volume_type, double *value); +audio_return_t audio_get_mute (void *userdata, audio_info_t *info, uint32_t volume_type, uint32_t direction, uint32_t *mute); +audio_return_t audio_set_mute (void *userdata, audio_info_t *info, uint32_t volume_type, uint32_t direction, uint32_t mute); +audio_return_t audio_set_session (void *userdata, uint32_t session, uint32_t subsession, uint32_t cmd); +audio_return_t audio_alsa_pcm_open (void *userdata, void **pcm_handle, char *device_name, uint32_t direction, int mode); +audio_return_t audio_alsa_pcm_close (void *userdata, void *pcm_handle); +audio_return_t audio_set_route (void *userdata, uint32_t session, uint32_t subsession, uint32_t device_in, uint32_t device_out, uint32_t route_flag); +audio_return_t audio_set_mixer_value_string(void *userdata, const char* ctl, const char* value); +audio_return_t audio_mixer_control_get_value (void *userdata, const char *ctl_name, int *val); +#endif |