Add to stop recording from FFV
[platform/core/uifw/stt.git] / server / sttd_recorder.c
index 6e0e340..4452096 100644 (file)
@@ -1,5 +1,5 @@
 /*
-*  Copyright (c) 2012, 2013 Samsung Electronics Co., Ltd All Rights Reserved 
+*  Copyright (c) 2011-2016 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
 *  limitations under the License.
 */
 
-#include <audio_io.h> 
+#ifdef TV_PRODUCT
+#define TV_BT_MODE
+#define TV_FFV_MODE
+#endif
+
+#include <audio_io.h>
 #include <Ecore.h>
 #include <math.h>
+#include <pthread.h>
+#include <sound_manager.h>
+#include <sound_manager_internal.h>
+#ifdef TV_BT_MODE
+#include <bluetooth_product.h>
+#endif
+#ifdef TV_FFV_MODE
+#include <farfield-voice-api.h>
+#endif
+
+#include "stt_defs.h"
+#include "sttd_dbus.h"
 #include "sttd_recorder.h"
 #include "sttd_main.h"
-#include "stt_defs.h"
+#include "sttd_engine_agent.h"
+#include "sttd_client_data.h"
 
+#define STTE_AUDIO_ID_NONE             "STT_AUDIO_ID_NONE"             /**< None audio id */
+#ifdef TV_FFV_MODE
+#define STTE_AUDIO_ID_FFV              "STT_FARFIELD_VOICE_VD"
+#endif
 
-#define FRAME_LENGTH 160
+#define FRAME_LENGTH 320
 #define BUFFER_LENGTH FRAME_LENGTH * 2
 
+static pthread_mutex_t sttd_audio_in_handle_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+#ifndef TV_BT_MODE
+static Ecore_Timer* g_audio_timer = NULL;
+#endif
+
+typedef enum {
+       STTD_RECORDER_STATE_NONE = -1,
+       STTD_RECORDER_STATE_READY = 0,  /**< Recorder is ready to start */
+       STTD_RECORDER_STATE_RECORDING   /**< In the middle of recording */
+} sttd_recorder_state;
+
+typedef struct {
+       int                     uid;
+       audio_in_h              audio_h;
+       stte_audio_type_e       audio_type;
+} stt_recorder_s;
+
+static stt_recorder_s* g_recorder = NULL;
+
 static stt_recorder_audio_cb   g_audio_cb;
-static audio_in_h              g_audio_in_h;
 
-static sttd_recorder_state     g_recorder_state = -1;
+static sound_stream_info_h     g_stream_info_h;
+
+static stt_recorder_interrupt_cb       g_interrupt_cb;
 
-static sttp_audio_type_e       g_audio_type;
+static sttd_recorder_state     g_recorder_state = STTD_RECORDER_STATE_NONE;
 
-static char g_buffer[BUFFER_LENGTH];
+static int g_buffer_count;
 
-static FILE* g_pFile_vol;
+static int g_stream_focus_id;
 
-static char g_temp_vol[64];
+static bool g_is_set_audio_type = false;
+
+#ifdef TV_FFV_MODE
+farfield_voice_h g_farfieldvoice_h = NULL;
+#endif
 
-/* Sound buf save */
+/* Sound buf save for test */
 /*
 #define BUF_SAVE_MODE
 */
-
 #ifdef BUF_SAVE_MODE
 static char g_temp_file_name[128] = {'\0',};
 
 static FILE* g_pFile;
 
 static int g_count = 1;
