qahw: test: Enhance and add new functionality to playback test app

- add mp3 & aac compress playback support
- move arguments to linux command like and take all
  args while invoking the test app
- add volume support
- align all the code to c style
- add default parameters

Change-Id: Ie5aa0b741887cddc3d6eb58d2ef5c9c93bc4e22d
diff --git a/qahw_api/test/qahw_playback_test.c b/qahw_api/test/qahw_playback_test.c
index 8426945..f334719 100644
--- a/qahw_api/test/qahw_playback_test.c
+++ b/qahw_api/test/qahw_playback_test.c
@@ -18,188 +18,471 @@
 
 /* Test app to play audio at the HAL layer */
 
+#include <getopt.h>
+#include <pthread.h>
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
 #include <errno.h>
 #include "qahw_api.h"
 #include "qahw_defs.h"
+
 #define nullptr NULL
-#define WAV 1
-#define MP3 2
+FILE * log_file = NULL;
+const char *log_filename = NULL;
+float vol_level = 0.01;
+
+enum {
+    FILE_WAV = 1,
+    FILE_MP3,
+    FILE_AAC,
+    FILE_AAC_ADTS
+};
+
+typedef enum {
+    AAC_LC = 1,
+    AAC_HE_V1,
+    AAC_HE_V2
+} aac_format_type_t;
+
+static pthread_mutex_t write_lock = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t write_cond = PTHREAD_COND_INITIALIZER;
+static pthread_mutex_t drain_lock = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t drain_cond = PTHREAD_COND_INITIALIZER;
+
+
+int async_callback(qahw_stream_callback_event_t event, void *param,
+                  void *cookie)
+{
+    switch (event) {
+    case QAHW_STREAM_CBK_EVENT_WRITE_READY:
+        fprintf(log_file, "QAHW_STREAM_CBK_EVENT_DRAIN_READY\n");
+        pthread_mutex_lock(&write_lock);
+        pthread_cond_signal(&write_cond);
+        pthread_mutex_unlock(&write_lock);
+        break;
+    case QAHW_STREAM_CBK_EVENT_DRAIN_READY:
+        fprintf(log_file, "QAHW_STREAM_CBK_EVENT_DRAIN_READY\n");
+        pthread_mutex_lock(&drain_lock);
+        pthread_cond_signal(&drain_cond);
+        pthread_mutex_unlock(&drain_lock);
+    default:
+        break;
+    }
+    return 0;
+}
+
+
+int write_to_hal(qahw_stream_handle_t* out_handle, char *data,
+              size_t bytes)
+{
+    ssize_t ret;
+    pthread_mutex_lock(&write_lock);
+    qahw_out_buffer_t out_buf;
+
+    memset(&out_buf,0, sizeof(qahw_out_buffer_t));
+    out_buf.buffer = data;
+    out_buf.bytes = bytes;
+
+    ret = qahw_out_write(out_handle, &out_buf);
+    if (ret < 0 || ret == bytes) {
+        fprintf(log_file, "Writing data to hal failed or full write %ld, %ld\n",
+            ret, bytes);
+    } else if (ret != bytes) {
+        fprintf(log_file, "ret %ld, bytes %ld\n", ret, bytes);
+        fprintf(log_file, "Waiting for event write ready\n");
+        pthread_cond_wait(&write_cond, &write_lock);
+        fprintf(log_file, "out of wait for event write ready\n");
+    }
+
+    pthread_mutex_unlock(&write_lock);
+    return ret;
+}
 
 
 /* Play audio from a WAV file.
+ *
+ * Parameters:
+ *  out_stream: A pointer to the output audio stream.
+ *  in_file: A pointer to a SNDFILE object.
+ *  config: A pointer to struct that contains audio configuration data.
+ *
+ * Returns: An int which has a non-negative number on success.
+ */
 
