audio: qahw_effect_api: Initial version of QTI audio effect HAL

Initial version of QTI audio effect HAL Wrapper.

CRs-Fixed: 1081403
Change-Id: I12291cc7106f7530422891d1bee7e3d4530563c5
diff --git a/qahw_api/test/Makefile.am b/qahw_api/test/Makefile.am
index c269557..46a2ee7 100644
--- a/qahw_api/test/Makefile.am
+++ b/qahw_api/test/Makefile.am
@@ -3,7 +3,8 @@
 PLAY_CPPFLAGS = -Wno-sign-compare
 PLAY_INCLUDES = -I $(top_srcdir)/qahw_api/inc
 
-hal_play_test_SOURCES = qahw_playback_test.c
+hal_play_test_SOURCES = qahw_playback_test.c \
+                        qahw_effect_test.c
 hal_play_test_CPPFLAGS = $(PLAY_CPPFLAGS) $(PLAY_INCLUDES)
 hal_play_test_CPPFLAGS += -D__unused=__attribute__\(\(__unused__\)\)
 hal_play_test_LDADD = -lutils ../libqahw.la
diff --git a/qahw_api/test/qahw_effect_test.c b/qahw_api/test/qahw_effect_test.c
new file mode 100644
index 0000000..d9ce81e
--- /dev/null
+++ b/qahw_api/test/qahw_effect_test.c
@@ -0,0 +1,231 @@
+/*
+* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions are
+* met:
+*     * Redistributions of source code must retain the above copyright
+*       notice, this list of conditions and the following disclaimer.
+*     * Redistributions in binary form must reproduce the above
+*       copyright notice, this list of conditions and the following
+*       disclaimer in the documentation and/or other materials provided
+*       with the distribution.
+*     * Neither the name of The Linux Foundation nor the names of its
+*       contributors may be used to endorse or promote products derived
+*       from this software without specific prior written permission.
+*
+* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+* ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/* effect test to be applied on HAL layer */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+#include "qahw_api.h"
+#include "qahw_defs.h"
+#include "qahw_effect_api.h"
+#include "qahw_effect_bassboost.h"
+#include "qahw_effect_environmentalreverb.h"
+#include "qahw_effect_equalizer.h"
+#include "qahw_effect_presetreverb.h"
+#include "qahw_effect_virtualizer.h"
+#include "qahw_effect_visualizer.h"
+
+#include "qahw_effect_test.h"
+
+thread_func_t effect_thread_funcs[EFFECT_NUM] = {
+    &bassboost_thread_func,
+    &virtualizer_thread_func,
+    &equalizer_thread_func,
+    &visualizer_thread_func,
+    &reverb_thread_func,
+};
+
+const char * effect_str[EFFECT_NUM] = {
+    "bassboost",
+    "virtualizer",
+    "equalizer",
+    "visualizer",
+    "reverb",
+};
+
+void *bassboost_thread_func(void* data) {
+}
+
+void *virtualizer_thread_func(void* data) {
+}
+
+void *equalizer_thread_func(void* data) {
+    thread_data_t            *thr_ctxt = (thread_data_t *)data;
+    qahw_effect_lib_handle_t lib_handle;
+    qahw_effect_handle_t     effect_handle;
+    qahw_effect_descriptor_t effect_desc;
+    int32_t                  rc;
+    int                      reply_data;
+    uint32_t                 reply_size = sizeof(int);
+    uint32_t                 size = (sizeof(qahw_effect_param_t) + 2 * sizeof(int32_t));
+    uint32_t                 buf32[size];
+    qahw_effect_param_t      *param = (qahw_effect_param_t *)buf32;
+    uint32_t                 preset = EQ_PRESET_NORMAL;
+
+    pthread_mutex_lock(&thr_ctxt->mutex);
+    while(!thr_ctxt->exit) {
+        // suspend thread till signaled
+        fprintf(stdout, "suspend effect thread\n");
+        pthread_cond_wait(&thr_ctxt->loop_cond, &thr_ctxt->mutex);
+        fprintf(stdout, "awake effect thread\n");
+
+        switch(thr_ctxt->cmd) {
+        case(EFFECT_LOAD_LIB):
+            lib_handle = qahw_effect_load_library(QAHW_EFFECT_EQUALIZER_LIBRARY);
+            break;
+        case(EFFECT_GET_DESC):
+            rc = qahw_effect_get_descriptor(lib_handle, SL_IID_EQUALIZER_UUID, &effect_desc);
+            if (rc != 0) {
+                fprintf(stderr, "effect_get_descriptor() returns %d\n", rc);
+            }
+            break;
+        case(EFFECT_CREATE):
+            rc = qahw_effect_create(lib_handle, SL_IID_EQUALIZER_UUID,
+                                    thr_ctxt->io_handle, &effect_handle);
+            if (rc != 0) {
+                fprintf(stderr, "effect_create() returns %d\n", rc);
+            }
+            break;
+        case(EFFECT_CMD):
+            if ((thr_ctxt->cmd_code == QAHW_EFFECT_CMD_ENABLE) ||
+                (thr_ctxt->cmd_code == QAHW_EFFECT_CMD_DISABLE)) {
+                thr_ctxt->reply_size = (uint32_t *)&reply_size;
+                thr_ctxt->reply_data = (void *)&reply_data;
+            } else if (thr_ctxt->cmd_code == QAHW_EFFECT_CMD_SET_PARAM) {
+                param->psize = sizeof(int32_t);
+                *(int32_t *)param->data = EQ_PARAM_CUR_PRESET;
+                param->vsize = sizeof(int32_t);
+                memcpy((param->data + param->psize), &preset, param->vsize);
+
+                thr_ctxt->reply_size = (uint32_t *)&reply_size;
+                thr_ctxt->reply_data = (void *)&reply_data;
+                thr_ctxt->cmd_size = size;
+                thr_ctxt->cmd_data = param;
+                preset = (preset + 1) % EQ_PRESET_MAX_NUM; // enumerate through all EQ presets
+            }
+            rc = qahw_effect_command(effect_handle, thr_ctxt->cmd_code,
+                                     thr_ctxt->cmd_size, thr_ctxt->cmd_data,
+                                     thr_ctxt->reply_size, thr_ctxt->reply_data);
+            if (rc != 0) {
+                fprintf(stderr, "effect_command() returns %d\n", rc);
+            }
+            break;
+        case(EFFECT_PROC):
+            //qahw_effect_process();
+            break;
+        case(EFFECT_RELEASE):
+            rc = qahw_effect_release(lib_handle, effect_handle);
+            if (rc != 0) {
+                fprintf(stderr, "effect_release() returns %d\n", rc);
+            }
+            break;
+        case(EFFECT_UNLOAD_LIB):
+            rc = qahw_effect_unload_library(lib_handle);
+            if (rc != 0) {
+                fprintf(stderr, "effect_unload_library() returns %d\n", rc);
+            }
+            break;
+        }
+    }
+    pthread_mutex_unlock(&thr_ctxt->mutex);
+
+    return NULL;
+}
+
+void *visualizer_thread_func(void* data) {
+}
+
+void *reverb_thread_func(void* data) {
+}
+
+thread_data_t *create_effect_thread(thread_func_t func_ptr) {
+    int result;
+
+    thread_data_t *ethread_data = (thread_data_t *)calloc(1, sizeof(thread_data_t));
+    ethread_data->exit = false;
+
+    pthread_attr_init(&ethread_data->attr);
+    pthread_attr_setdetachstate(&ethread_data->attr, PTHREAD_CREATE_JOINABLE);
+    pthread_mutex_init(&ethread_data->mutex, NULL);
+    if (pthread_cond_init(&ethread_data->loop_cond, NULL) != 0) {
+        fprintf(stderr, "pthread_cond_init fails\n");
+        return NULL;
+    }
+    // create effect thread
+    result = pthread_create(&ethread_data->effect_thread, &ethread_data->attr,
+                            func_ptr, ethread_data);
+
+    if (result < 0) {
+        fprintf(stderr, "Could not create effect thread!\n");
+        return NULL;
+    }
+
+    return ethread_data;
+}
+
+void effect_thread_command(thread_data_t *ethread_data,
+                           int cmd, uint32_t cmd_code,
+                           uint32_t cmd_size, void *cmd_data) {
+    if (ethread_data == NULL) {
+        fprintf(stderr, "invalid thread data\n");
+        return;
+    }
+
+    // leave interval to let thread consume the previous cond signal
+    usleep(500000);
+
+    pthread_mutex_lock(&ethread_data->mutex);
+    ethread_data->cmd = cmd;
+    if (cmd_code >= 0) {
+        ethread_data->cmd_code = cmd_code;
+        ethread_data->cmd_size = cmd_size;
+        ethread_data->cmd_data = cmd_data;
+    }
+    pthread_mutex_unlock(&ethread_data->mutex);
+    pthread_cond_signal(&ethread_data->loop_cond);
+
+    return;
+}
+
+void destroy_effect_thread(thread_data_t *ethread_data) {
+    int result;
+
+    if (ethread_data == NULL) {
+        fprintf(stderr, "invalid thread data\n");
+        return;
+    }
+
+    pthread_mutex_lock(&ethread_data->mutex);
+    ethread_data->exit = true;
+    pthread_mutex_unlock(&ethread_data->mutex);
+    pthread_cond_signal(&ethread_data->loop_cond);
+
+    result = pthread_join(ethread_data->effect_thread, NULL);
+    if (result < 0) {
+        fprintf(stderr, "Fail to join effect thread!\n");
+        return;
+    }
+    pthread_mutex_destroy(&ethread_data->mutex);
+    pthread_cond_destroy(&ethread_data->loop_cond);
+
+    return;
+}
diff --git a/qahw_api/test/qahw_effect_test.h b/qahw_api/test/qahw_effect_test.h
new file mode 100644
index 0000000..ffb48ea
--- /dev/null
+++ b/qahw_api/test/qahw_effect_test.h
@@ -0,0 +1,73 @@
+/*
+* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions are
+* met:
+*     * Redistributions of source code must retain the above copyright
+*       notice, this list of conditions and the following disclaimer.
+*     * Redistributions in binary form must reproduce the above
+*       copyright notice, this list of conditions and the following
+*       disclaimer in the documentation and/or other materials provided
+*       with the distribution.
+*     * Neither the name of The Linux Foundation nor the names of its
+*       contributors may be used to endorse or promote products derived
+*       from this software without specific prior written permission.
+*
+* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+* ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include <pthread.h>
+
+#define EFFECT_NUM 5
+typedef void* (*thread_func_t)(void *);
+extern thread_func_t effect_thread_funcs[EFFECT_NUM];
+
+void *bassboost_thread_func(void*);   // thread main of bassboost effect
+void *virtualizer_thread_func(void*); // thread main of virtualizer effect
+void *equalizer_thread_func(void*);   // thread main of equalizer effect
+void *visualizer_thread_func(void*);  // thread main of visualizer effect
+void *reverb_thread_func(void*);      // thread main of reverb effect
+
+typedef struct thread_data {
+    pthread_t         effect_thread;
+    pthread_attr_t    attr;
+    pthread_mutex_t   mutex;
+    pthread_cond_t    loop_cond;
+    audio_io_handle_t io_handle;
+    bool              exit;
+    int               cmd;
+    uint32_t          cmd_code;
+    uint32_t          cmd_size;
+    void              *cmd_data;
+    uint32_t          *reply_size;
+    void              *reply_data;
+} thread_data_t;
+
+extern thread_data_t *create_effect_thread(thread_func_t);
+extern void effect_thread_command(thread_data_t *, int, uint32_t, uint32_t, void *);
+extern void destroy_effect_thread(thread_data_t *);
+
+extern const char *effect_str[EFFECT_NUM];
+
+enum {
+    EFFECT_LOAD_LIB = 1,
+    EFFECT_GET_DESC,
+    EFFECT_CREATE,
+    EFFECT_CMD,
+    EFFECT_PROC,
+    EFFECT_RELEASE,
+    EFFECT_UNLOAD_LIB,
+    EFFECT_EXIT
+};
+
diff --git a/qahw_api/test/qahw_playback_test.c b/qahw_api/test/qahw_playback_test.c
index 86244e8..d539ce2 100644
--- a/qahw_api/test/qahw_playback_test.c
+++ b/qahw_api/test/qahw_playback_test.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
  * Not a Contribution.
  *
  * Copyright (C) 2015 The Android Open Source Project *
@@ -27,6 +27,8 @@
 #include <time.h>
 #include "qahw_api.h"
 #include "qahw_defs.h"
+#include "qahw_effect_api.h"
+#include "qahw_effect_test.h"
 
 #define nullptr NULL
 
@@ -43,6 +45,8 @@
 
 #define FORMAT_PCM 1
 
+static thread_data_t *ethread_data = NULL;
+
 struct wav_header {
     uint32_t riff_id;
     uint32_t riff_sz;
@@ -377,6 +381,11 @@
         bytes_remaining -= bytes_written;
         fprintf(log_file, "bytes_written %zd, bytes_remaining %zd\n",
                 bytes_written, bytes_remaining);
+
+        // set eq preset
+        if (ethread_data) {
+            effect_thread_command(ethread_data, EFFECT_CMD, QAHW_EFFECT_CMD_SET_PARAM, 0, NULL);
+        }
     }
 
     return rc;
@@ -552,6 +561,8 @@
     printf(" -k  --kpi-mode                            - Required for Latency KPI measurement\n");
     printf("                                             file path is not used here as file playback is not done in this mode\n");
     printf("                                             file path and other file specific options would be ignored in this mode.\n\n");
+    printf(" -e  --effect-type <effect type>           - Effect used for test\n");
+    printf("                                             0:bassboost 1:virtualizer 2:equalizer 3:visualizer 4:reverb others:null");
     printf(" \n Examples \n");
     printf(" hal_play_test -f /etc/Anukoledenadu.wav     -> plays Wav stream with default params\n\n");
     printf(" hal_play_test -f /etc/MateRani.mp3 -t 2 -d 2 -v 0.01 -r 44100 -c 2 \n");
@@ -591,6 +602,10 @@
     printf("                                          ->avg_bit_rate,sample_rate,wma_bit_per_sample,wma_block_align\n");
     printf("                                          ->wma_channel_mask,wma_encode_option,wma_format_tag\n");
     printf(" hal_play_test -K -F 4                    -> Measure latency KPIs for low latency output\n\n");
+    printf(" hal_play_test /etc//Moto_320kbps.mp3 -t 2 -d 2 -v 0.1 -r 44100 -c 2 -e 2\n");
+    printf("                                          -> plays MP3 stream(-t = 2) on speaker device(-d = 2)\n");
+    printf("                                          -> 2 channels and 44100 sample rate\n\n");
+    printf("                                          -> sound effect equalizer enabled\n\n");
 }
 
 int main(int argc, char* argv[]) {
@@ -639,12 +654,15 @@
         {"kvpairs",       required_argument,    0, 'k'},
         {"flags",         required_argument,    0, 'F'},
         {"kpi-mode",      no_argument,          0, 'K'},
+        {"effect-path",   required_argument,    0, 'e'},
         {"help",          no_argument,          0, 'h'},
         {0, 0, 0, 0}
     };
 
     int opt = 0;
     int option_index = 0;
+    int effect_index = -1;
+    thread_func_t ethread_func = NULL;
     proxy_params.hdr.riff_id = ID_RIFF;
     proxy_params.hdr.riff_sz = 0;
     proxy_params.hdr.riff_fmt = ID_WAVE;
@@ -660,7 +678,7 @@
     proxy_params.hdr.data_sz = 0;
     while ((opt = getopt_long(argc,
                               argv,
-                              "-f:r:c:b:d:v:l:t:a:w:k:D:KF:h",
+                              "-f:r:c:b:d:v:l:t:a:w:k:D:KF:e:h",
                               long_options,
                               &option_index)) != -1) {
             switch (opt) {
@@ -714,6 +732,14 @@
             case 'F':
                 flags = atoll(optarg);
                 flags_set = true;
+            case 'e':
+                effect_index = atoi(optarg);
+                if (effect_index < 0 || effect_index >= EFFECT_NUM) {
+                    fprintf(stderr, "Invalid effect type %d\n", effect_index);
+                    effect_index = -1;
+                } else {
+                    ethread_func = effect_thread_funcs[effect_index];
+                }
                 break;
             case 'h':
                 usage();
@@ -754,6 +780,9 @@
     if (output_device & AUDIO_DEVICE_OUT_ALL_A2DP)
         fprintf(stdout, "Saving pcm data to file: %s\n", proxy_params.acp.file_name);
 
+    if (effect_index != -1)
+        fprintf(stdout, "Effect type:%s\n", effect_str[effect_index]);
+
     fprintf(stdout, "Starting audio hal tests.\n");
 
     qahw_mod_handle = qahw_load_module(mod_name);
@@ -776,6 +805,7 @@
 
         if (!flags_set)
             flags = AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD|AUDIO_OUTPUT_FLAG_NON_BLOCKING;
+            flags |= AUDIO_OUTPUT_FLAG_DIRECT;
 
         switch (filetype) {
         case FILE_WAV:
@@ -919,16 +949,53 @@
         if (!rc)
             proxy_thread_active = true;
     }
+
+    // create effect thread, use thread_data to transfer command
+    if (ethread_func)
+        ethread_data = create_effect_thread(ethread_func);
+
+    if (ethread_data) {
+        // load effect module
+        effect_thread_command(ethread_data, EFFECT_LOAD_LIB, -1, 0, NULL);
+
+        // get effect desc
+        effect_thread_command(ethread_data, EFFECT_GET_DESC, -1, 0, NULL);
+
+        // create effect
+        ethread_data->io_handle = handle;
+        effect_thread_command(ethread_data, EFFECT_CREATE, -1, 0, NULL);
+
+        // enable effect
+        effect_thread_command(ethread_data, EFFECT_CMD, QAHW_EFFECT_CMD_ENABLE, 0, NULL);
+    }
+
     play_file(out_handle,
               file_stream,
              (flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD));
 
+    if (ethread_data) {
+        // disable effect
+        effect_thread_command(ethread_data, EFFECT_CMD, QAHW_EFFECT_CMD_DISABLE, 0, NULL);
+
+        // release effect
+        effect_thread_command(ethread_data, EFFECT_RELEASE, -1, 0, NULL);
+
+        // unload effect module
+        effect_thread_command(ethread_data, EFFECT_UNLOAD_LIB, -1, 0, NULL);
+
+        // destroy effect thread
+        destroy_effect_thread(ethread_data);
+
+        free(ethread_data);
+        ethread_data = NULL;
+    }
+
     if (proxy_thread_active) {
-        /*
-         * DSP gives drain ack for last buffer which will close proxy thread before
-         * app reads last buffer. So add sleep before exiting proxy thread to read
-         * last buffer of data. This is not a calculated value.
-         */
+       /*
+        * DSP gives drain ack for last buffer which will close proxy thread before
+        * app reads last buffer. So add sleep before exiting proxy thread to read
+        * last buffer of data. This is not a calculated value.
+        */
         usleep(500000);
         proxy_params.acp.thread_exit = true;
         fprintf(log_file, "wait for proxy thread exit\n");