[ACR-1422] Add new APIs for sound ducking 59/208159/30 accepted/tizen/unified/20190716.111232 submit/tizen/20190716.020521
authorJeongmo Yang <jm80.yang@samsung.com>
Wed, 19 Jun 2019 05:19:42 +0000 (14:19 +0900)
committerJeongmo Yang <jm80.yang@samsung.com>
Mon, 15 Jul 2019 12:21:27 +0000 (21:21 +0900)
- sound_manager_create_stream_ducking
- sound_manager_is_ducked
- sound_manager_activate_ducking
- sound_manager_deactivate_ducking
- sound_manager_destroy_stream_ducking

[Version] 0.5.31
[Issue Type] New feature

Change-Id: I69edb909e469a8ce7078a5fe23701856db738b8e
Signed-off-by: Jeongmo Yang <jm80.yang@samsung.com>
include/sound_manager.h
include/sound_manager_private.h
packaging/capi-media-sound-manager.spec
src/sound_manager.c
src/sound_manager_internal.c
src/sound_manager_private.c
test/sound_manager_test.c

index b3f6017..9ed2da8 100644 (file)
@@ -88,6 +88,12 @@ typedef enum {
 typedef struct sound_stream_info_s* sound_stream_info_h;
 
 /**
+ * @brief Sound stream ducking handle.
+ * @since_tizen 5.5
+ */
+typedef struct sound_stream_ducking_s* sound_stream_ducking_h;
+
+/**
  * @brief Enumeration for sound stream type.
  * @since_tizen 3.0
  */
@@ -276,6 +282,23 @@ typedef enum {
 typedef void (*sound_manager_volume_changed_cb) (sound_type_e type, unsigned int volume, void *user_data);
 
 /**
+ * @brief Called when the ducking activation or deactivation is finished.
+ * @since_tizen 5.5
+ *
+ * @remarks    This function is invoked by the internal thread of the sound manager.
+ *     Therefore it is recommended not to call functions which update the UI from this callback.
+ * @remarks    @a stream_ducking is the same handle for which the callback was set in the sound_manager_create_stream_ducking() call,
+ *     so it should not be released in this callback.
+ *
+ * @param[in]   stream_ducking The stream ducking handle
+ * @param[in]   is_ducked      The flag whether it's ducked or not
+ * @param[in]   user_data      The user data passed from the callback registration function
+ * @pre You should register this callback when sound_manager_create_stream_ducking() is called.
+ * @see sound_manager_create_stream_ducking()
+ */
+typedef void (*sound_stream_ducking_state_changed_cb) (sound_stream_ducking_h stream_ducking, bool is_ducked, void *user_data);
+
+/**
  * @}
  */
 
@@ -484,6 +507,112 @@ int sound_manager_add_volume_changed_cb(sound_manager_volume_changed_cb callback
 int sound_manager_remove_volume_changed_cb(int id);
 
 /**
+ * @brief Creates a handle for stream ducking.
+ * @since_tizen 5.5
+ *
+ * @remarks @a stream_ducking should be released using sound_manager_destroy_stream_ducking().
+ *
+ * @param[in]  target_stream   The type of target stream
+ * @param[in]  callback                The ducking state changed callback function (optional, this can be NULL)
+ * @param[in]  user_data               The user data to be passed to the callback function (optional, this can be NULL)
+ * @param[out] stream_ducking  The handle of stream ducking
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SOUND_MANAGER_ERROR_NONE Success
+ * @retval #SOUND_MANAGER_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #SOUND_MANAGER_ERROR_INTERNAL Internal error inside the sound system
+ * @see sound_manager_destroy_stream_ducking()
+ * @see sound_manager_is_ducked()
+ * @see sound_manager_activate_ducking()
+ * @see sound_manager_deactivate_ducking()
+ */
+int sound_manager_create_stream_ducking(sound_stream_type_e target_stream, sound_stream_ducking_state_changed_cb callback, void *user_data, sound_stream_ducking_h *stream_ducking);
+
+/**
+ * @brief Checks if the stream is ducked.
+ * @since_tizen 5.5
+ *
+ * @param[in]  stream_ducking  The handle of stream ducking
+ * @param[out] is_ducked               Whether the stream is ducked or not: (@c true = ducked, @c false = unducked)
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SOUND_MANAGER_ERROR_NONE Success
+ * @retval #SOUND_MANAGER_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #SOUND_MANAGER_ERROR_INTERNAL Internal error inside the sound system
+ * @see sound_manager_create_stream_ducking()
+ * @see sound_manager_destroy_stream_ducking()
+ * @see sound_manager_activate_ducking()
+ * @see sound_manager_deactivate_ducking()
+ */
+int sound_manager_is_ducked(sound_stream_ducking_h stream_ducking, bool *is_ducked);
+
+/**
+ * @brief Activates ducking, asynchronously.
+ * @since_tizen 5.5
+ * @privlevel public
+ * @privilege %http://tizen.org/privilege/volume.set
+ * @remarks If ducking is activated successfully, the volume of all sound streams
+ *     matched with target_stream (set in sound_manager_create_stream_ducking())
+ *     is decreased by @a ratio for @a duration. The change is system-wide.
+ * @param[in]  stream_ducking  The handle of stream ducking
+ * @param[in]  duration                The duration for ducking (msec, 0 <= duration <= 3000)
+ * @param[in]  ratio                   The volume ratio after ducked (0.0 < ratio < 1.0)
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SOUND_MANAGER_ERROR_NONE Success
+ * @retval #SOUND_MANAGER_ERROR_PERMISSION_DENIED Permission denied
+ * @retval #SOUND_MANAGER_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #SOUND_MANAGER_ERROR_INVALID_STATE Invalid state
+ * @retval #SOUND_MANAGER_ERROR_INTERNAL Internal error inside the sound system
+ * @pre The stream should be unducked before calling this function,
+ *      otherwise, #SOUND_MANAGER_ERROR_INVALID_STATE is returned.
+ * @see sound_manager_create_stream_ducking()
+ * @see sound_manager_destroy_stream_ducking()
+ * @see sound_manager_is_ducked()
+ * @see sound_manager_deactivate_ducking()
+ */
+int sound_manager_activate_ducking(sound_stream_ducking_h stream_ducking, unsigned int duration, double ratio);
+
+/**
+ * @brief Deactivates ducking, asynchronously.
+ * @since_tizen 5.5
+ * @privlevel public
+ * @privilege %http://tizen.org/privilege/volume.set
+ * @param[in]  stream_ducking  The handle of stream ducking
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SOUND_MANAGER_ERROR_NONE Success
+ * @retval #SOUND_MANAGER_ERROR_PERMISSION_DENIED Permission denied
+ * @retval #SOUND_MANAGER_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #SOUND_MANAGER_ERROR_INVALID_STATE Invalid state
+ * @retval #SOUND_MANAGER_ERROR_INTERNAL Internal error inside the sound system
+ * @pre The stream should be ducked before calling this function,
+ *      otherwise, #SOUND_MANAGER_ERROR_INVALID_STATE is returned.
+ * @see sound_manager_create_stream_ducking()
+ * @see sound_manager_destroy_stream_ducking()
+ * @see sound_manager_is_ducked()
+ * @see sound_manager_activate_ducking()
+ */
+int sound_manager_deactivate_ducking(sound_stream_ducking_h stream_ducking);
+
+/**
+ * @brief Destroys the handle for stream ducking.
+ * @since_tizen 5.5
+ *
+ * @param[in]  stream_ducking  The handle of stream ducking
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SOUND_MANAGER_ERROR_NONE Success
+ * @retval #SOUND_MANAGER_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #SOUND_MANAGER_ERROR_INTERNAL Internal error inside the sound system
+ * @see sound_manager_create_stream_ducking()
+ * @see sound_manager_is_ducked()
+ * @see sound_manager_activate_ducking()
+ * @see sound_manager_deactivate_ducking()
+ */
+int sound_manager_destroy_stream_ducking(sound_stream_ducking_h stream_ducking);
+
+/**
  * @}
  */
 
index 0502cd0..2434576 100644 (file)
@@ -159,6 +159,7 @@ if (pthread_mutex_unlock(x_mutex)) { \
 #define SOUND_STREAM_DIRECTION_MAX 2
 #define SOUND_DEVICE_TYPE_LEN 64
 #define SOUND_SAMPLE_FORMAT_LEN 12
+#define SOUND_DUCKING_ARR_MAX 128
 
 #define DIRECTION_OUT_STR         "out"
 #define SOUND_TYPE_MASTER_STR     "master"
@@ -194,14 +195,18 @@ typedef struct _manual_route_info_s {
        bool is_set;
 } manual_route_info_s;
 
+typedef struct _sound_pa_info_s {
+       pa_threaded_mainloop *mainloop;
+       pa_context *context;
+       unsigned int index;
+} sound_pa_info_s;
+
 typedef struct _sound_stream_info_s {
-       unsigned int pa_index;
        int focus_id;
        char *stream_type;
        bool is_focus_unavailable;
        bool is_requesting;
-       pa_threaded_mainloop *pa_mainloop;
-       pa_context *pa_context;
+       sound_pa_info_s pa_info;
        stream_conf_info_s stream_conf_info;
        unsigned int prev_acquired_focus;
        unsigned int acquired_focus;
@@ -212,6 +217,16 @@ typedef struct _sound_stream_info_s {
        pthread_mutex_t focus_cb_mutex;
 } sound_stream_info_s;
 
+typedef struct _sound_stream_ducking_s {
+       char *target_stream;
+       bool is_ducked;
+       unsigned int duration;
+       double ratio;
+       sound_pa_info_s pa_info;
+       sound_stream_ducking_state_changed_cb user_cb;
+       void *user_data;
+} sound_stream_ducking_s;
+
 typedef enum {
        _VSTREAM_STATE_READY,
        _VSTREAM_STATE_RUNNING,
@@ -313,8 +328,12 @@ void _pa_context_state_cb(pa_context *c, void *userdata);
 
 void _pa_stream_state_cb(pa_stream *s, void * userdata);
 
+int _make_pa_connection(sound_pa_info_s *pa_info, const char *context_name);
+
 int _make_pa_connection_and_register_focus(sound_stream_info_s *stream_h, sound_stream_focus_state_changed_cb callback, void *user_data);
 
+void _destroy_pa_connection(sound_pa_info_s *pa_info);
+
 int _destroy_pa_connection_and_unregister_focus(sound_stream_info_s *stream_h);
 
 int _add_device_for_stream_routing(sound_stream_info_s *stream_info, sound_device_h device);
@@ -343,6 +362,10 @@ int _set_virtual_stream_volume(virtual_sound_stream_info_s *virtual_stream, doub
 
 int _set_acm_master_mode(bool on);
 
+int _activate_ducking(uint32_t stream_index, bool enable, const char *target_stream, uint32_t duration, double ratio);
+
+int _get_ducking_state(uint32_t stream_index, bool *is_ducked);
+
 #ifdef __cplusplus
 }
 #endif
index 1071c84..13b9125 100644 (file)
@@ -1,6 +1,6 @@
 Name:       capi-media-sound-manager
 Summary:    Sound Manager library
-Version:    0.5.30
+Version:    0.5.31
 Release:    0
 Group:      Multimedia/API
 License:    Apache-2.0
index eabfbc5..ce9db51 100644 (file)
 #include "sound_manager.h"
 #include "sound_manager_private.h"
 
+static int ducking_arr_count;
+static unsigned int ducking_cb_subs_id;
+static pthread_mutex_t ducking_mutex;
+static sound_stream_ducking_s *ducking_arr[SOUND_DUCKING_ARR_MAX];
+
 _focus_watch_info_s focus_watch_info_arr[SOUND_STREAM_INFO_ARR_MAX];
 
+static void _ducking_state_changed_cb(int pa_index, bool is_ducked, void *user_data)
+{
+       int i = 0;
+
+       SM_ENTER_CRITICAL_SECTION(&ducking_mutex);
+
+       for (i = 0 ; i < SOUND_DUCKING_ARR_MAX ; i++) {
+               if (ducking_arr[i] && ducking_arr[i]->pa_info.index == pa_index)
+                       break;
+       }
+
+       if (i < SOUND_DUCKING_ARR_MAX) {
+               LOGI("ducking state changed [i:%d,ducked:%u]", pa_index, is_ducked);
+
+               if (ducking_arr[i]->user_cb)
+                       ducking_arr[i]->user_cb((sound_stream_ducking_h)ducking_arr[i], is_ducked, ducking_arr[i]->user_data);
+               else
+                       LOGW("no user callback for pa_index %d", pa_index);
+       } else {
+               LOGE("could not found for pa_index %d", pa_index);
+       }
+
+       SM_LEAVE_CRITICAL_SECTION(&ducking_mutex);
+
+       return;
+}
+
 int sound_manager_get_max_volume(sound_type_e type, int *max)
 {
        const char *volume_type = NULL;
@@ -136,7 +168,7 @@ int sound_manager_create_stream_information(sound_stream_type_e stream_type, sou
                if (ret == MM_ERROR_NONE) {
                        *stream_info = (sound_stream_info_h)stream_h;
                        LOGI("stream_h(%p), pa_index(%u), focus_id(%d), user_cb(%p), ret(0x%x)",
-                               stream_h, stream_h->pa_index, stream_h->focus_id, stream_h->user_cb, ret);
+                               stream_h, stream_h->pa_info.index, stream_h->focus_id, stream_h->user_cb, ret);
                }
        }
 
@@ -325,7 +357,7 @@ int sound_manager_acquire_focus(sound_stream_info_h stream_info, sound_stream_fo
        if (ret == MM_ERROR_NONE) {
                stream_h->acquired_focus |= focus_mask;
                stream_h->prev_acquired_focus |= focus_mask;
-               _update_focus_status(stream_h->pa_index, (unsigned int)stream_h->acquired_focus);
+               _update_focus_status(stream_h->pa_info.index, (unsigned int)stream_h->acquired_focus);
        }
 
        LOGI("acquired_focus[0x%x], prev[0x%x]", stream_h->acquired_focus, stream_h->prev_acquired_focus);
@@ -385,7 +417,7 @@ int sound_manager_release_focus(sound_stream_info_h stream_info, sound_stream_fo
        if (ret == MM_ERROR_NONE) {
                stream_h->acquired_focus &= ~focus_mask;
                stream_h->prev_acquired_focus &= ~focus_mask;
-               _update_focus_status(stream_h->pa_index, (unsigned int)stream_h->acquired_focus);
+               _update_focus_status(stream_h->pa_info.index, (unsigned int)stream_h->acquired_focus);
        }
 
        LOGI("acquired_focus[0x%x], prev[0x%x]", stream_h->acquired_focus, stream_h->prev_acquired_focus);
@@ -457,7 +489,7 @@ int sound_manager_acquire_focus_all(sound_stream_info_h stream_info, int sound_b
        if (ret == MM_ERROR_NONE) {
                stream_h->acquired_focus |= focus_mask;
                stream_h->prev_acquired_focus |= focus_mask;
-               _update_focus_status(stream_h->pa_index, (unsigned int)stream_h->acquired_focus);
+               _update_focus_status(stream_h->pa_info.index, (unsigned int)stream_h->acquired_focus);
        }
 
 LEAVE:
@@ -515,7 +547,7 @@ int sound_manager_release_focus_all(sound_stream_info_h stream_info, int sound_b
        if (ret == MM_ERROR_NONE) {
                stream_h->acquired_focus = 0;
                stream_h->prev_acquired_focus = 0;
-               _update_focus_status(stream_h->pa_index, (unsigned int)stream_h->acquired_focus);
+               _update_focus_status(stream_h->pa_info.index, (unsigned int)stream_h->acquired_focus);
        }
 
 LEAVE:
@@ -616,7 +648,7 @@ int sound_manager_is_stream_on_device(sound_stream_info_h stream_info, sound_dev
        SM_NULL_ARG_CHECK(device);
        SM_NULL_ARG_CHECK(is_on);
 
-       ret = mm_sound_is_stream_on_device(stream_h->pa_index, device, is_on);
+       ret = mm_sound_is_stream_on_device(stream_h->pa_info.index, device, is_on);
 
        return _convert_sound_manager_error_code(__func__, ret);
 }
@@ -1115,3 +1147,196 @@ int sound_manager_remove_device_state_changed_cb(int id)
        return _convert_sound_manager_error_code(__func__, ret);
 }
 
+int sound_manager_create_stream_ducking(sound_stream_type_e target_stream, sound_stream_ducking_state_changed_cb callback, void *user_data, sound_stream_ducking_h *stream_ducking)
+{
+       int i = 0;
+       int ret = MM_ERROR_NONE;
+       sound_stream_ducking_s *new_ducking = NULL;
+
+       SM_NULL_ARG_CHECK(stream_ducking);
+
+       SM_ENTER_CRITICAL_SECTION_WITH_RETURN(&ducking_mutex, SOUND_MANAGER_ERROR_INTERNAL);
+
+       for (i = 0 ; i < SOUND_DUCKING_ARR_MAX ; i++)
+               if (ducking_arr[i] == NULL)
+                       break;
+
+       if (i == SOUND_DUCKING_ARR_MAX) {
+               LOGE("ducking array is full");
+               ret = MM_ERROR_SOUND_INTERNAL;
+               goto LEAVE;
+       }
+
+       new_ducking = (sound_stream_ducking_s *)calloc(1, sizeof(sound_stream_ducking_s));
+       if (!new_ducking) {
+               ret = MM_ERROR_OUT_OF_MEMORY;
+               goto LEAVE;
+       }
+
+       ret = _convert_stream_type(target_stream, &new_ducking->target_stream);
+       if (ret != MM_ERROR_NONE)
+               goto LEAVE;
+
+       ret = _make_pa_connection(&new_ducking->pa_info, "SOUND_MANAGER_STREAM_DUCKING");
+
+LEAVE:
+       if (ret == MM_ERROR_NONE) {
+               if (++ducking_arr_count == 1) {
+                       /* subscribe ducking finished signal */
+                       ret = mm_sound_add_ducking_state_changed_callback((mm_sound_ducking_state_changed_cb)_ducking_state_changed_cb,
+                               NULL, &ducking_cb_subs_id);
+                       if (ret != MM_ERROR_NONE) {
+                               ducking_arr_count = 0;
+                               goto LEAVE;
+                       }
+
+                       LOGI("ducking state changed cb subs id %d", ducking_cb_subs_id);
+               }
+
+               new_ducking->user_cb = callback;
+               new_ducking->user_data = user_data;
+
+               ducking_arr[i] = new_ducking;
+               *stream_ducking = (sound_stream_ducking_h)new_ducking;
+
+               LOGI("new stream_ducking(%p), target_stream(%s), pa_index(%u), user_cb(%p)",
+                       new_ducking, new_ducking->target_stream, new_ducking->pa_info.index, new_ducking->user_cb);
+       } else {
+               if (new_ducking) {
+                       _destroy_pa_connection(&new_ducking->pa_info);
+                       free(new_ducking);
+               }
+       }
+
+       SM_LEAVE_CRITICAL_SECTION(&ducking_mutex);
+
+       return _convert_sound_manager_error_code(__func__, ret);
+}
+
+int sound_manager_destroy_stream_ducking(sound_stream_ducking_h stream_ducking)
+{
+       int i = 0;
+       int ret = MM_ERROR_NONE;
+       bool is_ducked = false;
+       sound_stream_ducking_s *ducking = (sound_stream_ducking_s*)stream_ducking;
+
+       LOGI(">> enter %p", ducking);
+
+       SM_INSTANCE_CHECK(ducking);
+
+       ret = _get_ducking_state(ducking->pa_info.index, &is_ducked);
+       if (ret != MM_ERROR_NONE)
+               return _convert_sound_manager_error_code(__func__, ret);
+
+       SM_ENTER_CRITICAL_SECTION_WITH_RETURN(&ducking_mutex, SOUND_MANAGER_ERROR_INTERNAL);
+
+       if (!is_ducked) {
+               _destroy_pa_connection(&ducking->pa_info);
+       } else {
+               LOGE("ducked now, it should be deactivated first.");
+               ret = MM_ERROR_SOUND_INVALID_STATE;
+       }
+
+       if (ret == MM_ERROR_NONE) {
+               LOGI("destroy stream ducking(%p)", ducking);
+
+               for (i = 0 ; i < SOUND_DUCKING_ARR_MAX ; i++)
+                       if (ducking_arr[i] == ducking)
+                               ducking_arr[i] = NULL;
+
+               if (--ducking_arr_count == 0) {
+                       /* unsubscribe ducking finished signal */
+                       if (mm_sound_remove_ducking_state_changed_callback(ducking_cb_subs_id) != MM_ERROR_NONE)
+                               LOGW("mm_sound_remove_ducking_state_changed_callback(id:%u) failed", ducking_cb_subs_id);
+
+                       ducking_cb_subs_id = 0;
+               }
+
+               free(ducking);
+       }
+
+       SM_LEAVE_CRITICAL_SECTION(&ducking_mutex);
+
+       return _convert_sound_manager_error_code(__func__, ret);
+}
+
+int sound_manager_is_ducked(sound_stream_ducking_h stream_ducking, bool *is_ducked)
+{
+       int ret = MM_ERROR_NONE;
+       sound_stream_ducking_s *ducking = (sound_stream_ducking_s*)stream_ducking;
+
+       LOGI(">> enter %p", ducking);
+
+       SM_INSTANCE_CHECK(ducking);
+       SM_NULL_ARG_CHECK(is_ducked);
+
+       ret = _get_ducking_state(ducking->pa_info.index, is_ducked);
+
+       return _convert_sound_manager_error_code(__func__, ret);
+}
+
+int sound_manager_activate_ducking(sound_stream_ducking_h stream_ducking, unsigned int duration, double ratio)
+{
+       int ret = MM_ERROR_NONE;
+       bool is_ducked = false;
+       sound_stream_ducking_s *ducking = (sound_stream_ducking_s*)stream_ducking;
+
+       if (duration > 3000 || ratio >= 1.0 || ratio <= 0.0) {
+               LOGE("Invalid params : duration(%u) or ratio(%lf)", duration, ratio);
+               return SOUND_MANAGER_ERROR_INVALID_PARAMETER;
+       }
+
+       LOGI(">> enter %p - duration(%u), ratio(%lf)", ducking, duration, ratio);
+
+       SM_INSTANCE_CHECK(ducking);
+
+       ret = _get_ducking_state(ducking->pa_info.index, &is_ducked);
+       if (ret != MM_ERROR_NONE)
+               return _convert_sound_manager_error_code(__func__, ret);
+
+       SM_ENTER_CRITICAL_SECTION_WITH_RETURN(&ducking_mutex, SOUND_MANAGER_ERROR_INTERNAL);
+
+       if (!is_ducked) {
+               ducking->duration = duration;
+               ducking->ratio = ratio;
+
+               ret = _activate_ducking(ducking->pa_info.index,
+                       true, ducking->target_stream, ducking->duration, ducking->ratio);
+       } else {
+               LOGE("already ducked");
+               ret = MM_ERROR_SOUND_INVALID_STATE;
+       }
+
+       SM_LEAVE_CRITICAL_SECTION(&ducking_mutex);
+
+       return _convert_sound_manager_error_code(__func__, ret);
+}
+
+int sound_manager_deactivate_ducking(sound_stream_ducking_h stream_ducking)
+{
+       int ret = MM_ERROR_NONE;
+       bool is_ducked = false;
+       sound_stream_ducking_s *ducking = (sound_stream_ducking_s*)stream_ducking;
+
+       LOGI(">> enter %p", ducking);
+
+       SM_INSTANCE_CHECK(ducking);
+
+       ret = _get_ducking_state(ducking->pa_info.index, &is_ducked);
+       if (ret != MM_ERROR_NONE)
+               return _convert_sound_manager_error_code(__func__, ret);
+
+       SM_ENTER_CRITICAL_SECTION_WITH_RETURN(&ducking_mutex, SOUND_MANAGER_ERROR_INTERNAL);
+
+       if (is_ducked) {
+               ret = _activate_ducking(ducking->pa_info.index,
+                       false, ducking->target_stream, ducking->duration, ducking->ratio);
+       } else {
+               LOGE("not ducked");
+               ret = MM_ERROR_SOUND_INVALID_STATE;
+       }
+
+       SM_LEAVE_CRITICAL_SECTION(&ducking_mutex);
+
+       return _convert_sound_manager_error_code(__func__, ret);
+}
index a2670a1..106fe93 100644 (file)
@@ -113,7 +113,7 @@ int sound_manager_create_stream_information_internal(sound_stream_type_internal_
                if (!ret) {
                        *stream_info = (sound_stream_info_h)stream_h;
                        LOGI("stream_h(%p), pa_index(%u), focus_id(%d), user_cb(%p), ret(0x%x)",
-                               stream_h, stream_h->pa_index, stream_h->focus_id, stream_h->user_cb, ret);
+                               stream_h, stream_h->pa_info.index, stream_h->focus_id, stream_h->user_cb, ret);
                }
        }
 
@@ -134,7 +134,7 @@ int sound_manager_set_stream_routing_option(sound_stream_info_h stream_info, con
        SM_INSTANCE_CHECK(stream_h);
        SM_NULL_ARG_CHECK(name);
 
-       ret = _set_route_option(stream_h->pa_index, name, value);
+       ret = _set_route_option(stream_h->pa_info.index, name, value);
 
        return _convert_sound_manager_error_code(__func__, ret);
 }
@@ -182,8 +182,8 @@ int sound_manager_get_index_from_stream_information(sound_stream_info_h stream_i
        SM_INSTANCE_CHECK(stream_h);
        SM_NULL_ARG_CHECK(index);
 
-       *index = stream_h->pa_index;
-       LOGI("stream_index[%u]", stream_h->pa_index);
+       *index = stream_h->pa_info.index;
+       LOGI("stream_index[%u]", stream_h->pa_info.index);
 
        return _convert_sound_manager_error_code(__func__, ret);
 }
@@ -692,7 +692,7 @@ int sound_manager_is_stream_on_device_by_id(sound_stream_info_h stream_info, int
        SM_NULL_ARG_CHECK(stream_h);
        SM_NULL_ARG_CHECK(is_on);
 
-       ret = mm_sound_is_stream_on_device_by_id(stream_h->pa_index, device_id, is_on);
+       ret = mm_sound_is_stream_on_device_by_id(stream_h->pa_info.index, device_id, is_on);
 
        return _convert_sound_manager_error_code(__func__, ret);
 }
index 30f2294..225aa4e 100644 (file)
@@ -34,6 +34,8 @@
 #define PA_STREAM_MANAGER_METHOD_NAME_GET_CURRENT_MEDIA_ROUTING_PATH "GetCurrentMediaRoutingPath"
 #define PA_STREAM_MANAGER_METHOD_NAME_UPDATE_FOCUS_STATUS       "UpdateFocusStatus"
 #define PA_STREAM_MANAGER_METHOD_NAME_CHECK_STREAM_EXIST_BY_PID "CheckStreamExistByPid"
+#define PA_STREAM_MANAGER_METHOD_NAME_ACTIVATE_DUCKING          "ActivateDucking"
+#define PA_STREAM_MANAGER_METHOD_NAME_GET_DUCKING_STATE         "GetDuckingState"
 
 #define PA_DEVICE_MANAGER_OBJECT_PATH                           "/org/pulseaudio/DeviceManager"
 #define PA_DEVICE_MANAGER_INTERFACE                             "org.pulseaudio.DeviceManager"
 #define VCONF_PATH_PREFIX_VOLUME                                "file/private/sound/volume/"
 #define VCONF_PATH_MAX                                          64
 
-#define DBUS_ERR_MSG_MAX                                        24
-
 //LCOV_EXCL_START
 int _convert_dbus_error(const char *error_msg)
 {
        int ret = MM_ERROR_NONE;
-       const char ch = '.';
-       char *ret_str;
 
        if (!error_msg)
                return MM_ERROR_SOUND_INTERNAL;
 
-       /* parsing dbus error message, for example,
-        *   GDBus.Error:org.freedesktop.DBus.Error.InvalidArgs:
-        *   org.tizen.multimedia.audio.InvalidArgument */
-       if (!(ret_str = strrchr(error_msg, ch)))
-               return MM_ERROR_SOUND_INTERNAL;
-
-       ret_str = ret_str + 1;
-       if (ret_str[0] == '\0')
-               return MM_ERROR_SOUND_INTERNAL;
-
-       if (!strncmp(ret_str, "InvalidArgument", DBUS_ERR_MSG_MAX))
+       if (strstr(error_msg, "InvalidArgument"))
                ret = MM_ERROR_INVALID_ARGUMENT;
-       else if (!strncmp(ret_str, "InvalidOperation", DBUS_ERR_MSG_MAX))
+       else if (strstr(error_msg, "InvalidOperation"))
                ret = MM_ERROR_SOUND_INVALID_OPERATION;
-       else if (!strncmp(ret_str, "PolicyInternal", DBUS_ERR_MSG_MAX))
+       else if (strstr(error_msg, "PolicyInternal"))
                ret = MM_ERROR_POLICY_INTERNAL;
+       else if (strstr(error_msg, "AccessDenied"))
+               ret = MM_ERROR_SOUND_PERMISSION_DENIED;
        else
                ret = MM_ERROR_SOUND_INTERNAL;
 
-       LOGE("%s => 0x%x", ret_str, ret);
+       LOGE("%s => 0x%x", error_msg, ret);
 
        return ret;
 }
@@ -165,8 +155,6 @@ int _convert_sound_manager_error_code(const char *func, int code)
 
 int _convert_stream_type(sound_stream_type_e stream_type_enum, char **stream_type)
 {
-       int ret = MM_ERROR_NONE;
-
        if (stream_type == NULL)
                return MM_ERROR_INVALID_ARGUMENT;
 
@@ -204,13 +192,13 @@ int _convert_stream_type(sound_stream_type_e stream_type_enum, char **stream_typ
        default:
                //LCOV_EXCL_START
                LOGE("could not find the stream_type[%d] in this switch case statement", stream_type_enum);
-               ret = MM_ERROR_SOUND_INTERNAL;
-               break;
+               return MM_ERROR_SOUND_INTERNAL;
                //LCOV_EXCL_STOP
        }
+
        LOGI("stream_type[%s]", *stream_type);
 
-       return ret;
+       return MM_ERROR_NONE;
 }
 
 //LCOV_EXCL_START
@@ -765,7 +753,7 @@ void _focus_state_change_callback(int index, mm_sound_focus_type_e focus_type, m
                stream_info->acquired_focus |= focus_type;
 
        if (state == FOCUS_IS_ACQUIRED)
-               _update_focus_status(stream_info->pa_index, (unsigned int)stream_info->acquired_focus);
+               _update_focus_status(stream_info->pa_info.index, (unsigned int)stream_info->acquired_focus);
 
        LOGI("[FOCUS USER CALLBACK(%p) START]", stream_info->user_cb);
        stream_info->user_cb((sound_stream_info_h)stream_info, focus_type, state, change_reason,
@@ -773,7 +761,7 @@ void _focus_state_change_callback(int index, mm_sound_focus_type_e focus_type, m
        LOGI("[FOCUS USER CALLBACK(%p) END]", stream_info->user_cb);
 
        if (state == FOCUS_IS_RELEASED)
-               _update_focus_status(stream_info->pa_index, (unsigned int)stream_info->acquired_focus);
+               _update_focus_status(stream_info->pa_info.index, (unsigned int)stream_info->acquired_focus);
 
        if (state == FOCUS_IS_RELEASED)
                stream_info->prev_acquired_focus &= ~focus_type;
@@ -823,17 +811,17 @@ LEAVE:
 void _pa_context_state_cb(pa_context *c, void *userdata)
 {
        pa_context_state_t state;
-       sound_stream_info_s *stream_info_h = (sound_stream_info_s*)userdata;
+       sound_pa_info_s *pa_info = (sound_pa_info_s *)userdata;
 
        assert(c);
 
        state = pa_context_get_state(c);
-       LOGI("[%p] context state = [%d]", stream_info_h, state);
+       LOGI("[%p] context state = [%d]", pa_info, state);
        switch (state) {
        case PA_CONTEXT_READY:
        case PA_CONTEXT_TERMINATED:
        case PA_CONTEXT_FAILED:
-               pa_threaded_mainloop_signal(stream_info_h->pa_mainloop, 0);
+               pa_threaded_mainloop_signal(pa_info->mainloop, 0);
                break;
        case PA_CONTEXT_UNCONNECTED:
        case PA_CONTEXT_CONNECTING:
@@ -1956,61 +1944,82 @@ LEAVE:
 }
 //LCOV_EXCL_STOP
 
-int _make_pa_connection_and_register_focus(sound_stream_info_s *stream_h, sound_stream_focus_state_changed_cb callback, void *user_data)
+int _make_pa_connection(sound_pa_info_s *pa_info, const char *context_name)
 {
-       int ret = MM_ERROR_NONE;
        int pa_ret = PA_OK;
-       int i = 0;
-       bool is_focus_cb_thread = false;
-
-       if ((ret = mm_sound_focus_is_cb_thread(&is_focus_cb_thread, NULL)))
-               return ret;
-
-       if (is_focus_cb_thread)
-               return MM_ERROR_SOUND_INVALID_OPERATION;
-
-       /* get configuration information of this stream type */
-       if ((ret = _get_stream_conf_info(stream_h->stream_type, &stream_h->stream_conf_info)))
-               return ret;
 
-       LOGI("stream_conf_info : stream type[%s], priority[%d], route type[%d]",
-               stream_h->stream_type, stream_h->stream_conf_info.priority, stream_h->stream_conf_info.route_type);
+       SM_NULL_ARG_CHECK_FOR_PRIV(pa_info);
 
-       if (!(stream_h->pa_mainloop = pa_threaded_mainloop_new()))
+       if (!(pa_info->mainloop = pa_threaded_mainloop_new()))
                goto PA_ERROR;
 
-       if (!(stream_h->pa_context = pa_context_new(pa_threaded_mainloop_get_api(stream_h->pa_mainloop), "SOUND_MANAGER_STREAM_INFO")))
+       if (!(pa_info->context = pa_context_new(pa_threaded_mainloop_get_api(pa_info->mainloop), context_name)))
                goto PA_ERROR;
 
-       pa_context_set_state_callback(stream_h->pa_context, _pa_context_state_cb, stream_h);
+       pa_context_set_state_callback(pa_info->context, _pa_context_state_cb, pa_info);
 
-       if (pa_context_connect(stream_h->pa_context, NULL, 0, NULL) < 0) {
-               pa_ret = pa_context_errno(stream_h->pa_context);//LCOV_EXCL_LINE
+       if (pa_context_connect(pa_info->context, NULL, 0, NULL) < 0) {
+               pa_ret = pa_context_errno(pa_info->context);//LCOV_EXCL_LINE
                goto PA_ERROR;
        }
 
-       pa_threaded_mainloop_lock(stream_h->pa_mainloop);
+       pa_threaded_mainloop_lock(pa_info->mainloop);
 
-       if (pa_threaded_mainloop_start(stream_h->pa_mainloop) < 0)
+       if (pa_threaded_mainloop_start(pa_info->mainloop) < 0)
                goto PA_ERROR_WITH_UNLOCK;
 
        /* wait for ready state of the context */
        for (;;) {
                pa_context_state_t state;
-               state = pa_context_get_state(stream_h->pa_context);
+               state = pa_context_get_state(pa_info->context);
                if (state == PA_CONTEXT_READY)
                        break;
                if (!PA_CONTEXT_IS_GOOD(state)) {
-                       pa_ret = pa_context_errno(stream_h->pa_context);//LCOV_EXCL_LINE
+                       pa_ret = pa_context_errno(pa_info->context);//LCOV_EXCL_LINE
                        goto PA_ERROR_WITH_UNLOCK;
                }
-               pa_threaded_mainloop_wait(stream_h->pa_mainloop);
+               pa_threaded_mainloop_wait(pa_info->mainloop);
        }
 
        /* get index of this context */
-       stream_h->pa_index = pa_context_get_index(stream_h->pa_context);
+       pa_info->index = pa_context_get_index(pa_info->context);
 
-       pa_threaded_mainloop_unlock(stream_h->pa_mainloop);
+       pa_threaded_mainloop_unlock(pa_info->mainloop);
+
+       return MM_ERROR_NONE;
+//LCOV_EXCL_START
+PA_ERROR_WITH_UNLOCK:
+       pa_threaded_mainloop_unlock(pa_info->mainloop);
+
+PA_ERROR:
+       _destroy_pa_connection(pa_info);
+       LOGE("pa_ret %d", pa_ret);
+
+       return MM_ERROR_SOUND_INTERNAL;
+//LCOV_EXCL_STOP
+}
+
+int _make_pa_connection_and_register_focus(sound_stream_info_s *stream_h, sound_stream_focus_state_changed_cb callback, void *user_data)
+{
+       int ret = MM_ERROR_NONE;
+       int i = 0;
+       bool is_focus_cb_thread = false;
+
+       if ((ret = mm_sound_focus_is_cb_thread(&is_focus_cb_thread, NULL)))
+               return ret;
+
+       if (is_focus_cb_thread)
+               return MM_ERROR_SOUND_INVALID_OPERATION;
+
+       /* get configuration information of this stream type */
+       if ((ret = _get_stream_conf_info(stream_h->stream_type, &stream_h->stream_conf_info)))
+               return ret;
+
+       LOGI("stream_conf_info : stream type[%s], priority[%d], route type[%d]",
+               stream_h->stream_type, stream_h->stream_conf_info.priority, stream_h->stream_conf_info.route_type);
+
+       if ((ret = _make_pa_connection(&stream_h->pa_info, "SOUND_MANAGER_STREAM_INFO")))
+               goto ERROR;
 
        /* register focus */
        if (!stream_h->is_focus_unavailable) {
@@ -2021,15 +2030,12 @@ int _make_pa_connection_and_register_focus(sound_stream_info_s *stream_h, sound_
                } else {
                        LOGE("failed to register focus, ret(0x%x)", ret);//LCOV_EXCL_LINE
                        /* disconnect */
-                       goto PA_ERROR;
+                       goto ERROR;
                }
        }
        goto SUCCESS;
 //LCOV_EXCL_START
-PA_ERROR_WITH_UNLOCK:
-       pa_threaded_mainloop_unlock(stream_h->pa_mainloop);
-
-PA_ERROR:
+ERROR:
        for (i = 0; i < AVAIL_DEVICES_MAX; i++) {
                SM_SAFE_FREE(stream_h->stream_conf_info.avail_in_devices[i]);
                SM_SAFE_FREE(stream_h->stream_conf_info.avail_out_devices[i]);
@@ -2039,22 +2045,40 @@ PA_ERROR:
 
        SM_SAFE_FREE(stream_h->stream_conf_info.volume_type);
 
-       if (stream_h->pa_context) {
-               pa_context_disconnect(stream_h->pa_context);
-               pa_context_unref(stream_h->pa_context);
-               stream_h->pa_context = NULL;
-       }
-       if (stream_h->pa_mainloop) {
-               pa_threaded_mainloop_free(stream_h->pa_mainloop);
-               stream_h->pa_mainloop = NULL;
-       }
+       _destroy_pa_connection(&stream_h->pa_info);
+
        ret = MM_ERROR_SOUND_INTERNAL;
-       LOGE("pa_ret(%d), ret(0x%x)", pa_ret, ret);
 //LCOV_EXCL_STOP
 SUCCESS:
        return ret;
 }
 
+void _destroy_pa_connection(sound_pa_info_s *pa_info)
+{
+       if (!pa_info) {
+               LOGW("NULL pa info - skip..");
+               return;
+       }
+
+       LOGI("[%p][%p]", pa_info->mainloop, pa_info->context);
+
+       if (pa_info->mainloop)
+               pa_threaded_mainloop_stop(pa_info->mainloop);
+
+       if (pa_info->context) {
+               pa_context_disconnect(pa_info->context);
+               pa_context_unref(pa_info->context);
+               pa_info->context = NULL;
+       }
+
+       if (pa_info->mainloop) {
+               pa_threaded_mainloop_free(pa_info->mainloop);
+               pa_info->mainloop = NULL;
+       }
+
+       return;
+}
+
 int _destroy_pa_connection_and_unregister_focus(sound_stream_info_s *stream_h)
 {
        int i = 0;
@@ -2068,19 +2092,7 @@ int _destroy_pa_connection_and_unregister_focus(sound_stream_info_s *stream_h)
        if (is_focus_cb_thread)
                return MM_ERROR_SOUND_INVALID_OPERATION;
 
-       if (stream_h->pa_mainloop)
-               pa_threaded_mainloop_stop(stream_h->pa_mainloop);
-
-       if (stream_h->pa_context) {
-               pa_context_disconnect(stream_h->pa_context);
-               pa_context_unref(stream_h->pa_context);
-               stream_h->pa_context = NULL;
-       }
-
-       if (stream_h->pa_mainloop) {
-               pa_threaded_mainloop_free(stream_h->pa_mainloop);
-               stream_h->pa_mainloop = NULL;
-       }
+       _destroy_pa_connection(&stream_h->pa_info);
 
        /* unregister focus */
        if (!stream_h->is_focus_unavailable) {
@@ -2399,7 +2411,7 @@ int _apply_stream_routing(sound_stream_info_s *stream_info)
        for (i = 0; i < AVAIL_DEVICES_MAX; i++) {
                if (stream_info->manual_route_info.route_in_devices[i] ||
                    stream_info->manual_route_info.route_out_devices[i])
-                       return _set_manual_route_info(stream_info->pa_index, &stream_info->manual_route_info);
+                       return _set_manual_route_info(stream_info->pa_info.index, &stream_info->manual_route_info);
        }
 
        return MM_ERROR_SOUND_INVALID_STATE;
@@ -2438,11 +2450,11 @@ int _create_virtual_stream(sound_stream_info_s *stream_info, virtual_sound_strea
 
        memset((*virtual_stream), 0, sizeof(virtual_sound_stream_info_s));
        (*virtual_stream)->stream_type = stream_info->stream_type;
-       (*virtual_stream)->pa_mainloop = stream_info->pa_mainloop;
-       (*virtual_stream)->pa_context = stream_info->pa_context;
+       (*virtual_stream)->pa_mainloop = stream_info->pa_info.mainloop;
+       (*virtual_stream)->pa_context = stream_info->pa_info.context;
        (*virtual_stream)->pa_proplist = pa_proplist_new();
        pa_proplist_sets((*virtual_stream)->pa_proplist, PA_PROP_MEDIA_ROLE, (*virtual_stream)->stream_type);
-       pa_proplist_setf((*virtual_stream)->pa_proplist, PA_PROP_MEDIA_PARENT_ID, "%u", stream_info->pa_index);
+       pa_proplist_setf((*virtual_stream)->pa_proplist, PA_PROP_MEDIA_PARENT_ID, "%u", stream_info->pa_info.index);
        (*virtual_stream)->state = _VSTREAM_STATE_READY;
        (*virtual_stream)->stream_info = stream_info;
 
@@ -2704,3 +2716,101 @@ LEAVE:
        return ret;
 }
 //LCOV_EXCL_STOP
+
+int _activate_ducking(uint32_t stream_index, bool enable, const char *target_stream, uint32_t duration, double ratio)
+{
+       int ret = MM_ERROR_NONE;
+       GDBusConnection *conn = NULL;
+       GError *err = NULL;
+       GVariant *result = NULL;
+       const gchar *dbus_ret = NULL;
+
+       if ((ret = __get_dbus_connection(&conn)))
+               return ret;
+
+       result = g_dbus_connection_call_sync(conn,
+               PA_BUS_NAME,
+               PA_STREAM_MANAGER_OBJECT_PATH,
+               PA_STREAM_MANAGER_INTERFACE,
+               PA_STREAM_MANAGER_METHOD_NAME_ACTIVATE_DUCKING,
+               g_variant_new("(ubsud)", stream_index, enable, target_stream, duration, ratio),
+               G_VARIANT_TYPE("(s)"),
+               G_DBUS_CALL_FLAGS_NONE,
+               2000,
+               NULL,
+               &err);
+       if (!result || err) {
+               LOGE("g_dbus_connection_call_sync() for ACTIVATE_DUCKING error");
+               ret = _convert_dbus_error(err ? err->message : NULL);
+               if (err)
+                       g_error_free(err);
+               goto LEAVE;
+       }
+
+       g_variant_get(result, "(&s)", &dbus_ret);
+
+       LOGI("g_dbus_connection_call_sync() success, method return value is (%s)", dbus_ret);
+
+       if (strncmp("STREAM_MANAGER_RETURN_OK", dbus_ret, strlen(dbus_ret)))
+               ret = MM_ERROR_SOUND_INTERNAL;
+
+LEAVE:
+       g_variant_unref(result);
+       g_object_unref(conn);
+
+       return ret;
+}
+
+int _get_ducking_state(uint32_t stream_index, bool *is_ducked)
+{
+       int ret = MM_ERROR_NONE;
+       gboolean _is_ducked = FALSE;
+       GDBusConnection *conn = NULL;
+       GError *err = NULL;
+       GVariant *result = NULL;
+       const gchar *dbus_ret = NULL;
+
+       SM_NULL_ARG_CHECK_FOR_PRIV(is_ducked);
+
+       if ((ret = __get_dbus_connection(&conn)))
+               return ret;
+
+       result = g_dbus_connection_call_sync(conn,
+               PA_BUS_NAME,
+               PA_STREAM_MANAGER_OBJECT_PATH,
+               PA_STREAM_MANAGER_INTERFACE,
+               PA_STREAM_MANAGER_METHOD_NAME_GET_DUCKING_STATE,
+               g_variant_new("(u)", stream_index),
+               G_VARIANT_TYPE("(bs)"),
+               G_DBUS_CALL_FLAGS_NONE,
+               1000,
+               NULL,
+               &err);
+       if (!result || err) {
+               LOGE("g_dbus_connection_call_sync() for GET_DUCKING_STATE error");
+               ret = _convert_dbus_error(err ? err->message : NULL);
+               if (err)
+                       g_error_free(err);
+               goto LEAVE;
+       }
+
+       g_variant_get(result, "(b&s)", &_is_ducked, &dbus_ret);
+
+       LOGI("dbus return value is (%s)", dbus_ret);
+
+       if (!strncmp("STREAM_MANAGER_RETURN_OK", dbus_ret, strlen(dbus_ret))) {
+               *is_ducked = (bool)_is_ducked;
+               LOGI("is_ducked %u", *is_ducked);
+       } else {
+               ret = MM_ERROR_SOUND_INTERNAL;
+       }
+
+       if (strncmp("STREAM_MANAGER_RETURN_OK", dbus_ret, strlen(dbus_ret)))
+               ret = MM_ERROR_SOUND_INTERNAL;
+
+LEAVE:
+       g_variant_unref(result);
+       g_object_unref(conn);
+
+       return ret;
+}
index bfa5522..64314b9 100644 (file)
@@ -99,6 +99,11 @@ enum {
        CURRENT_STATUS_GET_MASTER_VOLUME,
        CURRENT_STATUS_SET_ACM_MASTER_MODE,
 #endif
+       CURRENT_STATUS_CREATE_STREAM_DUCKING,
+       CURRENT_STATUS_DESTROY_STREAM_DUCKING,
+       CURRENT_STATUS_ACTIVATE_DUCKING,
+       CURRENT_STATUS_DEACTIVATE_DUCKING,
+       CURRENT_STATUS_GET_DUCKING_STATE,
 };
 
 
@@ -110,6 +115,7 @@ sound_device_h g_device = NULL;
 sound_device_mask_e g_device_mask = SOUND_DEVICE_ALL_MASK;
 sound_stream_info_h g_stream_info_h = NULL;
 virtual_sound_stream_h g_vstream_h = NULL;
+sound_stream_ducking_h g_stream_ducking_h = NULL;
 int g_volume_cb_id;
 int g_device_conn_cb_id;
 int g_device_state_cb_id;
@@ -177,6 +183,13 @@ void focus_watch_callback(int id, sound_stream_focus_mask_e  focus_mask, sound_s
        return;
 }
 
+void ducking_state_changed_cb(sound_stream_ducking_h stream_ducking, bool is_ducked, void *user_data)
+{
+       g_print("stream_ducking %p, is_ducked %u\n", stream_ducking, is_ducked);
+
+       return;
+}
+
 void quit_program()
 {
        g_main_loop_quit(g_loop);
@@ -318,6 +331,16 @@ void _interpret_main_menu(char *cmd)
        else if (strncmp(cmd, "acm", MAX_CMD_LEN) == 0)
                g_menu_state = CURRENT_STATUS_SET_ACM_MASTER_MODE;
 #endif
+       else if (strncmp(cmd, "csd", MAX_CMD_LEN) == 0)
+               g_menu_state = CURRENT_STATUS_CREATE_STREAM_DUCKING;
+       else if (strncmp(cmd, "dsd", MAX_CMD_LEN) == 0)
+               g_menu_state = CURRENT_STATUS_DESTROY_STREAM_DUCKING;
+       else if (strncmp(cmd, "adk", MAX_CMD_LEN) == 0)
+               g_menu_state = CURRENT_STATUS_ACTIVATE_DUCKING;
+       else if (strncmp(cmd, "ddk", MAX_CMD_LEN) == 0)
+               g_menu_state = CURRENT_STATUS_DEACTIVATE_DUCKING;
+       else if (strncmp(cmd, "gds", MAX_CMD_LEN) == 0)
+               g_menu_state = CURRENT_STATUS_GET_DUCKING_STATE;
        else if (strncmp(cmd, "q", MAX_CMD_LEN) == 0) {
                g_print("closing the test suite\n");
                quit_program();
@@ -346,6 +369,11 @@ void display_sub_basic()
        g_print("mgv. *Get Master Volume \t");
        g_print("msv. *Set Master Volume \n");
 #endif
+       g_print("csd. Create Stream Ducking\t");
+       g_print("dsd. Destroy Stream Ducking\n");
+       g_print("adk. Activate Ducking\t\t");
+       g_print("ddk. Deactivate Ducking\n");
+       g_print("gds. Get Ducking State\n");
        g_print("-----------------------------------------------------------------------------------------\n");
        g_print("                                    DEVICE MODULE \n");
        g_print("-----------------------------------------------------------------------------------------\n");
@@ -557,6 +585,18 @@ static void displaymenu()
        else if (g_menu_state == CURRENT_STATUS_SET_ACM_MASTER_MODE)
                g_print("*** input master mode enable or disable (0:disable 1:enable)\n");
 #endif
+       else if (g_menu_state == CURRENT_STATUS_CREATE_STREAM_DUCKING)
+               g_print("*** input target stream (0:MEDIA 1:SYSTEM 2:ALARM 3:NOTIFICATION 4:EMERGENCY 5:VOICE_INFORMATION 6:VOICE_RECOGNITION 7:RINGTONE_VOIP 8:VOIP 9:MEDIA_EXTERNAL_ONLY\n");
+       else if (g_menu_state == CURRENT_STATUS_DESTROY_STREAM_DUCKING)
+               g_print("*** press enter to destroy stream ducking\n");
+       else if (g_menu_state == CURRENT_STATUS_ACTIVATE_DUCKING) {
+               if (flag == 0)
+                       g_print("*** input duration (0 <= duration <= 3000) and ratio (0.0 < ratio < 1.0)\n");
+               flag = 1;
+       } else if (g_menu_state == CURRENT_STATUS_DEACTIVATE_DUCKING)
+               g_print("*** press enter to deactivate ducking\n");
+       else if (g_menu_state == CURRENT_STATUS_GET_DUCKING_STATE)
+               g_print("*** press enter to get ducking state\n");
        else {
                g_print("*** unknown status.\n");
                quit_program();
@@ -2176,6 +2216,88 @@ static void interpret(char *cmd)
                break;
        }
 #endif
+       case CURRENT_STATUS_CREATE_STREAM_DUCKING: {
+               int ret = SOUND_MANAGER_ERROR_NONE;
+               sound_stream_type_e target_stream = SOUND_STREAM_TYPE_MEDIA;
+
+               if (g_stream_ducking_h) {
+                       g_print("fail to create stream ducking, g_stream_ducking_h(%p) is already set\n", g_stream_ducking_h);
+                       reset_menu_state();
+                       break;
+               }
+
+               target_stream = (sound_stream_type_e)atoi(cmd);
+
+               ret = sound_manager_create_stream_ducking(target_stream,
+                       ducking_state_changed_cb, NULL, &g_stream_ducking_h);
+               if (ret)
+                       g_print("fail to sound_manager_create_stream_ducking(t:%u), ret(0x%x)\n", target_stream, ret);
+
+               reset_menu_state();
+               break;
+       }
+       case CURRENT_STATUS_DESTROY_STREAM_DUCKING: {
+               int ret = SOUND_MANAGER_ERROR_NONE;
+               ret = sound_manager_destroy_stream_ducking(g_stream_ducking_h);
+               if (ret)
+                       g_print("fail to sound_manager_destroy_stream_ducking(), ret(0x%x)\n", ret);
+               else
+                       g_stream_ducking_h = NULL;
+
+               reset_menu_state();
+               break;
+       }
+       case CURRENT_STATUS_ACTIVATE_DUCKING: {
+               int ret = SOUND_MANAGER_ERROR_NONE;
+               static int cnt = 0;
+               static unsigned int duration = 0;
+               static double ratio = 0;
+
+               switch (cnt) {
+               case 0:
+                       duration = (unsigned int)atoi(cmd);
+                       cnt++;
+                       break;
+               case 1:
+                       ratio = (double)atof(cmd);
+
+                       ret = sound_manager_activate_ducking(g_stream_ducking_h, duration, ratio);
+                       if (ret)
+                               g_print("fail to activate_ducking(%u,%lf), ret(0x%x)\n", duration, ratio, ret);
+                       else
+                               g_print("success to sound_manager_activate_ducking()\n");
+
+                       cnt = 0;
+                       reset_menu_state();
+                       break;
+               }
+               break;
+       }
+       case CURRENT_STATUS_DEACTIVATE_DUCKING: {
+               int ret = SOUND_MANAGER_ERROR_NONE;
+
+               ret = sound_manager_deactivate_ducking(g_stream_ducking_h);
+               if (ret)
+                       g_print("fail to sound_manager_deactivate_ducking(), ret(0x%x)\n", ret);
+               else
+                       g_print("success to sound_manager_deactivate_ducking()\n");
+
+               reset_menu_state();
+               break;
+       }
+       case CURRENT_STATUS_GET_DUCKING_STATE: {
+               int ret = SOUND_MANAGER_ERROR_NONE;
+               bool is_ducked = false;
+
+               ret = sound_manager_is_ducked(g_stream_ducking_h, &is_ducked);
+               if (ret)
+                       g_print("fail to sound_manager_is_ducked(), ret(0x%x)\n", ret);
+               else
+                       g_print("success to sound_manager_is_ducked() - is_ducked %u\n", is_ducked);
+
+               reset_menu_state();
+               break;
+       }
        }
        g_timeout_add(100, timeout_menu_display, 0);
 }