diff options
Diffstat (limited to 'mm_sound_pcm.c')
-rw-r--r-- | mm_sound_pcm.c | 1249 |
1 files changed, 1249 insertions, 0 deletions
diff --git a/mm_sound_pcm.c b/mm_sound_pcm.c new file mode 100644 index 0000000..8e11481 --- /dev/null +++ b/mm_sound_pcm.c @@ -0,0 +1,1249 @@ +/* + * libmm-sound + * + * Copyright (c) 2000 - 2013 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: Seungbae Shin <seungbae.shin@samsung.com> + * + * 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 <stdlib.h> +#include <memory.h> +#include <unistd.h> +#include <pthread.h> +#include <stdio.h> +#include <sys/types.h> +#include <fcntl.h> +#include <vconf.h> + +#include <sys/stat.h> +#include <errno.h> + +#include <mm_types.h> +#include <mm_error.h> +#include <mm_message.h> +#include <mm_debug.h> +#include "include/mm_sound_private.h" +#include "include/mm_sound.h" +#include "include/mm_sound_utils.h" +#include "include/mm_sound_common.h" +#include "include/mm_sound_pa_client.h" + +#include <audio-session-manager.h> +#include <mm_session.h> +#include <mm_session_private.h> + + +#define _MIN_SYSTEM_SAMPLERATE 8000 +#define _MAX_SYSTEM_SAMPLERATE 48000 +#define RW_LOG_PERIOD 5 /* period(second) for print log in capture read or play write*/ + +#define PCM_LOCK_INTERNAL(LOCK) do { pthread_mutex_lock(LOCK); } while (0) +#define PCM_UNLOCK_INTERNAL(LOCK) do { pthread_mutex_unlock(LOCK); } while (0) +#define PCM_LOCK_DESTROY_INTERNAL(LOCK) do { pthread_mutex_destroy(LOCK); } while (0) + +int g_capture_h_count = 0; +#define PCM_CAPTURE_H_COUNT_INC() do { g_capture_h_count++; } while (0) +#define PCM_CAPTURE_H_COUNT_DEC() do { g_capture_h_count--; if(g_capture_h_count < 0) debug_error ("g_capture_h_count[%d] is not valid, check application side for proper handle usage\n", g_capture_h_count); } while (0) +#define PCM_CAPTURE_H_COUNT_GET(x_val) do { x_val = g_capture_h_count; } while (0) + +typedef enum { + MMSOUND_SESSION_TYPE_PLAYBACK, + MMSOUND_SESSION_TYPE_CAPTURE, +}MMSound_session_type_e; + +enum { + MMSOUND_SESSION_REGISTERED_INTERNALLY, + MMSOUND_SESSION_REGISTERED_BY_OUTSIDE_MEDIA, +}; + +typedef struct { + int handle; + int asm_handle; + ASM_sound_events_t asm_event; + int asm_options; + int session_registered_type; + + bool is_started; + bool is_playback; + bool skip_session; + ASM_resource_t resource; + pthread_mutex_t pcm_mutex_internal; + MMMessageCallback msg_cb; + void *msg_cb_param; + + unsigned int rate; + MMSoundPcmChannel_t channel; + MMSoundPcmFormat_t format; + unsigned int byte_per_sec; + + int volume_config; + +} mm_sound_pcm_t; + +static int _pcm_sound_start (MMSoundPcmHandle_t handle); +static int _pcm_sound_stop_internal (MMSoundPcmHandle_t handle); +static int _pcm_sound_stop(MMSoundPcmHandle_t handle); +static void __sound_pcm_send_message (mm_sound_pcm_t *pcmHandle, int message, int code); +static int _pcm_sound_ignore_session (MMSoundPcmHandle_t handle, MMSound_session_type_e type); + +static char* __get_channel_str(MMSoundPcmChannel_t channel) +{ + if (channel == MMSOUND_PCM_MONO) + return "Mono"; + else if (channel == MMSOUND_PCM_STEREO) + return "Stereo"; + else + return "Unknown"; +} + +static char* __get_format_str(MMSoundPcmFormat_t format) +{ + if (format == MMSOUND_PCM_S16_LE) + return "S16LE"; + else if (format == MMSOUND_PCM_U8) + return "U8"; + else + return "Unknown"; +} + +static int _get_asm_information(MMSound_session_type_e session_type, ASM_sound_events_t *type, int *options, int *session_registered_type) +{ + int cur_session = MM_SESSION_TYPE_MEDIA; + int session_options = 0; + int ret = MM_ERROR_NONE; + ASM_sound_events_t asm_event; + + if(type == NULL) + return MM_ERROR_SOUND_INVALID_POINTER; + + /* read session information */ + if(_mm_session_util_read_information(-1, &cur_session, &session_options) < 0) { + debug_log("Read Session Information failed. Set default \"Media\" type\n"); + if (session_type == MMSOUND_SESSION_TYPE_PLAYBACK) { + cur_session = MM_SESSION_TYPE_MEDIA; + } else if (session_type == MMSOUND_SESSION_TYPE_CAPTURE) { + cur_session = MM_SESSION_TYPE_MEDIA_RECORD; + } + ret = _mm_session_util_write_type(-1, cur_session); + if (ret) { + debug_error("_mm_session_util_write_type() failed\n"); + return MM_ERROR_SOUND_INTERNAL; + } + *session_registered_type = MMSOUND_SESSION_REGISTERED_INTERNALLY; + } else { + /* session was already registered */ + if (session_type == MMSOUND_SESSION_TYPE_CAPTURE) { + if (cur_session > MM_SESSION_TYPE_MEDIA && cur_session < MM_SESSION_TYPE_CALL) { + debug_error("current session type(%d) does not support capture\n", session_type); + return MM_ERROR_POLICY_BLOCKED; + } else if (cur_session == MM_SESSION_TYPE_MEDIA) { + debug_log("session was already registered to MEDIA, update it to MEDIA_RECORD"); + /* update session information */ + ret = _mm_session_util_write_information(-1, MM_SESSION_TYPE_MEDIA_RECORD, session_options); + if (ret) { + debug_error("_mm_session_util_write_type() failed\n"); + return MM_ERROR_SOUND_INTERNAL; + } + *session_registered_type = MMSOUND_SESSION_REGISTERED_BY_OUTSIDE_MEDIA; + } + } + } + + /* convert MM_SESSION_TYPE to ASM_EVENT_TYPE */ + switch (cur_session) + { + case MM_SESSION_TYPE_MEDIA: + case MM_SESSION_TYPE_MEDIA_RECORD: + asm_event = ASM_EVENT_MEDIA_MMSOUND; + break; + case MM_SESSION_TYPE_NOTIFY: + asm_event = ASM_EVENT_NOTIFY; + break; + case MM_SESSION_TYPE_ALARM: + asm_event = ASM_EVENT_ALARM; + break; + case MM_SESSION_TYPE_CALL: + asm_event = ASM_EVENT_CALL; + break; + case MM_SESSION_TYPE_VIDEOCALL: + asm_event = ASM_EVENT_VIDEOCALL; + break; + case MM_SESSION_TYPE_VOIP: + asm_event = ASM_EVENT_VOIP; + break; + case MM_SESSION_TYPE_EMERGENCY: + asm_event = ASM_EVENT_EMERGENCY; + break; + case MM_SESSION_TYPE_VOICE_RECOGNITION: + asm_event = ASM_EVENT_VOICE_RECOGNITION; + break; + case MM_SESSION_TYPE_RECORD_AUDIO: + asm_event = ASM_EVENT_MMCAMCORDER_AUDIO; + break; + case MM_SESSION_TYPE_RECORD_VIDEO: + asm_event = ASM_EVENT_MMCAMCORDER_VIDEO; + break; + default: + debug_error("Unexpected %d\n", cur_session); + return MM_ERROR_SOUND_INTERNAL; + } + + *type = asm_event; + *options = session_options; + return MM_ERROR_NONE; +} + +static bool _check_skip_session_type_for_capture(mm_sound_pcm_t *pcmHandle, mm_sound_source_type_e type) +{ + bool ret = false; + int session_result = MM_ERROR_NONE; + switch (type) + { + case SUPPORT_SOURCE_TYPE_DEFAULT: + case SUPPORT_SOURCE_TYPE_VOICECONTROL: + ret = false; + break; + case SUPPORT_SOURCE_TYPE_MIRRORING: + ret = true; + break; + default: + debug_error("Unexpected %d\n", type); + return false; + } + if (ret) { + int capture_h_count = 0; + PCM_CAPTURE_H_COUNT_GET(capture_h_count); + if (capture_h_count == 1) { /* if it is last one */ + /* Recover session information */ + session_result = _mm_session_util_write_information(-1, MM_SESSION_TYPE_MEDIA, pcmHandle->asm_options); + if (session_result) { + debug_error("_mm_session_util_write_information() [type %d, options %x] failed[%x]", MM_SESSION_TYPE_MEDIA, pcmHandle->asm_options, session_result); + } + } + } + return ret; +} + +static void __sound_pcm_send_message (mm_sound_pcm_t *pcmHandle, int message, int code) +{ + int ret = 0; + if (pcmHandle->msg_cb) { + MMMessageParamType msg; + msg.union_type = MM_MSG_UNION_CODE; + msg.code = code; + + debug_log ("calling msg callback(%p) with message(%d), code(%d), msg callback param(%p)\n", + pcmHandle->msg_cb, message, msg.code, pcmHandle->msg_cb_param); + ret = pcmHandle->msg_cb(message, &msg, pcmHandle->msg_cb_param); + debug_log ("msg callback returned (%d)\n", ret); + } else { + debug_log ("No pcm msg callback\n"); + } +} + +static ASM_cb_result_t sound_pcm_asm_callback(int handle, ASM_event_sources_t event_src, ASM_sound_commands_t command, unsigned int sound_status, void *cb_data) +{ + mm_sound_pcm_t *pcmHandle = (mm_sound_pcm_t *)cb_data; + ASM_cb_result_t cb_res = ASM_CB_RES_IGNORE; + int message = MM_MESSAGE_SOUND_PCM_INTERRUPTED; + + /* Check input param */ + if(pcmHandle == NULL) { + debug_error("sound_pcm_asm_callback cb_data is null\n"); + return cb_res; + } + + debug_log ("command = %d, handle = %p, is_started = %d\n",command, pcmHandle, pcmHandle->is_started); + switch(command) + { + case ASM_COMMAND_PAUSE: + case ASM_COMMAND_STOP: + /* Do stop */ + PCM_LOCK_INTERNAL(&pcmHandle->pcm_mutex_internal); + _pcm_sound_stop_internal (pcmHandle); + PCM_UNLOCK_INTERNAL(&pcmHandle->pcm_mutex_internal); + cb_res = ASM_CB_RES_PAUSE; + break; + + case ASM_COMMAND_RESUME: + cb_res = ASM_CB_RES_IGNORE; + message = MM_MESSAGE_READY_TO_RESUME; + break; + + case ASM_COMMAND_PLAY: + case ASM_COMMAND_NONE: + debug_error ("Not an expected case!!!!\n"); + break; + } + + /* execute user callback if callback available */ + __sound_pcm_send_message (pcmHandle, message, event_src); + + return cb_res; +} + +static int _pcm_sound_ignore_session (MMSoundPcmHandle_t handle, MMSound_session_type_e type) +{ + int result = MM_ERROR_NONE; + int session_result = MM_ERROR_NONE; + mm_sound_pcm_t *pcmHandle = (mm_sound_pcm_t*)handle; + int errorcode = 0; + + debug_fenter(); + + /* Check input param */ + if(pcmHandle == NULL) { + debug_error ("Handle is null, return Invalid Argument\n"); + result = MM_ERROR_INVALID_ARGUMENT; + goto EXIT; + } + + if (pcmHandle->is_started) { + debug_error ("Operation is not permitted while started\n"); + result = MM_ERROR_SOUND_INVALID_STATE; + goto EXIT; + } + + PCM_LOCK_INTERNAL(&pcmHandle->pcm_mutex_internal); + + /* Unregister ASM */ + if (pcmHandle->skip_session == false && pcmHandle->asm_handle) { + if(!ASM_unregister_sound(pcmHandle->asm_handle, pcmHandle->asm_event, &errorcode)) { + debug_error("ASM_unregister failed with 0x%x\n", errorcode); + result = MM_ERROR_SOUND_INTERNAL; + } + pcmHandle->skip_session = true; + pcmHandle->asm_handle = 0; + } + if (type == MMSOUND_SESSION_TYPE_CAPTURE){ + int capture_h_count = 0; + PCM_CAPTURE_H_COUNT_GET(capture_h_count); + if (capture_h_count == 1) { /* if it is last one */ + /* Recover session information */ + session_result = _mm_session_util_write_information(-1, MM_SESSION_TYPE_MEDIA, pcmHandle->asm_options); + if (session_result) { + debug_error("_mm_session_util_write_information() [type %d, options %x] failed[%x]", MM_SESSION_TYPE_MEDIA, pcmHandle->asm_options, session_result); + } + } + } + + PCM_UNLOCK_INTERNAL(&pcmHandle->pcm_mutex_internal); + +EXIT: + debug_fleave(); + return result; +} + +EXPORT_API +int mm_sound_pcm_capture_open(MMSoundPcmHandle_t *handle, const unsigned int rate, MMSoundPcmChannel_t channel, MMSoundPcmFormat_t format) +{ + mm_sound_pcm_t *pcmHandle = NULL; + int size = 0; + int result = MM_ERROR_NONE; + int errorcode = 0; + int ret_mutex = 0; + int ret = MM_ERROR_NONE; + + int volume_config = 0; + pa_sample_spec ss; + + mm_sound_handle_route_info route_info; + route_info.policy = HANDLE_ROUTE_POLICY_DEFAULT; + + debug_warning ("enter : rate=[%d], channel=[%x], format=[%x]\n", rate, channel, format); + + if (rate < _MIN_SYSTEM_SAMPLERATE || rate > _MAX_SYSTEM_SAMPLERATE) { + debug_error("unsupported sample rate %u", rate); + return MM_ERROR_SOUND_DEVICE_INVALID_SAMPLERATE; + } else { + ss.rate = rate; + } + + switch(channel) + { + case MMSOUND_PCM_MONO: + ss.channels = 1; + break; + case MMSOUND_PCM_STEREO: + ss.channels = 2; + break; + + default: + debug_error("Unsupported channel type\n"); + return MM_ERROR_SOUND_DEVICE_INVALID_CHANNEL; + } + + switch(format) + { + case MMSOUND_PCM_U8: + ss.format = PA_SAMPLE_U8; + break; + case MMSOUND_PCM_S16_LE: + ss.format = PA_SAMPLE_S16LE; + break; + default: + debug_error("Unsupported format type\n"); + return MM_ERROR_SOUND_DEVICE_INVALID_FORMAT; + } + + pcmHandle = calloc(sizeof(mm_sound_pcm_t), 1); + if(pcmHandle == NULL) + return MM_ERROR_OUT_OF_MEMORY; + + ret_mutex = pthread_mutex_init(&pcmHandle->pcm_mutex_internal, NULL); + if(ret_mutex != 0) + { + free(pcmHandle); + return MM_ERROR_OUT_OF_MEMORY; + } + + /* Register ASM */ + /* get session information */ + ret = _get_asm_information(MMSOUND_SESSION_TYPE_CAPTURE, &pcmHandle->asm_event, &pcmHandle->asm_options, &pcmHandle->session_registered_type); + if(ret) { + PCM_LOCK_DESTROY_INTERNAL(&pcmHandle->pcm_mutex_internal); + free(pcmHandle); + return ret; + } + PCM_CAPTURE_H_COUNT_INC(); + + /* register asm */ + if(pcmHandle->asm_event != ASM_EVENT_CALL && + pcmHandle->asm_event != ASM_EVENT_VIDEOCALL && + pcmHandle->asm_event != ASM_EVENT_VOIP && + pcmHandle->asm_event != ASM_EVENT_VOICE_RECOGNITION && + pcmHandle->asm_event != ASM_EVENT_MMCAMCORDER_AUDIO && + pcmHandle->asm_event != ASM_EVENT_MMCAMCORDER_VIDEO && + pcmHandle->skip_session == false) { + if(!ASM_register_sound(-1, &pcmHandle->asm_handle, pcmHandle->asm_event, + /* ASM_STATE_PLAYING */ ASM_STATE_NONE, sound_pcm_asm_callback, (void*)pcmHandle, pcmHandle->resource, &errorcode)) + { + debug_error("ASM_register_sound() failed 0x%x\n", errorcode); + PCM_LOCK_DESTROY_INTERNAL(&pcmHandle->pcm_mutex_internal); + free(pcmHandle); + PCM_CAPTURE_H_COUNT_DEC(); + return MM_ERROR_POLICY_BLOCKED; + } + if(!ASM_set_session_option(pcmHandle->asm_handle, pcmHandle->asm_options, &errorcode)) { + debug_error("ASM_set_session_option() failed 0x%x\n", errorcode); + } + } else { + pcmHandle->skip_session = true; + } + + /* Open */ + if(pcmHandle->asm_event == ASM_EVENT_VOIP) + volume_config = VOLUME_TYPE_VOIP; + else + volume_config = VOLUME_TYPE_SYSTEM; //dose not effect at capture mode + + pcmHandle->handle = mm_sound_pa_open(HANDLE_MODE_INPUT, &route_info, 0, volume_config, &ss, NULL, &size); + if(pcmHandle->handle<0) { + result = pcmHandle->handle; + debug_error("Device Open Error 0x%x\n", result); + PCM_LOCK_DESTROY_INTERNAL(&pcmHandle->pcm_mutex_internal); + free(pcmHandle); + PCM_CAPTURE_H_COUNT_DEC(); + return result; + } + + pcmHandle->is_playback = false; + pcmHandle->rate = rate; + pcmHandle->channel = channel; + pcmHandle->format = format; + pcmHandle->byte_per_sec = rate*(format==MMSOUND_PCM_U8?1:2)*(channel==MMSOUND_PCM_MONO?1:2); + + /* Set handle to return */ + *handle = (MMSoundPcmHandle_t)pcmHandle; + + debug_warning ("success : handle=[%p], size=[%d]\n", pcmHandle, size); + + return size; +} + +EXPORT_API +int mm_sound_pcm_capture_open_ex(MMSoundPcmHandle_t *handle, const unsigned int rate, MMSoundPcmChannel_t channel, MMSoundPcmFormat_t format, mm_sound_source_type_e source_type) +{ + mm_sound_pcm_t *pcmHandle = NULL; + int size = 0; + int result = MM_ERROR_NONE; + int errorcode = 0; + int ret_mutex = 0; + + int volume_config = 0; + pa_sample_spec ss; + mm_sound_handle_route_info route_info; + route_info.policy = HANDLE_ROUTE_POLICY_DEFAULT; + + debug_warning ("enter : rate=[%d Hz], channel=[%x][%s], format=[%x][%s], source_type=[%x]\n", + rate, channel, __get_channel_str(channel), format, __get_format_str(format), source_type); + + if (rate < _MIN_SYSTEM_SAMPLERATE || rate > _MAX_SYSTEM_SAMPLERATE) { + debug_error("unsupported sample rate %u", rate); + return MM_ERROR_SOUND_DEVICE_INVALID_SAMPLERATE; + } else { + ss.rate = rate; + } + + switch(channel) + { + case MMSOUND_PCM_MONO: + ss.channels = 1; + break; + case MMSOUND_PCM_STEREO: + ss.channels = 2; + break; + + default: + debug_error("Unsupported channel type\n"); + return MM_ERROR_SOUND_DEVICE_INVALID_CHANNEL; + } + + switch(format) + { + case MMSOUND_PCM_U8: + ss.format = PA_SAMPLE_U8; + break; + case MMSOUND_PCM_S16_LE: + ss.format = PA_SAMPLE_S16LE; + break; + default: + debug_error("Unsupported format type\n"); + return MM_ERROR_SOUND_DEVICE_INVALID_FORMAT; + } + + pcmHandle = calloc(sizeof(mm_sound_pcm_t), 1); + if(pcmHandle == NULL) + return MM_ERROR_OUT_OF_MEMORY; + + ret_mutex = pthread_mutex_init(&pcmHandle->pcm_mutex_internal, NULL); + if(ret_mutex != 0) + { + free(pcmHandle); + return MM_ERROR_OUT_OF_MEMORY; + } + + /* Register ASM */ + /* get session information */ + if(MM_ERROR_NONE != _get_asm_information(MMSOUND_SESSION_TYPE_CAPTURE, &pcmHandle->asm_event, &pcmHandle->asm_options, &pcmHandle->session_registered_type)) { + PCM_LOCK_DESTROY_INTERNAL(&pcmHandle->pcm_mutex_internal); + free(pcmHandle); + return MM_ERROR_POLICY_INTERNAL; + } + PCM_CAPTURE_H_COUNT_INC(); + + switch (source_type) { + case SUPPORT_SOURCE_TYPE_VOIP: + route_info.policy = HANDLE_ROUTE_POLICY_IN_VOIP; + break; + case SUPPORT_SOURCE_TYPE_MIRRORING: + route_info.policy = HANDLE_ROUTE_POLICY_IN_MIRRORING; + break; + case SUPPORT_SOURCE_TYPE_DEFAULT: + case SUPPORT_SOURCE_TYPE_VIDEOCALL: + case SUPPORT_SOURCE_TYPE_VOICERECORDING: + break; + case SUPPORT_SOURCE_TYPE_VOICECONTROL: + pcmHandle->asm_event = ASM_EVENT_EXCLUSIVE_RESOURCE; + pcmHandle->resource = ASM_RESOURCE_VOICECONTROL; + break; + default: + break; + } + + /* register asm */ + if(pcmHandle->asm_event != ASM_EVENT_CALL && + pcmHandle->asm_event != ASM_EVENT_VIDEOCALL && + pcmHandle->asm_event != ASM_EVENT_VOIP && + pcmHandle->asm_event != ASM_EVENT_VOICE_RECOGNITION && + pcmHandle->asm_event != ASM_EVENT_MMCAMCORDER_AUDIO && + pcmHandle->asm_event != ASM_EVENT_MMCAMCORDER_VIDEO && + pcmHandle->skip_session == false && + _check_skip_session_type_for_capture(pcmHandle, source_type) == false) { + if(!ASM_register_sound(-1, &pcmHandle->asm_handle, pcmHandle->asm_event, + /* ASM_STATE_PLAYING */ ASM_STATE_NONE, sound_pcm_asm_callback, (void*)pcmHandle, pcmHandle->resource, &errorcode)) { + debug_error("ASM_register_sound() failed 0x%x\n", errorcode); + PCM_LOCK_DESTROY_INTERNAL(&pcmHandle->pcm_mutex_internal); + free(pcmHandle); + PCM_CAPTURE_H_COUNT_DEC(); + return MM_ERROR_POLICY_BLOCKED; + } + if(!ASM_set_session_option(pcmHandle->asm_handle, pcmHandle->asm_options, &errorcode)) { + debug_error("ASM_set_session_option() failed 0x%x\n", errorcode); + } + } else { + pcmHandle->skip_session = true; + } + + /* For Video Call or VoIP select volume type VOLUME_TYPE_VOIP for sink/source */ + if( (pcmHandle->asm_event == ASM_EVENT_VIDEOCALL) || (pcmHandle->asm_event == ASM_EVENT_VOIP) ) + volume_config = VOLUME_TYPE_VOIP; + else + volume_config = VOLUME_TYPE_SYSTEM; //dose not effect at capture mode + + pcmHandle->handle = mm_sound_pa_open(HANDLE_MODE_INPUT, &route_info, 0, volume_config, &ss, NULL, &size); + if(pcmHandle->handle<0) { + result = pcmHandle->handle; + debug_error("Device Open Error 0x%x\n", result); + PCM_LOCK_DESTROY_INTERNAL(&pcmHandle->pcm_mutex_internal); + free(pcmHandle); + PCM_CAPTURE_H_COUNT_DEC(); + return result; + } + + pcmHandle->is_playback = false; + pcmHandle->rate = rate; + pcmHandle->channel = channel; + pcmHandle->format = format; + pcmHandle->byte_per_sec = rate*(format==MMSOUND_PCM_U8?1:2)*(channel==MMSOUND_PCM_MONO?1:2); + + /* Set handle to return */ + *handle = (MMSoundPcmHandle_t)pcmHandle; + + debug_warning ("success : handle=[%p], size=[%d]\n", handle, size); + + return size; +} + +EXPORT_API +int mm_sound_pcm_capture_ignore_session(MMSoundPcmHandle_t *handle) +{ + return _pcm_sound_ignore_session(handle, MMSOUND_SESSION_TYPE_CAPTURE); +} + +static int _pcm_sound_start (MMSoundPcmHandle_t handle) +{ + mm_sound_pcm_t *pcmHandle = (mm_sound_pcm_t*)handle; + int errorcode = 0; + int ret = 0; + + debug_fenter(); + + /* Check input param */ + if(pcmHandle == NULL) { + debug_error ("Handle is null, return Invalid Argument\n"); + ret = MM_ERROR_INVALID_ARGUMENT; + goto NULL_HANDLE; + } + + PCM_LOCK_INTERNAL(&pcmHandle->pcm_mutex_internal); + + if (pcmHandle->skip_session == false) { + /* ASM set state to PLAYING */ + if (!ASM_set_sound_state(pcmHandle->asm_handle, pcmHandle->asm_event, ASM_STATE_PLAYING, pcmHandle->resource, &errorcode)) { + debug_error("ASM_set_sound_state(PLAYING) failed 0x%x\n", errorcode); + ret = MM_ERROR_POLICY_BLOCKED; + goto EXIT; + } + } + + /* Update State */ + pcmHandle->is_started = true; + + /* Un-Cork */ + mm_sound_pa_cork(pcmHandle->handle, 0); + +EXIT: + PCM_UNLOCK_INTERNAL(&pcmHandle->pcm_mutex_internal); + +NULL_HANDLE: + debug_fleave(); + return ret; +} + +EXPORT_API +int mm_sound_pcm_capture_start(MMSoundPcmHandle_t handle) +{ + int ret = MM_ERROR_NONE; + + debug_warning ("enter : handle=[%p]\n", handle); + + ret = _pcm_sound_start (handle); + if (ret != MM_ERROR_NONE) { + debug_error ("_pcm_sound_start() failed (%x)\n", ret); + goto EXIT; + } + +EXIT: + debug_warning ("leave : handle=[%p], ret=[0x%X]", handle, ret); + + return ret; +} + +static int _pcm_sound_stop_internal (MMSoundPcmHandle_t handle) +{ + mm_sound_pcm_t *pcmHandle = (mm_sound_pcm_t*)handle; + + /* Check input param */ + if(pcmHandle == NULL) + return MM_ERROR_INVALID_ARGUMENT; + + /* Check State */ + if (pcmHandle->is_started == false) { + debug_warning ("Can't stop because not started\n"); + return MM_ERROR_SOUND_INVALID_STATE; + } + + /* Drain if playback mode */ + if (pcmHandle->is_playback) { + if(MM_ERROR_NONE != mm_sound_pa_drain(pcmHandle->handle)) { + debug_error("drain failed\n"); + } + } + + /* Update State */ + pcmHandle->is_started = false; + + /* Cork */ + return mm_sound_pa_cork(pcmHandle->handle, 1); +} + +static int _pcm_sound_stop(MMSoundPcmHandle_t handle) +{ + mm_sound_pcm_t *pcmHandle = (mm_sound_pcm_t*)handle; + int errorcode = 0; + int ret = MM_ERROR_NONE; + + debug_fenter(); + + /* Check input param */ + if(pcmHandle == NULL) { + debug_error ("Handle is null, return Invalid Argument\n"); + ret = MM_ERROR_INVALID_ARGUMENT; + goto NULL_HANDLE; + } + + PCM_LOCK_INTERNAL(&pcmHandle->pcm_mutex_internal); + + /* Do stop procedure */ + ret = _pcm_sound_stop_internal(handle); + if (ret == MM_ERROR_NONE) { + /* Set ASM State to STOP */ + if (pcmHandle->skip_session == false) { + if (!ASM_set_sound_state(pcmHandle->asm_handle, pcmHandle->asm_event, ASM_STATE_STOP, pcmHandle->resource, &errorcode)) { + debug_error("ASM_set_sound_state(STOP) failed 0x%x\n", errorcode); + ret = MM_ERROR_POLICY_BLOCKED; + goto EXIT; + } + } + } + +EXIT: + PCM_UNLOCK_INTERNAL(&pcmHandle->pcm_mutex_internal); +NULL_HANDLE: + debug_fleave(); + return ret; +} + +EXPORT_API +int mm_sound_pcm_capture_stop(MMSoundPcmHandle_t handle) +{ + int ret = 0; + + debug_warning ("enter : handle=[%p]\n", handle); + ret = _pcm_sound_stop(handle); + debug_warning ("leave : handle=[%p], ret=[0x%X]\n", handle, ret); + + return ret; +} + +EXPORT_API +int mm_sound_pcm_capture_read(MMSoundPcmHandle_t handle, void *buffer, const unsigned int length ) +{ + int ret = 0; + static int read_byte = 0; + mm_sound_pcm_t *pcmHandle = (mm_sound_pcm_t*)handle; + + /* Check input param */ + if(pcmHandle == NULL) { + debug_error ("Handle is null, return Invalid Argument\n"); + ret = MM_ERROR_INVALID_ARGUMENT; + goto NULL_HANDLE; + } + PCM_LOCK_INTERNAL(&pcmHandle->pcm_mutex_internal); + + if(buffer == NULL) { + debug_error("Invalid buffer pointer\n"); + ret = MM_ERROR_SOUND_INVALID_POINTER; + goto EXIT; + } + if(length == 0 ) { + debug_error ("length is 0, return 0\n"); + ret = 0; + goto EXIT; + } + + /* Check State : return fail if not started */ + if (!pcmHandle->is_started) { + /* not started, return fail */ + debug_error ("Not started yet, return Invalid State \n"); + ret = MM_ERROR_SOUND_INVALID_STATE; + goto EXIT; + } + + /* Read */ + ret = mm_sound_pa_read(pcmHandle->handle, buffer, length); + +EXIT: + PCM_UNLOCK_INTERNAL(&pcmHandle->pcm_mutex_internal); +NULL_HANDLE: + read_byte += length; + + if(ret > 0 && read_byte>pcmHandle->byte_per_sec*RW_LOG_PERIOD){ + debug_log ("(%d)/read-once, (%d)/%dsec bytes read \n", length, read_byte, RW_LOG_PERIOD); + read_byte = 0; + } + return ret; +} + +EXPORT_API +int mm_sound_pcm_capture_close(MMSoundPcmHandle_t handle) +{ + int result = MM_ERROR_NONE; + mm_sound_pcm_t *pcmHandle = (mm_sound_pcm_t*)handle; + int errorcode = 0; + + debug_warning ("enter : handle=[%p]\n", handle); + + /* Check input param */ + if(pcmHandle == NULL) { + debug_error ("Handle is null, return Invalid Argument\n"); + result = MM_ERROR_INVALID_ARGUMENT; + goto NULL_HDL; + } + PCM_LOCK_INTERNAL(&pcmHandle->pcm_mutex_internal); + /* Close */ + if(MM_ERROR_NONE != mm_sound_pa_close(pcmHandle->handle)) { + debug_error("handle close failed 0x%X", result); + result = MM_ERROR_SOUND_INTERNAL; + goto EXIT; + } + + /* Unregister ASM */ + if (pcmHandle->skip_session == false) { + if(pcmHandle->asm_event != ASM_EVENT_CALL && + pcmHandle->asm_event != ASM_EVENT_VIDEOCALL && + pcmHandle->asm_event != ASM_EVENT_VOIP && + pcmHandle->asm_event != ASM_EVENT_VOICE_RECOGNITION && + pcmHandle->asm_event != ASM_EVENT_MMCAMCORDER_AUDIO && + pcmHandle->asm_event != ASM_EVENT_MMCAMCORDER_VIDEO) { + if (pcmHandle->asm_handle) { + if(!ASM_unregister_sound(pcmHandle->asm_handle, pcmHandle->asm_event, &errorcode)) { + debug_error("ASM_unregister failed with 0x%x\n", errorcode); + result = MM_ERROR_SOUND_INTERNAL; + goto EXIT; + } + } + } + int capture_h_count = 0; + PCM_CAPTURE_H_COUNT_GET(capture_h_count); + if (capture_h_count == 1) { /* if it is last one */ + /* Recover session information */ + result = _mm_session_util_write_information(-1, MM_SESSION_TYPE_MEDIA, pcmHandle->asm_options); + if (result) { + debug_error("_mm_session_util_write_information() [type %d, options %x] failed[%x]", MM_SESSION_TYPE_MEDIA, pcmHandle->asm_options, result); + } + } + } + +EXIT: + PCM_UNLOCK_INTERNAL(&pcmHandle->pcm_mutex_internal); +NULL_HDL: + debug_warning ("leave : handle=[%p], ret=[0x%X]\n", handle, result); + /* Free handle */ + if (pcmHandle) { + PCM_LOCK_DESTROY_INTERNAL(&pcmHandle->pcm_mutex_internal); + free(pcmHandle); + pcmHandle = NULL; + } + PCM_CAPTURE_H_COUNT_DEC(); + + return result; +} + +EXPORT_API +int mm_sound_pcm_set_message_callback (MMSoundPcmHandle_t handle, MMMessageCallback callback, void *user_param) +{ + mm_sound_pcm_t *pcmHandle = (mm_sound_pcm_t*)handle; + + if(pcmHandle == NULL || callback == NULL) + return MM_ERROR_INVALID_ARGUMENT; + + pcmHandle->msg_cb = callback; + pcmHandle->msg_cb_param = user_param; + + debug_log ("set pcm message callback (%p,%p)\n", callback, user_param); + + return MM_ERROR_NONE; +} + +EXPORT_API +int mm_sound_pcm_play_open_ex (MMSoundPcmHandle_t *handle, const unsigned int rate, MMSoundPcmChannel_t channel, MMSoundPcmFormat_t format, int volume_config, ASM_sound_events_t asm_event) +{ + mm_sound_pcm_t *pcmHandle = NULL; + int size = 0; + int result = MM_ERROR_NONE; + int errorcode = 0; + int volume_type = MM_SOUND_VOLUME_CONFIG_TYPE(volume_config); + int ret_mutex = 0; + mm_sound_handle_route_info route_info; + route_info.policy = HANDLE_ROUTE_POLICY_OUT_AUTO; + + pa_sample_spec ss; + + debug_warning ("enter : rate=[%d], channel=[%x][%s], format=[%x][%s], volconf=[%d], event=[%d]\n", + rate, channel, __get_channel_str(channel), format, __get_format_str(format), volume_config, asm_event); + + /* Check input param */ + if (volume_type < 0 || volume_type >= VOLUME_TYPE_MAX) { + debug_error("Volume type is invalid %d\n", volume_type); + return MM_ERROR_INVALID_ARGUMENT; + } + if (rate < _MIN_SYSTEM_SAMPLERATE || rate > _MAX_SYSTEM_SAMPLERATE) { + debug_error("unsupported sample rate %u", rate); + return MM_ERROR_SOUND_DEVICE_INVALID_SAMPLERATE; + } else { + ss.rate = rate; + } + + switch(channel) + { + case MMSOUND_PCM_MONO: + ss.channels = 1; + break; + case MMSOUND_PCM_STEREO: + ss.channels = 2; + break; + default: + debug_error("Unsupported channel type\n"); + return MM_ERROR_SOUND_DEVICE_INVALID_CHANNEL; + } + + switch(format) + { + case MMSOUND_PCM_U8: + ss.format = PA_SAMPLE_U8; + break; + case MMSOUND_PCM_S16_LE: + ss.format = PA_SAMPLE_S16LE; + break; + default: + debug_error("Unsupported format type\n"); + return MM_ERROR_SOUND_DEVICE_INVALID_FORMAT; + } + + pcmHandle = calloc(sizeof(mm_sound_pcm_t),1); + if(pcmHandle == NULL) + return MM_ERROR_OUT_OF_MEMORY; + + ret_mutex = pthread_mutex_init(&pcmHandle->pcm_mutex_internal, NULL); + if(ret_mutex != 0) { + debug_error ("error mutex init....%d"); + result = MM_ERROR_OUT_OF_MEMORY; + goto ERROR; + } + + /* Register ASM */ + debug_log ("session start : input asm_event = %d-------------\n", asm_event); + if (asm_event == ASM_EVENT_MONITOR) { + debug_log ("Skip SESSION for event (%d)\n", asm_event); + pcmHandle->skip_session = true; + } else if (asm_event == ASM_EVENT_NONE) { + /* get session information */ + if(MM_ERROR_NONE != _get_asm_information(MMSOUND_SESSION_TYPE_PLAYBACK, &pcmHandle->asm_event, &pcmHandle->asm_options, &pcmHandle->session_registered_type)) { + debug_error ("_get_asm_information failed....\n"); + result = MM_ERROR_POLICY_INTERNAL; + goto ERROR; + } + + // should be fixed. call forwarding engine(voip) use call volume type. + if(volume_type == VOLUME_TYPE_CALL) { + pcmHandle->skip_session = true; + } + + if(pcmHandle->asm_event != ASM_EVENT_CALL && + pcmHandle->asm_event != ASM_EVENT_VIDEOCALL && + pcmHandle->asm_event != ASM_EVENT_VOIP && + pcmHandle->asm_event != ASM_EVENT_VOICE_RECOGNITION && + pcmHandle->asm_event != ASM_EVENT_MMCAMCORDER_AUDIO && + pcmHandle->asm_event != ASM_EVENT_MMCAMCORDER_VIDEO && + pcmHandle->skip_session == false) { + + /* register asm */ + if(!ASM_register_sound(-1, &pcmHandle->asm_handle, pcmHandle->asm_event, + ASM_STATE_NONE, sound_pcm_asm_callback, (void*)pcmHandle, pcmHandle->resource, &errorcode)) { + debug_error("ASM_register_sound() failed 0x%x\n", errorcode); + result = MM_ERROR_POLICY_BLOCKED; + goto ERROR; + } + if(!ASM_set_session_option(pcmHandle->asm_handle, pcmHandle->asm_options, &errorcode)) { + debug_error("ASM_set_session_option() failed 0x%x\n", errorcode); + } + } else { + pcmHandle->skip_session = true; + } + } else { + /* register asm using asm_event input */ + if(!ASM_register_sound(-1, &pcmHandle->asm_handle, asm_event, + ASM_STATE_NONE, NULL, (void*)pcmHandle, pcmHandle->resource, &errorcode)) { + debug_error("ASM_register_sound() failed 0x%x\n", errorcode); + result = MM_ERROR_POLICY_BLOCKED; + goto ERROR; + } + } + + + /* Open */ + pcmHandle->handle = mm_sound_pa_open(HANDLE_MODE_OUTPUT, &route_info, 0, volume_config, &ss, NULL, &size); + if(!pcmHandle->handle) { + debug_error("Device Open Error 0x%x\n"); + result = MM_ERROR_SOUND_DEVICE_NOT_OPENED; + goto ERROR; + } + + /* Set corked state, uncork will be done at prepare() + FIXME: we should consider audio_open() return with corked state */ + result = mm_sound_pa_cork(pcmHandle->handle, 1); + if(result) { + debug_error("Cork Error 0x%x\n", result); + result = MM_ERROR_SOUND_INTERNAL; + goto ERROR; + } + + pcmHandle->is_playback = true; + pcmHandle->rate = rate; + pcmHandle->channel = channel; + pcmHandle->format = format; + pcmHandle->byte_per_sec = rate*(format==MMSOUND_PCM_U8?1:2)*(channel==MMSOUND_PCM_MONO?1:2); + pcmHandle->volume_config = volume_config; + + /* Set handle to return */ + *handle = (MMSoundPcmHandle_t)pcmHandle; + + debug_warning ("success : handle=[%p], size=[%d]\n", pcmHandle, size); + return size; + +ERROR: + if (pcmHandle) { + if(ret_mutex == 0) { + PCM_LOCK_DESTROY_INTERNAL(&pcmHandle->pcm_mutex_internal); + } + free(pcmHandle); + } + return result; +} + +EXPORT_API +int mm_sound_pcm_play_open_no_session(MMSoundPcmHandle_t *handle, const unsigned int rate, MMSoundPcmChannel_t channel, MMSoundPcmFormat_t format, int volume_config) +{ + return mm_sound_pcm_play_open_ex (handle, rate, channel, format, volume_config, ASM_EVENT_MONITOR); +} + +EXPORT_API +int mm_sound_pcm_play_open(MMSoundPcmHandle_t *handle, const unsigned int rate, MMSoundPcmChannel_t channel, MMSoundPcmFormat_t format, int volume_config) +{ + return mm_sound_pcm_play_open_ex (handle, rate, channel, format, volume_config, ASM_EVENT_NONE); +} + +EXPORT_API +int mm_sound_pcm_play_start(MMSoundPcmHandle_t handle) +{ + int ret = 0; + + debug_warning ("enter : handle=[%p]\n", handle); + ret = _pcm_sound_start (handle); + debug_warning ("leave : handle=[%p], ret=[0x%X]\n", handle, ret); + + return ret; +} + +EXPORT_API +int mm_sound_pcm_play_stop(MMSoundPcmHandle_t handle) +{ + int ret = 0; + + debug_warning ("enter : handle=[%p]\n", handle); + ret = _pcm_sound_stop(handle); + debug_warning ("leave : handle=[%p], ret=[0x%X]\n", handle, ret); + + return ret; +} + +EXPORT_API +int mm_sound_pcm_play_write(MMSoundPcmHandle_t handle, void* ptr, unsigned int length_byte) +{ + int ret = 0; + static int written_byte = 0; + mm_sound_pcm_t *pcmHandle = (mm_sound_pcm_t*)handle; + int vr_state = 0; + + /* Check input param */ + if(pcmHandle == NULL) { + debug_error ("Handle is null, return Invalid Argument\n"); + ret = MM_ERROR_INVALID_ARGUMENT; + goto NULL_HANDLE; + } + + PCM_LOCK_INTERNAL(&pcmHandle->pcm_mutex_internal); + + if(ptr == NULL) { + debug_error("Invalid buffer pointer\n"); + ret = MM_ERROR_SOUND_INVALID_POINTER; + goto EXIT; + } + if(length_byte == 0 ) { + debug_error ("length is 0, return 0\n"); + ret = 0; + goto EXIT; + } + + /* Check State : return fail if not started */ + if (!pcmHandle->is_started) { + /* not started, return fail */ + debug_error ("Not started yet, return Invalid State \n"); + ret = MM_ERROR_SOUND_INVALID_STATE; + goto EXIT; + } + + /* check skip condition. + * if accessibility screen reader (VOICE type with NoSession), no need to check, always do write */ + if (!pcmHandle->skip_session || MM_SOUND_VOLUME_CONFIG_TYPE(pcmHandle->volume_config) != VOLUME_TYPE_VOICE) { + /* Check whether voicerecorder is running */ + vconf_get_int(VCONFKEY_RECORDER_STATE, &vr_state); + + if (vr_state == VCONFKEY_RECORDER_STATE_RECORDING) { + debug_log ("During VoiceRecording....MUTE!!!"); + goto EXIT; + } + } + + /* Write */ + ret = mm_sound_pa_write(pcmHandle->handle, ptr, length_byte); + + +EXIT: + PCM_UNLOCK_INTERNAL(&pcmHandle->pcm_mutex_internal); +NULL_HANDLE: + written_byte += length_byte; + if(ret > 0 && written_byte>pcmHandle->byte_per_sec*RW_LOG_PERIOD){ + debug_log ("(%d)/write-once, (%d)/%dsec bytes written\n", length_byte, written_byte, RW_LOG_PERIOD); + written_byte = 0; + } + + return ret; +} + +EXPORT_API +int mm_sound_pcm_play_close(MMSoundPcmHandle_t handle) +{ + int result = MM_ERROR_NONE; + mm_sound_pcm_t *pcmHandle = (mm_sound_pcm_t*)handle; + int errorcode = 0; + + debug_warning ("enter : handle=[%p]\n", handle); + + /* Check input param */ + if(pcmHandle == NULL) { + debug_error ("Handle is null, return Invalid Argument\n"); + result = MM_ERROR_INVALID_ARGUMENT; + goto NULL_HANDLE; + } + PCM_LOCK_INTERNAL(&pcmHandle->pcm_mutex_internal); + /* Drain if needed */ + if (pcmHandle->is_started) { + /* stop() is not called before close(), drain is needed */ + if(MM_ERROR_NONE != mm_sound_pa_drain(pcmHandle->handle)) { + debug_error("drain failed\n"); + result = MM_ERROR_SOUND_INTERNAL; + goto EXIT; + } + } + pcmHandle->is_started = false; + /* Close */ + if(MM_ERROR_NONE != mm_sound_pa_close(pcmHandle->handle)) { + debug_error("handle close failed. handle(%d)", pcmHandle->handle); + result = MM_ERROR_SOUND_INTERNAL; + goto EXIT; + } + + if (pcmHandle->skip_session == false) { + /* Unregister ASM */ + if(pcmHandle->asm_event != ASM_EVENT_CALL && + pcmHandle->asm_event != ASM_EVENT_VIDEOCALL && + pcmHandle->asm_event != ASM_EVENT_VOIP && + pcmHandle->asm_event != ASM_EVENT_VOICE_RECOGNITION && + pcmHandle->asm_event != ASM_EVENT_MMCAMCORDER_AUDIO && + pcmHandle->asm_event != ASM_EVENT_MMCAMCORDER_VIDEO) { + if(!ASM_unregister_sound(pcmHandle->asm_handle, pcmHandle->asm_event, &errorcode)) { + debug_error("ASM_unregister failed with 0x%x\n", errorcode); + } + } + } + +EXIT: + PCM_UNLOCK_INTERNAL(&pcmHandle->pcm_mutex_internal); +NULL_HANDLE: + debug_warning ("leave : handle=[%p], result[0x%X]\n", handle, result); + if (pcmHandle) { + /* Free handle */ + PCM_LOCK_DESTROY_INTERNAL(&pcmHandle->pcm_mutex_internal); + free(pcmHandle); + pcmHandle= NULL; + } + + return result; +} + +EXPORT_API +int mm_sound_pcm_play_ignore_session(MMSoundPcmHandle_t *handle) +{ + return _pcm_sound_ignore_session(handle, MMSOUND_SESSION_TYPE_PLAYBACK); +} + +EXPORT_API +int mm_sound_pcm_get_latency(MMSoundPcmHandle_t handle, int *latency) +{ + mm_sound_pcm_t *pcmHandle = (mm_sound_pcm_t*)handle; + int result = MM_ERROR_NONE; + int mlatency = 0; + + /* Check input param */ + if (latency == NULL) + return MM_ERROR_INVALID_ARGUMENT; + + if(MM_ERROR_NONE != mm_sound_pa_get_latency(pcmHandle->handle, &mlatency)) { + debug_error("Get Latency Error"); + return MM_ERROR_SOUND_DEVICE_NOT_OPENED; + } + + *latency = mlatency; + + return MM_ERROR_NONE; +} + +EXPORT_API +int mm_sound_pcm_is_started(MMSoundPcmHandle_t handle, bool *is_started) +{ + mm_sound_pcm_t *pcmHandle = (mm_sound_pcm_t*)handle; + + /* Check input param */ + if (is_started == NULL) + return MM_ERROR_INVALID_ARGUMENT; + + *is_started = pcmHandle->is_started; + + return MM_ERROR_NONE; +} |