-#endif 
+#endif
+
+#ifdef TV_BT_MODE
+static float get_volume_decibel(char* data, int size, stte_audio_type_e type);
+
+//static stt_recorder_s* __get_recorder(int engine_id);
+
+#define SMART_CONTROL_EXTEND_CMD       0x03
+#define SMART_CONTROL_START_CMD                0x04
+
+static void _bt_cb_hid_state_changed(int result, bool connected, const char *remote_address, void *user_data)
+{
+       SLOG(LOG_DEBUG, TAG_STTD, "[Recorder] Bluetooth Event [%d] Received address [%s]", result, remote_address);
+}
+
+static void _bt_hid_audio_data_receive_cb(bt_hid_voice_data_s *voice_data, void *user_data)
+{
+       if (0 >= sttd_client_get_ref_count()) {
+               if (0 == g_buffer_count % 50) {
+                       SLOG(LOG_INFO, TAG_STTD, "[Recorder INFO] BT audio function callback is invoked, but no cliet");
+
+                       if (100000 == g_buffer_count) {
+                               g_buffer_count = 0;
+                       }
+               }
+               g_buffer_count++;
+               return;
+       }
+
+       if (STTD_RECORDER_STATE_RECORDING != g_recorder_state) {
+               SLOG(LOG_INFO, TAG_STTD, "[Recorder] Exit audio reading normal func");
+               return;
+       }
+
+       if (NULL != g_audio_cb) {
+               if (false == g_is_set_audio_type) {
+                       sttd_engine_agent_set_audio_type(STTE_AUDIO_ID_BLUETOOTH);
+                       g_is_set_audio_type = true;
+               }
+
+               if (0 != g_audio_cb((void*)voice_data->audio_buf, (unsigned int)voice_data->length)) {
+                       SLOG(LOG_ERROR, TAG_STTD, "[Recorder ERROR] Fail to read audio");
+                       sttd_recorder_stop();
+               }
+
+               if (NULL == g_recorder) {
+                       return;
+               }
+
+               float vol_db = get_volume_decibel((char*)voice_data->audio_buf, (int)voice_data->length, g_recorder->audio_type);
+               if (0 != sttdc_send_set_volume(g_recorder->uid, vol_db)) {
+                       SLOG(LOG_ERROR, TAG_STTD, "[Recorder] Fail to send recording volume(%f)", vol_db);
+               }
+       }
+
+       if (0 == g_buffer_count || 0 == g_buffer_count % 50) {
+               SLOG(LOG_WARN, TAG_STTD, "[Recorder][%d] Recording... : read_size(%d)", g_buffer_count, voice_data->length);
+
+               if (100000 == g_buffer_count) {
+                       g_buffer_count = 0;
+               }
+       }
+
+       g_buffer_count++;
+
+#ifdef BUF_SAVE_MODE
+       /* write pcm buffer */
+       fwrite(data, 1, len, g_pFile);
+#endif
+       return;
+}
+#endif
+
+#ifdef TV_FFV_MODE
+static void _ffv_audio_function_cb(void* data, unsigned int length, void* user_data)
+{
+       if (STTD_RECORDER_STATE_RECORDING != g_recorder_state) {
+               SLOG(LOG_INFO, TAG_STTD, "[Recorder] Exit audio reading normal func");
+               return;
+       }
+
+       if (0 >= sttd_client_get_ref_count()) {
+               if (0 == g_buffer_count % 50) {
+                       SLOG(LOG_INFO, TAG_STTD, "[Recorder INFO] farfield audio function callback is invoked, but no cliet");
+
+                       if (100000 == g_buffer_count) {
+                               g_buffer_count = 0;
+                       }
+               }
+               g_buffer_count++;
+               return;
+       }
+
+       if (false == g_is_set_audio_type) {
+               sttd_engine_agent_set_audio_type(STTE_AUDIO_ID_FFV);
+               g_is_set_audio_type = true;
+       }
+
+       if (0 == g_buffer_count % 50) {
+               SLOG(LOG_INFO, TAG_STTD, "[Recorder INFO] farfield audio function callback is invoked");
+
+               if (100000 == g_buffer_count) {
+                       g_buffer_count = 0;
+               }
+       }
+
+       g_buffer_count++;
+
+       if (NULL != g_audio_cb) {
+               if (0 != g_audio_cb(data, length)) {
+                       SLOG(LOG_ERROR, TAG_STTD, "[Recorder ERROR] Fail to read audio");
+                       sttd_recorder_stop();
+               }
+
+               if (NULL == g_recorder) {
+                       SLOG(LOG_INFO, TAG_STTD, "[Recorder INFO] g_recoder is NULL");
+                       return;
+               }
+
+               float vol_db = get_volume_decibel((char*)data, (int)length, g_recorder->audio_type);
+               if (0 != sttdc_send_set_volume(g_recorder->uid, vol_db)) {
+                       SLOG(LOG_ERROR, TAG_STTD, "[Recorder] Fail to send recording volume(%f)", vol_db);
+               }
+       }
+}
+#endif
+
+const char* __stt_get_focus_changed_reason_code(sound_stream_focus_change_reason_e reason)
+{
+       switch (reason) {
+       case SOUND_STREAM_FOCUS_CHANGED_BY_MEDIA:                       return "SOUND_STREAM_FOCUS_CHANGED_BY_MEDIA";
+       case SOUND_STREAM_FOCUS_CHANGED_BY_SYSTEM:                      return "SOUND_STREAM_FOCUS_CHANGED_BY_SYSTEM";
+       case SOUND_STREAM_FOCUS_CHANGED_BY_ALARM:                       return "SOUND_STREAM_FOCUS_CHANGED_BY_ALARM";
+       case SOUND_STREAM_FOCUS_CHANGED_BY_NOTIFICATION:        return "SOUND_STREAM_FOCUS_CHANGED_BY_NOTIFICATION";
+       case SOUND_STREAM_FOCUS_CHANGED_BY_EMERGENCY:           return "SOUND_STREAM_FOCUS_CHANGED_BY_EMERGENCY";
+       case SOUND_STREAM_FOCUS_CHANGED_BY_VOICE_INFORMATION:   return "SOUND_STREAM_FOCUS_CHANGED_BY_VOICE_INFORMATION";
+       case SOUND_STREAM_FOCUS_CHANGED_BY_VOICE_RECOGNITION:   return "SOUND_STREAM_FOCUS_CHANGED_BY_VOICE_RECOGNITION";
+       case SOUND_STREAM_FOCUS_CHANGED_BY_RINGTONE:            return "SOUND_STREAM_FOCUS_CHANGED_BY_RINGTONE";
+       case SOUND_STREAM_FOCUS_CHANGED_BY_VOIP:                        return "SOUND_STREAM_FOCUS_CHANGED_BY_VOIP";
+       case SOUND_STREAM_FOCUS_CHANGED_BY_CALL:                        return "SOUND_STREAM_FOCUS_CHANGED_BY_CALL";
+       case SOUND_STREAM_FOCUS_CHANGED_BY_MEDIA_EXTERNAL_ONLY: return "SOUND_STREAM_FOCUS_CHANGED_BY_MEDIA_EXTERNAL_ONLY";
+       default:                                                                                        return "Undefined reason code";
+       }
+}
+
+void __recorder_focus_state_watch_cb(int id, sound_stream_focus_mask_e focus_mask, sound_stream_focus_state_e focus_state, 
+                                               sound_stream_focus_change_reason_e reason, const char *extra_info, void *user_data)
+{
+       SLOG(LOG_WARN, TAG_STTD, "[Recorder] focus_state_watch_cb is called, focus_state(%d), reason(%s)", (int)focus_state, __stt_get_focus_changed_reason_code(reason));
+
+       if (STTD_RECORDER_STATE_RECORDING == g_recorder_state && SOUND_STREAM_FOCUS_STATE_ACQUIRED == focus_state) {
+               SLOG(LOG_WARN, TAG_STTD, "[Recorder] Focus released as interrupt");
+               if (NULL != g_interrupt_cb) {
+                       g_interrupt_cb();
+               }
+       }
+}
 