- Parameters:
-   out_stream: A pointer to the output audio stream.
-   in_file: A pointer to a SNDFILE object.
-   config: A pointer to struct that contains audio configuration data.
+int play_file(qahw_stream_handle_t* out_handle, FILE* in_file,
+              bool is_offload) {
+    int rc = 0;
+    int offset = 0;
+    size_t bytes_wanted = 0;
+    size_t write_length = 0;
+    size_t bytes_remaining = 0;
+    size_t bytes_written = 0;
+    size_t bytes_read = 0;
+    char  *data = NULL;
+    qahw_out_buffer_t out_buf;
+    bool exit = false;
 
- Returns: An int which has a non-negative number on success.
-*/
+    if (is_offload) {
+        fprintf(log_file, "Set callback for offload stream\n");
+        qahw_out_set_callback(out_handle, async_callback, NULL);
+    }
 
-int play_file(qahw_stream_handle_t* out_handle, FILE* in_file) {
+    rc = qahw_out_set_volume(out_handle, vol_level, vol_level);
+    if (rc < 0)
+        fprintf(log_file, "unable to set volume");
 
-  int rc = 0;
-  size_t frames_read = 1;
-  size_t bytes_wanted ;
-  char  *data = NULL;
-  qahw_out_buffer_t out_buf;
+    bytes_wanted = qahw_out_get_buffer_size(out_handle);
+    data = (char *) malloc (bytes_wanted);
+    if (data == NULL) {
+        fprintf(log_file, "calloc failed!!\n");
+        return -ENOMEM;
+    }
 
-  bytes_wanted = qahw_out_get_buffer_size(out_handle);
-  data = (char *) malloc (bytes_wanted);
-  if (data == NULL) {
-      printf("calloc failed!!\n");
-      return -ENOMEM;
-  }
+    while (!exit) {
+        if (!bytes_remaining) {
+            bytes_read = fread(data, 1, bytes_wanted, in_file);
+            fprintf(log_file, "fread from file %ld\n", bytes_read);
+            if (bytes_read <= 0) {
+                if (feof(in_file)) {
+                    fprintf(log_file, "End of file");
+                    if (is_offload) {
+                        pthread_mutex_lock(&drain_lock);
+                        if (is_offload) {
+                            qahw_out_drain(out_handle, QAHW_DRAIN_ALL);
+                            pthread_cond_wait(&drain_cond, &drain_lock);
+                            fprintf(log_file, "Out of compress drain\n");
+                        }
+                        pthread_mutex_unlock(&drain_lock);
+                    }
+                } else {
+                    fprintf(log_file, "Error in fread --%d\n", ferror(in_file));
+                    fprintf(stderr, "Error in fread --%d\n", ferror(in_file));
+                }
+                exit = true;
+                continue;
+            }
+            bytes_remaining = write_length = bytes_read;
+        }
 
-  while(frames_read != 0) {
-      frames_read = fread(data, bytes_wanted , 1, in_file);
-      if (frames_read < 1) {
-          if (feof(in_file))
-              break;
-          else
-              printf("Error in fread --%d\n",ferror(in_file));
-      }
-      memset(&out_buf,0, sizeof(qahw_out_buffer_t));
-      out_buf.buffer = data;
-      out_buf.bytes = frames_read * bytes_wanted;
-      rc = qahw_out_write(out_handle, &out_buf);
-      if (rc < 0) {
-          printf("Writing data to hal failed %d \n",rc);
-          break;
-      }
-  }
-  return rc;
+        offset = write_length - bytes_remaining;
+        fprintf(log_file, "bytes_remaining %ld, offset %d, write length %ld\n",
+                bytes_remaining, offset, write_length);
+        bytes_written = write_to_hal(out_handle, data+offset, bytes_remaining);
+        bytes_remaining -= bytes_written;
+        fprintf(log_file, "bytes_written %ld, bytes_remaining %ld\n",
+                bytes_written, bytes_remaining);
+    }
+
+    return rc;
 }
 
-// Prints usage information if input arguments are missing.
-void Usage() {
-    fprintf(stderr, "Usage:hal_play [device] [filename] [filetype]\n"
-            "device: hex value representing the audio device (see "
-            "system/media/audio/include/system/audio.h)\n"
-            "filename must be passed as an argument.\n"
-            "filetype (1:WAV 2:MP3) \n");
+bool is_valid_aac_format_type(aac_format_type_t format_type)
+{
+    bool valid_format_type = false;
+
+    switch (format_type) {
+    case AAC_LC:
+    case AAC_HE_V1:
+    case AAC_HE_V2:
+        valid_format_type = true;
+        break;
+    default:
+        break;
+    }
+    return valid_format_type;
+}
+
+/*
+ * Obtain aac format (refer audio.h) for format type entered.
+ */
+
+audio_format_t get_aac_format(int filetype, aac_format_type_t format_type)
+{
+    audio_format_t aac_format = AUDIO_FORMAT_AAC_ADTS_LC; /* default aac frmt*/
+
+    if (filetype == FILE_AAC_ADTS) {
+        switch (format_type) {
+        case AAC_LC:
+            aac_format = AUDIO_FORMAT_AAC_ADTS_LC;
+            break;
+        case AAC_HE_V1:
+            aac_format = AUDIO_FORMAT_AAC_ADTS_HE_V1;
+            break;
+        case AAC_HE_V2:
+            aac_format = AUDIO_FORMAT_AAC_ADTS_HE_V2;
+            break;
+        default:
+            break;
+        }
+    } else if (filetype == FILE_AAC) {
+        switch (format_type) {
+        case AAC_LC:
+            aac_format = AUDIO_FORMAT_AAC_LC;
+            break;
+        case AAC_HE_V1:
+            aac_format = AUDIO_FORMAT_AAC_HE_V1;
+            break;
+        case AAC_HE_V2:
+            aac_format = AUDIO_FORMAT_AAC_HE_V2;
+            break;
+        default:
+            break;
+        }
+    } else {
+        fprintf(log_file, "Invalid filetype provided %d\n", filetype);
+        fprintf(stderr, "Invalid filetype provided %d\n", filetype);
+    }
+
+    fprintf(log_file, "aac format %d\n", aac_format);
+    return aac_format;
+}
+
+void usage() {
+    printf(" \n Command \n");
+    printf(" \n hal_play_test <file path>    - path of file to be played\n");
+    printf(" \n Options\n");
+    printf(" -r  --sample-rate <sampling rate>         - Required for Non-WAV streams\n");
+    printf("                                             For AAC-HE pls specify half the sample rate\n\n");
+    printf(" -c  --channel count <channels>            - Required for Non-WAV streams\n\n");
+    printf(" -v  --volume <float volume level>         - Volume level float value between 0.0 - 1.0.\n");
+    printf(" -d  --device <decimal value>              - see system/media/audio/include/system/audio.h for device values\n");
+    printf("                                             Optional Argument and Default value is 2, i.e Speaker\n\n");
+    printf(" -t  --file-type <file type>               - 1:WAV 2:MP3 3:AAC 4:AAC_ADTS\n");
+    printf("                                             Required for non WAV formats\n\n");
+    printf(" -a  --aac-type <aac type>                 - Required for AAC streams\n");
+    printf("                                             1: LC 2: HE_V1 3: HE_V2\n\n");
+    printf(" -l  --log-file <FILEPATH>                 - File path for debug msg, to print\n");
+    printf("                                             on console use stdout or 1 \n\n");
+    printf(" \n Examples \n");
+    printf(" hal_play_test /etc/Anukoledenadu.wav     -> plays Wav stream with default params\n\n");
+    printf(" hal_play_test /etc/MateRani.mp3 -t 2 -d 2 -v 0.01 -r 44100 -c 2 \n");
+    printf("                                          -> plays MP3 stream(-t = 2) on speaker device(-d = 2)\n");
+    printf("                                          -> 2 channels and 44100 sample rate\n\n");
+    printf(" hal_play_test /etc/AACLC-71-48000Hz-384000bps.aac  -t 4 -d 2 -v 0.05 -r 48000 -c 2 -a 1 \n");
+    printf("                                          -> plays AAC-ADTS stream(-t = 4) on speaker device(-d = 2)\n");
+    printf("                                          -> AAC format type is LC(-a = 1)\n");
+    printf("                                          -> 2 channels and 48000 sample rate\n\n");
+    printf(" hal_play_test /etc/AACHE-adts-stereo-32000KHz-128000Kbps.aac  -t 4 -d 2 -v 0.05 -r 16000 -c 2 -a 3 \n");
+    printf("                                          -> plays AAC-ADTS stream(-t = 4) on speaker device(-d = 2)\n");
+    printf("                                          -> AAC format type is HE V2(-a = 3)\n");
+    printf("                                          -> 2 channels and 16000 sample rate\n");
+    printf("                                          -> note that the sample rate is half the actual sample rate\n\n");
 }
 
 int main(int argc, char* argv[]) {
-    if (argc < 4) {
-        Usage();
-        return -1;
-  }
-  // Process command line arguments.
-  FILE *filestream = NULL;
-  char header[44] = {0};
-  int sample_rate = 0;
-  int channels = 0;
-  const int audio_device_base = 16;
-  char* filename = nullptr;
-  int filetype;
-  qahw_module_handle_t *qahw_mod_handle;
-  const  char *mod_name = "audio.primary";
 
-  uint32_t desired_output_device = strtol(
-      argv[1], nullptr /* look at full string*/, audio_device_base);
+    FILE *file_stream = NULL;
+    char header[44] = {0};
+    char* filename = nullptr;
+    qahw_module_handle_t *qahw_mod_handle;
+    const char *mod_name = "audio.primary";
+    qahw_stream_handle_t* out_handle = nullptr;
+    int rc = 0;
 
-  filename = argv[2];
-  filetype = atoi (argv[3]);
+    /*
+     * Default values
+     */
+    int filetype = FILE_WAV;
+    int sample_rate = 44100;
+    int channels = 2;
+    const int audio_device_base = 0x2;/* spkr device*/
+    aac_format_type_t format_type = AAC_LC;
+    log_file = stdout;
+    audio_devices_t output_device = AUDIO_DEVICE_OUT_SPEAKER;
 
-  printf("Starting audio hal tests.\n");
-  int rc = 0;
+    struct option long_options[] = {
+        /* These options set a flag. */
+        {"device",        required_argument,    0, 'd'},
+        {"sample-rate",   required_argument,    0, 'r'},
+        {"channels",      required_argument,    0, 'c'},
+        {"volume",        required_argument,    0, 'v'},
+        {"log-file",      required_argument,    0, 'l'},
+        {"file-type",     required_argument,    0, 't'},
+        {"aac-type",      required_argument,    0, 'a'},
+        {"help",          no_argument,          0, 'h'},
+        {0, 0, 0, 0}
+    };
 
-  qahw_mod_handle = qahw_load_module(mod_name);
+    int opt = 0;
+    int option_index = 0;
+    while ((opt = getopt_long(argc,
+                              argv,
+                              "-r:c:d:v:l::t:a:h",
+                              long_options,
+                              &option_index)) != -1) {
+            switch (opt) {
+            case 'r':
+                sample_rate = atoi(optarg);
+                break;
+            case 'c':;
+                channels = atoi(optarg);
+                break;
+            case 'd':
+                output_device = atoi(optarg);
+                break;
+            case 'v':
+                vol_level = atof(optarg);
+                break;
+            case 'l':
+                /*
+                 * Fix Me: unable to log to a given file.
+                 */
+                log_filename = optarg;
+                if((log_file = fopen(log_filename,"wb"))== NULL) {
+                    fprintf(stderr, "Cannot open log file %s\n", log_filename);
+                    /*
+                     * continue to log to std out.
+                     */
+                    log_file = stdout;
+                }
 
- // Set to a high number so it doesn't interfere with existing stream handles
-  audio_io_handle_t handle = 0x999;
-  audio_devices_t output_device =
-      (audio_devices_t)desired_output_device;
-  audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD;
-  audio_config_t config;
+                break;
+            case 't':
+                filetype = atoi(optarg);
+                break;
+            case 'a':
+                format_type = atoi(optarg);
+                break;
+            case 'h':
+                usage();
+                return 0;
+                break;
+         }
+    }
 
-  memset(&config, 0, sizeof(audio_config_t));
+    filename = argv[1];
+    if((file_stream = fopen(filename, "r"))== NULL) {
+        fprintf(stderr, "Cannot Open Audio File %s\n", filename);
+        goto EXIT;
+    }
 
-  if (filename) {
-      printf("filename-----%s\n",filename);
-      filestream = fopen (filename,"r");
-      if (filestream == NULL) {
-          printf("failed to open\n");
-          exit(0);
-      }
-  }
+    /*
+     * Set to a high number so it doesn't interfere with existing stream handles
+     */
 
-  switch (filetype) {
-      case WAV:
-          //Read the wave header
-          rc = fread (header, 44 , 1, filestream);
-          if (rc != 1) {
-              printf("Error .Fread failed\n");
-              exit(0);
-          }
-          if (strncmp (header,"RIFF",4) && strncmp (header+8, "WAVE",4)) {
-              printf("Not a wave format\n");
-              exit (1);
-          }
-          memcpy (&channels, &header[22], 2);
-          memcpy (&sample_rate, &header[24], 4);
-          config.channel_mask = audio_channel_out_mask_from_count(channels);
-          config.offload_info.channel_mask = config.channel_mask;
-          config.offload_info.sample_rate = sample_rate;
-          config.offload_info.format = AUDIO_FORMAT_PCM_16_BIT;
-          break;
-     case MP3:
-          printf("Enter Number of channels:");
-          scanf ("%d",&channels);
-          config.channel_mask = audio_channel_out_mask_from_count(channels);
-          printf("\nEnter Sample Rate:");
-          scanf ("%d",&sample_rate);
-          config.offload_info.channel_mask = config.channel_mask;
-          config.offload_info.sample_rate = sample_rate;
-          config.offload_info.format = AUDIO_FORMAT_MP3;
-          break;
-     default:
-          printf("Does not support given filetype\n");
-          Usage();
-          exit (0);
-  }
-  config.offload_info.version = AUDIO_OFFLOAD_INFO_VERSION_CURRENT;
-  config.offload_info.size = sizeof(audio_offload_info_t);
+    audio_io_handle_t handle = 0x999;
+    audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD;
 
-  printf("Now playing to output_device=%d sample_rate=%d \n",output_device,
-          config.offload_info.sample_rate);
-  const char* stream_name = "output_stream";
+    fprintf(stdout, "Playing:%s\n", filename);
+    fprintf(stdout, "File Type:%d\n", filetype);
+    fprintf(stdout, "Sample Rate:%d\n", sample_rate);
+    fprintf(stdout, "Channels:%d\n", channels);
+    fprintf(stdout, "Log file:%s\n", log_filename);
+    fprintf(stdout, "Volume level:%f\n", vol_level);
+    fprintf(stdout, "Output Device:%d\n", output_device);
+    fprintf(stdout, "Format Type:%d\n", format_type);
 
-  // Open audio output stream.
-  qahw_stream_handle_t* out_handle = nullptr;
-  printf("calling open_out_put_stream:\n");
-  rc = qahw_open_output_stream(qahw_mod_handle, handle, output_device,
-                                        flags, &config, &out_handle,
-                                        stream_name);
-  printf("open output stream is sucess:%d  out_handhle %p\n",rc,out_handle);
-  if (rc) {
-    printf("could not open output stream %d \n",rc);
-    return -1;
-  }
+    fprintf(stdout, "Starting audio hal tests.\n");
 
-  play_file(out_handle, filestream);
+    qahw_mod_handle = qahw_load_module(mod_name);
 
-  // Close output stream and device.
-  rc = qahw_out_standby(out_handle);
-  if (rc) {
-      printf("out standby failed %d \n",rc);
-  }
+    audio_config_t config;
+    memset(&config, 0, sizeof(audio_config_t));
 
-  rc = qahw_close_output_stream(out_handle);
-  if (rc) {
-      printf("could not close output stream %d \n",rc);
-  }
+    switch (filetype) {
+    case FILE_WAV:
+        /*
+         * Read the wave header
+         */
+        rc = fread (header, 44 , 1, file_stream);
+        if (rc != 1) {
+           fprintf(stdout, "Error .Fread failed\n");
+           exit(0);
+        }
+        if (strncmp (header, "RIFF", 4) && strncmp (header+8, "WAVE", 4)) {
+           fprintf(stdout, "Not a wave format\n");
+           exit (1);
+        }
+        memcpy (&channels, &header[22], 2);
+        memcpy (&sample_rate, &header[24], 4);
+        config.channel_mask = audio_channel_out_mask_from_count(channels);
+        config.offload_info.channel_mask = config.channel_mask;
+        config.offload_info.sample_rate = sample_rate;
+        config.offload_info.format = AUDIO_FORMAT_PCM_16_BIT;
+        break;
 
-  rc = qahw_unload_module(qahw_mod_handle);
-  if (rc) {
-      printf("could not unload hal  %d \n",rc);
-      return -1;
-  }
+    case FILE_MP3:
+        config.channel_mask = audio_channel_out_mask_from_count(channels);
+        config.offload_info.channel_mask = config.channel_mask;
+        config.sample_rate = sample_rate;
+        config.offload_info.sample_rate = sample_rate;
+        config.offload_info.format = AUDIO_FORMAT_MP3;
+        flags |= AUDIO_OUTPUT_FLAG_NON_BLOCKING;
+        break;
 
-  printf("Done with hal tests \n");
-  return 0;
+    case FILE_AAC:
+    case FILE_AAC_ADTS:
+        config.channel_mask = audio_channel_out_mask_from_count(channels);
+        config.offload_info.channel_mask = config.channel_mask;
+        config.sample_rate = sample_rate;
+        config.offload_info.sample_rate = sample_rate;
+        if (!is_valid_aac_format_type(format_type)) {
+            fprintf(log_file, "Invalid format type for AAC %d\n", format_type);
+            goto EXIT;
+        }
+        config.offload_info.format = get_aac_format(filetype, format_type);
+        flags |= AUDIO_OUTPUT_FLAG_NON_BLOCKING;
+        break;
+
+
+    default:
+       fprintf(stderr, "Does not support given filetype\n");
+       usage();
+       return 0;
+    }
+    config.offload_info.version = AUDIO_OFFLOAD_INFO_VERSION_CURRENT;
+    config.offload_info.size = sizeof(audio_offload_info_t);
+
+    fprintf(log_file, "Now playing to output_device=%d sample_rate=%d \n"
+        , output_device, config.offload_info.sample_rate);
+    const char* stream_name = "output_stream";
+
+    fprintf(log_file, "calling open_out_put_stream:\n");
+    rc = qahw_open_output_stream(qahw_mod_handle,
+                                 handle,
+                                 output_device,
+                                 flags,
+                                 &config,
+                                 &out_handle,
+                                 stream_name);
+    fprintf(log_file, "open output stream is sucess:%d  out_handhle %p\n"
+        , rc, out_handle);
+    if (rc) {
+        fprintf(stdout, "could not open output stream %d \n", rc);
+        goto EXIT;
+    }
+
+    play_file(out_handle,
+              file_stream,
+             (flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD));
+
+EXIT:
+
+    if (out_handle != nullptr) {
+        rc = qahw_out_standby(out_handle);
+        if (rc) {
+            fprintf(stdout, "out standby failed %d \n", rc);
+        }
+
+        rc = qahw_close_output_stream(out_handle);
+        if (rc) {
+            fprintf(stdout, "could not close output stream %d \n", rc);
+        }
+
+        rc = qahw_unload_module(qahw_mod_handle);
+        if (rc) {
+            fprintf(stdout, "could not unload hal  %d \n", rc);
+            return -1;
+        }
+    }
+
+    if ((log_file != stdout) && (log_file != nullptr))
+        fclose(log_file);
+
+    if (file_stream != nullptr)
+        fclose(file_stream);
+
+    fprintf(stdout, "\nBYE BYE\n");
+    return 0;
 }