-int sttd_recorder_create(stt_recorder_audio_cb callback, sttp_audio_type_e type, int channel, unsigned int sample_rate)
+int sttd_recorder_initialize(stt_recorder_audio_cb audio_cb, stt_recorder_interrupt_cb interrupt_cb)
 {
-       if (NULL == callback) {
+       if (NULL == audio_cb || NULL == interrupt_cb) {
                SLOG(LOG_ERROR, TAG_STTD, "[Recorder ERROR] Input param is NOT valid");
                return STTD_ERROR_INVALID_PARAMETER;
        }
 
+       if (STTD_RECORDER_STATE_NONE != g_recorder_state) {
+               SLOG(LOG_ERROR, TAG_STTD, "[Recorder ERROR] Current state of recorder is recording");
+               return STTD_ERROR_INVALID_STATE;
+       }
+
+       if (0 != pthread_mutex_init(&sttd_audio_in_handle_mutex, NULL)) {
+               SLOG(LOG_ERROR, TAG_STTD, "[Recorder ERROR] Fail to initialize audio in handle mutex.");
+       }
+
+       g_audio_cb = audio_cb;
+       g_interrupt_cb = interrupt_cb;
+       g_recorder_state = STTD_RECORDER_STATE_NONE;
+       g_recorder = NULL;
+
+       if (0 != sound_manager_create_stream_information_internal(SOUND_STREAM_TYPE_VOICE_RECOGNITION_SERVICE, NULL, NULL, &g_stream_info_h)) {
+               SLOG(LOG_ERROR, TAG_STTD, "[Recorder ERROR] Fail to create stream info");
+       }
+
+       if (0 != sound_manager_add_focus_state_watch_cb(SOUND_STREAM_FOCUS_FOR_BOTH, __recorder_focus_state_watch_cb, NULL, &g_stream_focus_id)) {
+               SLOG(LOG_ERROR, TAG_STTD, "[Recorder ERROR] Fail to add focus state watch callback");
+       }
+
+#ifdef TV_FFV_MODE
+       g_farfieldvoice_h = farfield_voice_init();
+       if (NULL == g_farfieldvoice_h) {
+               SLOG(LOG_ERROR, TAG_STTD, "[Recorder ERROR] Fail to init farfield_voice_init");
+       } else {
+               SLOG(LOG_INFO, TAG_STTD, "[Recorder INFO] Register farfield voice audio callback");
+               farfield_voice_register_audio_cb(g_farfieldvoice_h, _ffv_audio_function_cb, NULL);
+       }
+#endif
+
+#ifdef TV_BT_MODE
+       if (BT_ERROR_NONE != bt_product_init()) {
+               SLOG(LOG_ERROR, TAG_STTD, "[Recorder ERROR] Fail to init bt");
+               return STTD_ERROR_OPERATION_FAILED;
+       }
+
+       if (BT_ERROR_NONE != bt_hid_host_initialize(_bt_cb_hid_state_changed, NULL)) {
+               SLOG(LOG_ERROR, TAG_STTD, "[Recorder ERROR] Fail bt_hid_host_initialize()");
+               return STTD_ERROR_OPERATION_FAILED;
+       }
+#endif
+
+       return 0;
+}
+
+int sttd_recorder_deinitialize()
+{
+       if (0 != pthread_mutex_destroy(&sttd_audio_in_handle_mutex)) {
+               SLOG(LOG_ERROR, TAG_STTD, "[Server ERROR] Fail to destroy audio in handle mutex.");
+       }
+
+       if (0 != sound_manager_remove_focus_state_watch_cb(g_stream_focus_id)) {
+               SLOG(LOG_ERROR, TAG_STTD, "[Recorder ERROR] Fail to remove focus state watch callback");
+       }
+
+       if (0 != sound_manager_destroy_stream_information(g_stream_info_h)) {
+               SLOG(LOG_ERROR, TAG_STTD, "[Recorder ERROR] Fail to destroy stream info");
+       }
+
+       if (NULL != g_recorder) {
+               audio_in_destroy(g_recorder->audio_h);
+               g_recorder->audio_h = NULL;
+               free(g_recorder);
+               g_recorder = NULL;
+       }
+
+#ifdef __UNUSED_CODES__
+       /* Remove all recorder */
+       GSList *iter = NULL;
+       stt_recorder_s *recorder = NULL;
+
+       iter = g_slist_nth(g_recorder_list, 0);
+
+       while (NULL != iter) {
+               recorder = iter->data;
+
+               if (NULL != recorder) {
+                       g_recorder_list = g_slist_remove(g_recorder_list, recorder);
+                       if (recorder->audio_h) {
+                               audio_in_destroy(recorder->audio_h);
+                               recorder->audio_h = NULL;
+                       }
+                       free(recorder);
+               }
+
+               iter = g_slist_nth(g_recorder_list, 0);
+       }
+#endif
+
+#ifdef TV_FFV_MODE
+       if (NULL != g_farfieldvoice_h) {
+               SLOG(LOG_INFO, TAG_STTD, "[Recorder INFO] Unregister farfield voice");
+               farfield_voice_unregister_audio_cb(g_farfieldvoice_h);
+               farfield_voice_final(g_farfieldvoice_h);
+               g_farfieldvoice_h = NULL;
+       }
+#endif
+
+#ifdef TV_BT_MODE
+       bt_hid_host_deinitialize();
+
+       bt_product_deinit();
+#endif
+
+       g_recorder_state = STTD_RECORDER_STATE_NONE;
+
+       return 0;
+}
+
+int sttd_recorder_set_audio_session()
+{
+       return 0;
+}
+
+int sttd_recorder_unset_audio_session()
+{
+       return 0;
+}
+
+int sttd_recorder_create(stte_audio_type_e type, int channel, unsigned int sample_rate)
+{
+       /* Check engine id is valid */
+       if (NULL != g_recorder) {
+               SLOG(LOG_WARN, TAG_STTD, "[Recorder WARNING] Engine id is already registered");
+               return STTD_ERROR_INVALID_PARAMETER;
+       }
+
+       audio_in_h temp_in_h = NULL;
+
+#ifndef TV_BT_MODE
        audio_channel_e audio_ch;
        audio_sample_type_e audio_type;
 
-       switch(channel) {
-               case 1: audio_ch = AUDIO_CHANNEL_MONO;          break;
-               case 2: audio_ch = AUDIO_CHANNEL_STEREO;        break;
-               default:
-                       SLOG(LOG_ERROR, TAG_STTD, "[Recorder ERROR] Input channel is not supported");
-                       return STTD_ERROR_OPERATION_FAILED;
-                       break;
+       switch (channel) {
+       case 1: audio_ch = AUDIO_CHANNEL_MONO;          break;
+       case 2: audio_ch = AUDIO_CHANNEL_STEREO;        break;
+       default:
+               SLOG(LOG_ERROR, TAG_STTD, "[Recorder ERROR] Input channel is not supported");
+               return STTD_ERROR_OPERATION_FAILED;
+               break;
        }
 
        switch (type) {
-               case STTP_AUDIO_TYPE_PCM_S16_LE:        audio_type = AUDIO_SAMPLE_TYPE_S16_LE;  break;
-               case STTP_AUDIO_TYPE_PCM_U8:            audio_type = AUDIO_SAMPLE_TYPE_U8;      break;
-               default:        
-                       SLOG(LOG_ERROR, TAG_STTD, "[Recorder ERROR] Invalid Audio Type"); 
-                       return STTD_ERROR_OPERATION_FAILED;
-                       break;
+       case STTE_AUDIO_TYPE_PCM_S16_LE:        audio_type = AUDIO_SAMPLE_TYPE_S16_LE;  break;
+       case STTE_AUDIO_TYPE_PCM_U8:            audio_type = AUDIO_SAMPLE_TYPE_U8;      break;
+       default:
+               SLOG(LOG_ERROR, TAG_STTD, "[Recorder ERROR] Invalid Audio Type");
+               return STTD_ERROR_OPERATION_FAILED;
+               break;
        }
 
        int ret;
-       ret = audio_in_create(sample_rate, audio_ch, audio_type, &g_audio_in_h);
+       ret = audio_in_create(sample_rate, audio_ch, audio_type, &temp_in_h);
        if (AUDIO_IO_ERROR_NONE != ret) {
                SLOG(LOG_ERROR, TAG_STTD, "[Recorder ERROR] Fail to create audio handle : %d", ret);
                return STTD_ERROR_OPERATION_FAILED;
        }
+#else
+       if (BT_ERROR_NONE != bt_hid_set_audio_data_receive_cb(_bt_hid_audio_data_receive_cb, NULL)) {
+               SLOG(LOG_ERROR, TAG_STTD, "[Recorder ERROR] Fail bt_hid_set_audio_data_receive_cb()");
+               return STTD_ERROR_OPERATION_FAILED;
+       }
+#endif
+
+       stt_recorder_s* recorder;
+       recorder = (stt_recorder_s*)calloc(1, sizeof(stt_recorder_s));
+       if (NULL == recorder) {
+#ifndef TV_BT_MODE
+               if (temp_in_h) {
+                       audio_in_destroy(temp_in_h);
+                       temp_in_h = NULL;
+               }
+#endif
+               SLOG(LOG_ERROR, TAG_STTD, "[Recorder ERROR] Fail to allocate memory");
+               return STTD_ERROR_OUT_OF_MEMORY;
+       }
+
+       recorder->uid = -1;
+       recorder->audio_h = temp_in_h;
+       recorder->audio_type = type;
+
+       g_recorder = recorder;
 
-       g_audio_cb = callback;
        g_recorder_state = STTD_RECORDER_STATE_READY;
-       g_audio_type = type;
 
        return 0;
 }
 
 int sttd_recorder_destroy()
 {
+       // critical section required because this function can be called from stt engine thread context
+       SLOG(LOG_WARN, TAG_STTD, "[Recorder WARNING] Enter critical section");
+       pthread_mutex_lock(&sttd_audio_in_handle_mutex);
+
+       /* Check engine id is valid */
+       if (NULL == g_recorder) {
+               SLOG(LOG_WARN, TAG_STTD, "[Recorder WARNING] Engine id is not valid");
+               pthread_mutex_unlock(&sttd_audio_in_handle_mutex);
+               return STTD_ERROR_INVALID_PARAMETER;
+       }
+
+#ifndef TV_BT_MODE
        int ret;
        if (STTD_RECORDER_STATE_RECORDING == g_recorder_state) {
-               ret = audio_in_unprepare(g_audio_in_h);
+               if (g_recorder->audio_h) {
+                       ret = audio_in_unprepare(g_recorder->audio_h);
+                       if (AUDIO_IO_ERROR_NONE != ret) {
+                               SLOG(LOG_ERROR, TAG_STTD, "[Recorder ERROR] Fail to unprepare audioin : %d", ret);
+                       }
+               }
+
+               g_recorder_state = STTD_RECORDER_STATE_READY;
+       }
+
+       if (g_recorder->audio_h) {
+               ret = audio_in_destroy(g_recorder->audio_h);
                if (AUDIO_IO_ERROR_NONE != ret) {
-                       SLOG(LOG_ERROR, TAG_STTD, "[Recorder ERROR] Fail to unprepare audio_in");
+                       SLOG(LOG_ERROR, TAG_STTD, "[Recorder ERROR] Fail to destroy audioin : %d", ret);
                }
+               g_recorder->audio_h = NULL;
        }
 
-       ret = audio_in_destroy(g_audio_in_h);
-       if (AUDIO_IO_ERROR_NONE != ret) {
-               SLOG(LOG_ERROR, TAG_STTD, "[Recorder ERROR] Fail to destroy audio_in");
+#else
+       if (STTD_RECORDER_STATE_RECORDING == g_recorder_state) {
+               g_recorder_state = STTD_RECORDER_STATE_READY;
        }
 
-       g_audio_cb = NULL;
-       g_recorder_state = -1;
+       bt_hid_unset_audio_data_receive_cb();
+#endif
 
-       if (0 == access(STT_AUDIO_VOLUME_PATH, R_OK)) {
-               if (0 == remove(STT_AUDIO_VOLUME_PATH)) {
-                       SLOG(LOG_WARN, TAG_STTD, "[Recorder WARN] Fail to remove volume file"); 
-               }
-       }
+       free(g_recorder);
+       g_recorder = NULL;
+
+       pthread_mutex_unlock(&sttd_audio_in_handle_mutex);
+       SLOG(LOG_WARN, TAG_STTD, "[Recorder WARNING] Leave critical section");
 
        return 0;
 }
 
-static float get_volume_decibel(char* data, int size, sttp_audio_type_e type)
+float get_volume_decibel(char* data, int size, stte_audio_type_e type)
 {
-       #define MAX_AMPLITUDE_MEAN_16 23170.115738161934
-       #define MAX_AMPLITUDE_MEAN_08    89.803909382810
+       #define MAX_AMPLITUDE_MEAN_16   32768
+       #define MAX_AMPLITUDE_MEAN_08   128
 
        int i, depthByte;
        int count = 0;
 
-       short* pcm16 = 0;
-       char* pcm8 = 0;
-
        float db = 0.0;
        float rms = 0.0;
        unsigned long long square_sum = 0;
 
-       if (type == STTP_AUDIO_TYPE_PCM_S16_LE)
+       if (type == STTE_AUDIO_TYPE_PCM_S16_LE)
                depthByte = 2;
        else
                depthByte = 1;
 
        for (i = 0; i < size; i += (depthByte<<1)) {
                if (depthByte == 2) {
-                       pcm16 = (short*)(data + i);
-                       square_sum += (*pcm16) * (*pcm16);
+                       short pcm16 = 0;
+                       memcpy(&pcm16, data + i, sizeof(short));
+                       square_sum += pcm16 * pcm16;
                } else {
-                       pcm8 = (char*)(data +i);
-                       square_sum += (*pcm8) * (*pcm8);
+                       char pcm8 = 0;
+                       memcpy(&pcm8, data + i, sizeof(char));
+                       square_sum += pcm8 * pcm8;
                }
                count++;
        }
 
-       rms = sqrt(square_sum/count);
+       if (0 == count)
+               rms = 0.0;
+       else
+               rms = sqrt((double)square_sum/count);
 
        if (depthByte == 2)
                db = 20 * log10(rms/MAX_AMPLITUDE_MEAN_16);
@@ -158,16 +541,23 @@ static float get_volume_decibel(char* data, int size, sttp_audio_type_e type)
        return db;
 }
 
+#ifndef TV_BT_MODE
 Eina_Bool __read_audio_func(void *data)
 {
        int read_byte = -1;
+       static char g_buffer[BUFFER_LENGTH];
+
+       /* Check engine id is valid */
+       if (NULL == g_recorder) {
+               return EINA_FALSE;
+       }
 
        if (STTD_RECORDER_STATE_READY == g_recorder_state) {
                SLOG(LOG_DEBUG, TAG_STTD, "[Recorder] Exit audio reading func");
                return EINA_FALSE;
        }
 
-       read_byte = audio_in_read(g_audio_in_h, g_buffer, BUFFER_LENGTH);
+       read_byte = audio_in_read(g_recorder->audio_h, g_buffer, BUFFER_LENGTH);
        if (0 > read_byte) {
                SLOG(LOG_WARN, TAG_STTD, "[Recorder WARNING] Fail to read audio : %d", read_byte);
                g_recorder_state = STTD_RECORDER_STATE_READY;
@@ -180,10 +570,26 @@ Eina_Bool __read_audio_func(void *data)
                return EINA_FALSE;
        }
 
-       float vol_db = get_volume_decibel(g_buffer, BUFFER_LENGTH, g_audio_type);
+       if (NULL == g_recorder)
+               SLOG(LOG_WARN, TAG_STTD, "[Recorder] g_recorder is NULL. It is already stopped and destroyed.");
+
+       if (0 == g_buffer_count % 30 && NULL != g_recorder) {
+               float vol_db = get_volume_decibel(g_buffer, BUFFER_LENGTH, g_recorder->audio_type);
+               if (0 != sttdc_send_set_volume(g_recorder->uid, vol_db)) {
+                       SLOG(LOG_ERROR, TAG_STTD, "[Recorder] Fail to send recording volume(%f)", vol_db);
+               }
+       }
 
-       rewind(g_pFile_vol);
-       fwrite((void*)(&vol_db), sizeof(vol_db), 1, g_pFile_vol);
+       /* Audio read log */
+       if (0 == g_buffer_count % 50) {
+               SLOG(LOG_DEBUG, TAG_STTD, "[Recorder][%d] Recording... : read_size(%d)", g_buffer_count, read_byte);
+
+               if (100000 == g_buffer_count) {
+                       g_buffer_count = 0;
+               }
+       }
+
+       g_buffer_count++;
 
 #ifdef BUF_SAVE_MODE
        /* write pcm buffer */
@@ -192,44 +598,79 @@ Eina_Bool __read_audio_func(void *data)
 
        return EINA_TRUE;
 }
+#endif
 
-int sttd_recorder_start()
+int sttd_recorder_start(int uid)
 {
        if (STTD_RECORDER_STATE_RECORDING == g_recorder_state)
                return 0;
 
-       int ret = -1; 
-       ret = audio_in_prepare(g_audio_in_h);
+#ifndef TV_BT_MODE
+       int ret = -1;
+       /* Check engine id is valid */
+       if (NULL == g_recorder) {
+               SLOG(LOG_WARN, TAG_STTD, "[Recorder WARNING] Engine id is not valid");
+               return STTD_ERROR_INVALID_PARAMETER;
+       }
+
+       ret = audio_in_set_sound_stream_info(g_recorder->audio_h, g_stream_info_h);
        if (AUDIO_IO_ERROR_NONE != ret) {
-               SLOG(LOG_ERROR, TAG_STTD, "[Recorder ERROR] Fail to prepare audio in : %d", ret);
-               return STTD_ERROR_OPERATION_FAILED;
+               SLOG(LOG_ERROR, TAG_STTD, "[Recorder ERROR] Fail to set stream info");
        }
 
-       /* Add ecore timer to read audio data */
-       ecore_timer_add(0, __read_audio_func, NULL);
+       ret = audio_in_prepare(g_recorder->audio_h);
+       if (AUDIO_IO_ERROR_NONE != ret) {
+               SLOG(LOG_ERROR, TAG_STTD, "[Recorder ERROR] Fail to start audio : %d", ret);
+               return STTD_ERROR_RECORDER_BUSY;
+       }
 
-       g_recorder_state = STTD_RECORDER_STATE_RECORDING;
+       /* Add ecore timer to read audio data */
+       if (NULL != g_audio_timer) {
+               ecore_timer_del(g_audio_timer);
+               g_audio_timer = NULL;
+       }
 
-       g_pFile_vol = fopen(STT_AUDIO_VOLUME_PATH, "wb+");
-       if (!g_pFile_vol) {
-               SLOG(LOG_ERROR, TAG_STTD, "[Recorder ERROR] Fail to create Volume File");
-               return -1;
+       g_audio_timer = ecore_timer_add(0, __read_audio_func, NULL);
+       if (NULL == g_audio_timer) {
+               SLOG(LOG_WARN, TAG_STTD, "[Recorder WARNING] Fail to create timer of read_audio");
+               return STTD_ERROR_OPERATION_FAILED;
        }
+#endif
 
+       g_recorder_state = STTD_RECORDER_STATE_RECORDING;
+       g_recorder->uid = uid;
+
+       g_buffer_count = 0;
 
 #ifdef BUF_SAVE_MODE
        g_count++;
 
-       snprintf(g_temp_file_name, sizeof(g_temp_file_name), "/tmp/stt_temp_%d_%d", getpid(), g_count);
-       SLOG(LOG_DEBUG, TAG_STTD, "[Recorder] Temp file name=[%s]", g_temp_file_name);
+       while (1) {
+               snprintf(g_temp_file_name, sizeof(g_temp_file_name), "/tmp/stt_temp_%d_%d", getpid(), g_count);
+               ret = access(g_temp_file_name, 0);
+
+               if (0 == ret) {
+                       SLOG(LOG_ERROR, TAG_STTD, "[Recorder ERROR] File is already exist");
+                       if (0 == remove(g_temp_file_name)) {
+                               SLOG(LOG_DEBUG, TAG_STTD, "[Recorder] Remove file");
+                               break;
+                       } else {
+                               g_count++;
+                       }
+               } else {
+                       break;
+               }
+       }
+
+       SECURE_SLOG(LOG_DEBUG, TAG_STTD, "[Recorder] Temp file name=[%s]", g_temp_file_name);
 
        /* open test file */
-       g_pFile = fopen(g_temp_file_name, "wb+");
+       g_pFile = fopen(g_temp_file_name, "wb+x");
        if (!g_pFile) {
                SLOG(LOG_ERROR, TAG_STTD, "[Recorder ERROR] File not found!");
                return -1;
-       }       
-#endif 
+       }
+#endif
 
        return 0;
 }
@@ -239,20 +680,154 @@ int sttd_recorder_stop()
        if (STTD_RECORDER_STATE_READY == g_recorder_state)
                return 0;
 
-       g_recorder_state = STTD_RECORDER_STATE_READY;
+       // critical section required because this function can be called from stt engine thread context
+       SLOG(LOG_WARN, TAG_STTD, "[Recorder WARNING] Enter critical section");
+       pthread_mutex_lock(&sttd_audio_in_handle_mutex);
+
+       g_is_set_audio_type = false;
+       sttd_engine_agent_set_audio_type(STTE_AUDIO_ID_NONE);
+
+       /* Check engine id is valid */
+       if (NULL == g_recorder) {
+               SLOG(LOG_WARN, TAG_STTD, "[Recorder WARNING] Engine id is not valid");
+               pthread_mutex_unlock(&sttd_audio_in_handle_mutex);
+               return STTD_ERROR_INVALID_PARAMETER;
+       }
+
+       g_recorder->uid = -1;
+
+       int ret = -1;
+#ifndef TV_BT_MODE
+       if (NULL != g_audio_timer) {
+               ecore_timer_del(g_audio_timer);
+               g_audio_timer = NULL;
+       }
 
-       int ret = STTD_ERROR_OPERATION_FAILED; 
-       ret = audio_in_unprepare(g_audio_in_h);
+       ret = audio_in_unprepare(g_recorder->audio_h);
        if (AUDIO_IO_ERROR_NONE != ret) {
-               SLOG(LOG_ERROR, TAG_STTD, "[Recorder ERROR] Fail to unprepare audio_in : %d", ret);
+               SLOG(LOG_ERROR, TAG_STTD, "[Recorder ERROR] Fail to unprepare audioin : %d", ret);
        }
+#else
+       int bt_retry = 0;
+       bool stopped = false;
+       while (5 > bt_retry) {
+               ret = bt_hid_rc_stop_sending_voice(NULL);
+               if (BT_ERROR_NONE == ret) {
+                       SLOG(LOG_DEBUG, TAG_STTD, "[Recorder] Stop bt audio");
+                       stopped = true;
+                       break;
+               } else if (BT_ERROR_NOW_IN_PROGRESS == ret) {
+                       SLOG(LOG_ERROR, TAG_STTD, "[Recorder ERROR] Fail bt_hid_rc_stop_sending_voice()");
+                       usleep(50000);
+                       bt_retry++;
+               } else {
+                       SLOG(LOG_ERROR, TAG_STTD, "[Recorder ERROR] bt_hid_rc_stop_sending_voice(): ret(%d)", ret);
+                       break;
+               }
+       }
+       if (false == stopped) {
+               SLOG(LOG_ERROR, TAG_STTD, "[Recorder ERROR] Fail to stop bt audio(%d)", ret);
+//             return STTD_ERROR_OPERATION_FAILED;
+       }
+#endif
+
+#ifdef TV_FFV_MODE
+       ret = farfield_voice_set_recognition_state(FARFIELD_VOICE_RECOGNITION_STOP);
+       if (0 != ret) {
+               SLOG(LOG_ERROR, TAG_STTD, "[Recorder ERROR] Fail to stop FFV, ret(%d)", ret);
+       }
+#endif
 
-       fclose(g_pFile_vol);
+       g_recorder_state = STTD_RECORDER_STATE_READY;
 
 #ifdef BUF_SAVE_MODE
        fclose(g_pFile);
-#endif 
+#endif
+       pthread_mutex_unlock(&sttd_audio_in_handle_mutex);
+       SLOG(LOG_WARN, TAG_STTD, "[Recorder WARNING] Leave critical section");
+
+       return 0;
+}
+
+int sttd_recorder_start_file(int uid, const char *filepath)
+{
+       if (STTD_RECORDER_STATE_RECORDING == g_recorder_state)
+               return 0;
+
+       /* Check engine id is valid */
+       if (NULL == g_recorder) {
+               SLOG(LOG_WARN, TAG_STTD, "[Recorder WARNING] Engine id is not valid");
+               return STTD_ERROR_INVALID_PARAMETER;
+       }
+       g_recorder_state = STTD_RECORDER_STATE_RECORDING;
+       g_recorder->uid = uid;
+
+       int cnt = 0;
+       int totalReadBytes = 0;
+
+       FILE *infile = fopen(filepath, "rb");
+
+       //process the file
+       if (infile != NULL) {
+               while (!feof(infile)) {
+                       static char pcm_buff[BUFFER_LENGTH];
+                       int read_byte = fread(pcm_buff, 1, BUFFER_LENGTH, infile);
+                       totalReadBytes += read_byte;
+                       if (0 != read_byte) {
+                               if (0 != g_audio_cb(pcm_buff, read_byte)) {
+                                       SLOG(LOG_ERROR, TAG_STTD, "[Recorder ERROR] Fail to call audio callback");
+                                       fclose(infile);
+                                       return -1;
+                               }
+                               if (0 == cnt % 30) {
+                                       float vol_db = get_volume_decibel(pcm_buff, BUFFER_LENGTH, g_recorder->audio_type);
+                                       if (0 != sttdc_send_set_volume(g_recorder->uid, vol_db)) {
+                                               SLOG(LOG_ERROR, TAG_STTD, "[Recorder] Fail to send recording volume(%f)", vol_db);
+                                       }
+                               }
+
+                               /* Audio read log */
+                               if (0 == cnt % 50)
+                                       SLOG(LOG_DEBUG, TAG_STTD, "[Recorder][%d] Recording... : read_size(%d)", cnt, read_byte);
+                               cnt++;
+                       }
+               }
+               fclose(infile);
+       }
 
-       SLOG(LOG_ERROR, TAG_STTD, "[Recorder] Recorder stop success");
+       SLOG(LOG_DEBUG, TAG_STTD, "[Recorder][%d] total bytes(%d)", cnt, totalReadBytes);
        return 0;
-}
\ No newline at end of file
+}
+
+int sttd_recorder_stop_file()
+{
+       if (STTD_RECORDER_STATE_READY == g_recorder_state)
+               return 0;
+
+       /* Check engine id is valid */
+       if (NULL == g_recorder) {
+               SLOG(LOG_WARN, TAG_STTD, "[Recorder WARNING] Engine id is not valid");
+               return STTD_ERROR_INVALID_PARAMETER;
+       }
+
+       g_recorder->uid = -1;
+       g_recorder_state = STTD_RECORDER_STATE_READY;
+
+       return 0;
+}
+
+int sttd_recorder_clear()
+{
+#ifdef TV_BT_MODE
+       SLOG(LOG_DEBUG, TAG_STTD, "[Recorder DEBUG] Clear recorder on TV");
+
+       if (STTD_RECORDER_STATE_RECORDING == g_recorder_state) {
+               sttd_recorder_stop();
+       }
+       if (STTD_RECORDER_STATE_READY == g_recorder_state) {
+               sttd_recorder_destroy();
+       }
+#endif
+
+       return STTD_ERROR_NONE;
+}