| /* |
| * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. |
| * Not a Contribution. |
| * |
| * Copyright (C) 2015 The Android Open Source Project * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| /* Test app to play audio at the HAL layer */ |
| |
| #include "qahw_playback_test.h" |
| |
| #define nullptr NULL |
| |
| #define LATENCY_NODE "/sys/kernel/debug/audio_out_latency_measurement_node" |
| #define LATENCY_NODE_INIT_STR "1" |
| |
| #define AFE_PROXY_SAMPLING_RATE 48000 |
| #define AFE_PROXY_CHANNEL_COUNT 2 |
| |
| #define ID_RIFF 0x46464952 |
| #define ID_WAVE 0x45564157 |
| #define ID_FMT 0x20746d66 |
| #define ID_DATA 0x61746164 |
| |
| #define KV_PAIR_MAX_LENGTH 1000 |
| |
| #define FORMAT_PCM 1 |
| #define WAV_HEADER_LENGTH_MAX 128 |
| |
| #define MAX_PLAYBACK_STREAMS 105 //This value is changed to suppport 100 clips in playlist |
| #define PRIMARY_STREAM_INDEX 0 |
| |
| #define KVPAIRS_MAX 100 |
| #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[1])) |
| |
| #define FORMAT_DESCRIPTOR_SIZE 12 |
| #define SUBCHUNK1_SIZE(x) ((8) + (x)) |
| #define SUBCHUNK2_SIZE 8 |
| |
| #define DEFAULT_PRESET_STRENGTH -1 |
| |
| #define DTSHD_CHUNK_HEADER_KEYWORD "DTSHDHDR" |
| #define DTSHD_CHUNK_STREAM_KEYWORD "STRMDATA" |
| #define DTSHD_META_KEYWORD_SIZE 8 /*in bytes */ |
| |
| static ssize_t get_bytes_to_read(FILE* file, int filetype); |
| static void init_streams(void); |
| int pthread_cancel(pthread_t thread); |
| |
| struct wav_header { |
| uint32_t riff_id; |
| uint32_t riff_sz; |
| uint32_t riff_fmt; |
| uint32_t fmt_id; |
| uint32_t fmt_sz; |
| uint16_t audio_format; |
| uint16_t num_channels; |
| uint32_t sample_rate; |
| uint32_t byte_rate; |
| uint16_t block_align; |
| uint16_t bits_per_sample; |
| uint32_t data_id; |
| uint32_t data_sz; |
| }; |
| |
| struct proxy_data { |
| struct audio_config_params acp; |
| struct wav_header hdr; |
| }; |
| |
| struct drift_data { |
| qahw_module_handle_t *out_handle; |
| bool enable_drift_correction; |
| volatile bool thread_exit; |
| }; |
| |
| struct event_data { |
| uint32_t version; |
| uint32_t num_events; |
| uint32_t event_id; |
| uint32_t module_id; |
| uint16_t instance_id; |
| uint16_t reserved; |
| uint32_t config_mask; |
| }; |
| |
| /* Lock for dual main usecase */ |
| pthread_cond_t dual_main_cond; |
| pthread_mutex_t dual_main_lock; |
| bool is_dual_main = false; |
| |
| qahw_module_handle_t *primary_hal_handle = NULL; |
| qahw_module_handle_t *usb_hal_handle = NULL; |
| qahw_module_handle_t *bt_hal_handle = NULL; |
| |
| |
| FILE * log_file = NULL; |
| volatile bool stop_playback = false; |
| const char *log_filename = NULL; |
| struct proxy_data proxy_params; |
| pthread_t playback_thread[MAX_PLAYBACK_STREAMS]; |
| bool thread_active[MAX_PLAYBACK_STREAMS] = { false }; |
| bool qap_wrapper_session_active = false; |
| |
| stream_config stream_param[MAX_PLAYBACK_STREAMS]; |
| bool event_trigger; |
| |
| /* |
| * Set to a high number so it doesn't interfere with existing stream handles |
| */ |
| audio_io_handle_t stream_handle = 0x999; |
| |
| #define FLAC_KVPAIR "music_offload_avg_bit_rate=%d;" \ |
| "music_offload_flac_max_blk_size=%d;" \ |
| "music_offload_flac_max_frame_size=%d;" \ |
| "music_offload_flac_min_blk_size=%d;" \ |
| "music_offload_flac_min_frame_size=%d;" \ |
| "music_offload_sample_rate=%d;" |
| |
| #define ALAC_KVPAIR "music_offload_alac_avg_bit_rate=%d;" \ |
| "music_offload_alac_bit_depth=%d;" \ |
| "music_offload_alac_channel_layout_tag=%d;" \ |
| "music_offload_alac_compatible_version=%d;" \ |
| "music_offload_alac_frame_length=%d;" \ |
| "music_offload_alac_kb=%d;" \ |
| "music_offload_alac_max_frame_bytes=%d;" \ |
| "music_offload_alac_max_run=%d;" \ |
| "music_offload_alac_mb=%d;" \ |
| "music_offload_alac_num_channels=%d;" \ |
| "music_offload_alac_pb=%d;" \ |
| "music_offload_alac_sampling_rate=%d;" \ |
| "music_offload_avg_bit_rate=%d;" \ |
| "music_offload_sample_rate=%d;" |
| |
| #define VORBIS_KVPAIR "music_offload_avg_bit_rate=%d;" \ |
| "music_offload_sample_rate=%d;" \ |
| "music_offload_vorbis_bitstream_fmt=%d;" |
| |
| #define WMA_KVPAIR "music_offload_avg_bit_rate=%d;" \ |
| "music_offload_sample_rate=%d;" \ |
| "music_offload_wma_bit_per_sample=%d;" \ |
| "music_offload_wma_block_align=%d;" \ |
| "music_offload_wma_channel_mask=%d;" \ |
| "music_offload_wma_encode_option=%d;" \ |
| "music_offload_wma_encode_option1=%d;" \ |
| "music_offload_wma_encode_option2=%d;" \ |
| "music_offload_wma_format_tag=%d;" |
| |
| #ifndef AUDIO_OUTPUT_FLAG_ASSOCIATED |
| #define AUDIO_OUTPUT_FLAG_ASSOCIATED 0x10000000 |
| #endif |
| |
| #ifndef AUDIO_OUTPUT_FLAG_INTERACTIVE |
| #define AUDIO_OUTPUT_FLAG_INTERACTIVE 0x80000000 |
| #endif |
| |
| static bool request_wake_lock(bool wakelock_acquired, bool enable) |
| { |
| int system_ret; |
| |
| if (enable) { |
| if (!wakelock_acquired) { |
| system_ret = system("echo audio_services > /sys/power/wake_lock"); |
| if (system_ret < 0) { |
| fprintf(stderr, "%s.Failed to acquire audio_service lock\n", __func__); |
| fprintf(log_file, "%s.Failed to acquire audio_service lock\n", __func__); |
| } else { |
| wakelock_acquired = true; |
| fprintf(log_file, "%s.Success to acquire audio_service lock\n", __func__); |
| } |
| } else |
| fprintf(log_file, "%s.Lock is already acquired\n", __func__); |
| } |
| |
| if (!enable) { |
| if (wakelock_acquired) { |
| system_ret = system("echo audio_services > /sys/power/wake_unlock"); |
| if (system_ret < 0) { |
| fprintf(stderr, "%s.Failed to release audio_service lock\n", __func__); |
| fprintf(log_file, "%s.Failed to release audio_service lock\n", __func__); |
| } else { |
| wakelock_acquired = false; |
| fprintf(log_file, "%s.Success to release audio_service lock\n", __func__); |
| } |
| } else |
| fprintf(log_file, "%s.No Lock is acquired to release\n", __func__); |
| } |
| return wakelock_acquired; |
| } |
| |
| #ifndef AUDIO_FORMAT_AAC_LATM |
| #define AUDIO_FORMAT_AAC_LATM 0x23000000UL |
| #define AUDIO_FORMAT_AAC_LATM_LC (AUDIO_FORMAT_AAC_LATM | AUDIO_FORMAT_AAC_SUB_LC) |
| #define AUDIO_FORMAT_AAC_LATM_HE_V1 (AUDIO_FORMAT_AAC_LATM | AUDIO_FORMAT_AAC_SUB_HE_V1) |
| #define AUDIO_FORMAT_AAC_LATM_HE_V2 (AUDIO_FORMAT_AAC_LATM | AUDIO_FORMAT_AAC_SUB_HE_V2) |
| #endif |
| |
| |
| void stop_signal_handler(int signal __unused) |
| { |
| stop_playback = true; |
| } |
| |
| void usage(); |
| int measure_kpi_values(qahw_stream_handle_t* out_handle, bool is_offload); |
| int tigger_event(qahw_stream_handle_t* out_handle); |
| |
| static void init_streams(void) |
| { |
| int i = 0; |
| for ( i = 0; i < MAX_PLAYBACK_STREAMS; i++) { |
| memset(&stream_param[i], 0, sizeof(stream_config)); |
| |
| stream_param[i].qahw_out_hal_handle = nullptr; |
| stream_param[i].qahw_in_hal_handle = nullptr; |
| stream_param[i].filename = nullptr; |
| stream_param[i].file_stream = nullptr; |
| stream_param[i].filetype = FILE_WAV; |
| stream_param[i].stream_index = i+1; |
| stream_param[i].output_device = AUDIO_DEVICE_OUT_SPEAKER; |
| stream_param[i].input_device = AUDIO_DEVICE_NONE; |
| stream_param[i].flags = AUDIO_OUTPUT_FLAG_NONE; |
| stream_param[i].out_handle = nullptr; |
| stream_param[i].in_handle = nullptr; |
| stream_param[i].channels = 2; |
| stream_param[i].config.offload_info.sample_rate = 44100; |
| stream_param[i].config.offload_info.bit_width = 16; |
| stream_param[i].aac_fmt_type = AAC_LC; |
| stream_param[i].wma_fmt_type = WMA; |
| stream_param[i].kvpair_values = nullptr; |
| stream_param[i].flags_set = false; |
| stream_param[i].usb_mode = USB_MODE_DEVICE; |
| stream_param[i].effect_preset_strength = DEFAULT_PRESET_STRENGTH; |
| stream_param[i].effect_index = -1; |
| stream_param[i].ethread_func = nullptr; |
| stream_param[i].ethread_data = nullptr; |
| stream_param[i].device_url = "stream"; |
| stream_param[i].play_later = false; |
| |
| pthread_mutex_init(&stream_param[i].write_lock, (const pthread_mutexattr_t *)NULL); |
| pthread_cond_init(&stream_param[i].write_cond, (const pthread_condattr_t *) NULL); |
| pthread_mutex_init(&stream_param[i].drain_lock, (const pthread_mutexattr_t *)NULL); |
| pthread_cond_init(&stream_param[i].drain_cond, (const pthread_condattr_t *) NULL); |
| |
| stream_param[i].handle = stream_handle; |
| stream_handle--; |
| } |
| pthread_mutex_init(&dual_main_lock, (const pthread_mutexattr_t *)NULL); |
| pthread_cond_init(&dual_main_cond, (const pthread_condattr_t *) NULL); |
| } |
| |
| void read_kvpair(char *kvpair, char* kvpair_values, int filetype) |
| { |
| char *kvpair_type = NULL; |
| char *token = NULL; |
| int value = 0; |
| int len = 0; |
| int size = 0; |
| |
| switch (filetype) { |
| case FILE_FLAC: |
| kvpair_type = FLAC_KVPAIR; |
| break; |
| case FILE_ALAC: |
| kvpair_type = ALAC_KVPAIR; |
| break; |
| case FILE_VORBIS: |
| kvpair_type = VORBIS_KVPAIR; |
| break; |
| case FILE_WMA: |
| kvpair_type = WMA_KVPAIR; |
| break; |
| default: |
| break; |
| } |
| |
| if (kvpair_type) { |
| token = strtok(kvpair_values, ","); |
| while (token) { |
| len = strcspn(kvpair_type, "="); |
| size = len + strlen(token) + 2; |
| value = atoi(token); |
| snprintf(kvpair, size, kvpair_type, value); |
| kvpair += size - 1; |
| kvpair_type += len + 3; |
| token = strtok(NULL, ","); |
| } |
| } |
| } |
| |
| int async_callback(qahw_stream_callback_event_t event, void *param, |
| void *cookie) |
| { |
| uint32_t *payload = param; |
| int i; |
| |
| if(cookie == NULL) { |
| fprintf(log_file, "Invalid callback handle\n"); |
| fprintf(stderr, "Invalid callback handle\n"); |
| return 0; |
| } |
| |
| stream_config *params = (stream_config*) cookie; |
| |
| switch (event) { |
| case QAHW_STREAM_CBK_EVENT_WRITE_READY: |
| fprintf(log_file, "stream %d: received event - QAHW_STREAM_CBK_EVENT_WRITE_READY\n", params->stream_index); |
| pthread_mutex_lock(¶ms->write_lock); |
| pthread_cond_signal(¶ms->write_cond); |
| pthread_mutex_unlock(¶ms->write_lock); |
| break; |
| case QAHW_STREAM_CBK_EVENT_DRAIN_READY: |
| fprintf(log_file, "stream %d: received event - QAHW_STREAM_CBK_EVENT_DRAIN_READY\n", params->stream_index); |
| pthread_mutex_lock(¶ms->drain_lock); |
| params->drain_received = true; |
| pthread_cond_signal(¶ms->drain_cond); |
| pthread_mutex_unlock(¶ms->drain_lock); |
| break; |
| case QAHW_STREAM_CBK_EVENT_ADSP: |
| fprintf(log_file, "stream %d: received event - QAHW_STREAM_CBK_EVENT_ADSP\n", params->stream_index); |
| if (payload != NULL) { |
| fprintf(log_file, "event_type %d\n", payload[0]); |
| fprintf(log_file, "param_length %d\n", payload[1]); |
| for (i=2; i* sizeof(uint32_t) <= payload[1]; i++) |
| fprintf(log_file, "param[%d] = 0x%x\n", i, payload[i]); |
| } |
| break; |
| case QAHW_STREAM_CBK_EVENT_ERROR: |
| fprintf(log_file, "stream %d: received event - QAHW_STREAM_CBK_EVENT_ERROR\n", params->stream_index); |
| stop_playback = true; |
| break; |
| default: |
| break; |
| } |
| return 0; |
| } |
| |
| void *proxy_read (void* data) |
| { |
| struct proxy_data* params = (struct proxy_data*) data; |
| qahw_module_handle_t *qahw_mod_handle = params->acp.qahw_mod_handle; |
| qahw_in_buffer_t in_buf; |
| char *buffer; |
| int rc = 0; |
| int bytes_to_read, bytes_written = 0, bytes_wrote = 0; |
| FILE *fp = NULL; |
| qahw_stream_handle_t* in_handle = nullptr; |
| |
| rc = qahw_open_input_stream(qahw_mod_handle, params->acp.handle, |
| params->acp.input_device, ¶ms->acp.config, &in_handle, |
| params->acp.flags, params->acp.kStreamName, params->acp.kInputSource); |
| if (rc) { |
| fprintf(log_file, "Could not open input stream %d \n",rc); |
| fprintf(stderr, "Could not open input stream %d \n",rc); |
| pthread_exit(0); |
| } |
| |
| if (in_handle != NULL) { |
| bytes_to_read = qahw_in_get_buffer_size(in_handle); |
| buffer = (char *) calloc(1, bytes_to_read); |
| if (buffer == NULL) { |
| fprintf(log_file, "calloc failed!!\n"); |
| fprintf(stderr, "calloc failed!!\n"); |
| pthread_exit(0); |
| } |
| |
| if ((fp = fopen(params->acp.file_name,"w"))== NULL) { |
| fprintf(log_file, "Cannot open file to dump proxy data\n"); |
| fprintf(stderr, "Cannot open file to dump proxy data\n"); |
| pthread_exit(0); |
| } |
| else { |
| params->hdr.num_channels = audio_channel_count_from_in_mask(params->acp.config.channel_mask); |
| params->hdr.sample_rate = params->acp.config.sample_rate; |
| params->hdr.byte_rate = params->hdr.sample_rate * params->hdr.num_channels * 2; |
| params->hdr.block_align = params->hdr.num_channels * 2; |
| params->hdr.bits_per_sample = 16; |
| fwrite(¶ms->hdr, 1, sizeof(params->hdr), fp); |
| } |
| memset(&in_buf,0, sizeof(qahw_in_buffer_t)); |
| in_buf.buffer = buffer; |
| in_buf.bytes = bytes_to_read; |
| |
| while (!(params->acp.thread_exit)) { |
| rc = qahw_in_read(in_handle, &in_buf); |
| if (rc > 0) { |
| bytes_wrote = fwrite((char *)(in_buf.buffer), sizeof(char), (int)in_buf.bytes, fp); |
| bytes_written += bytes_wrote; |
| if(bytes_wrote < in_buf.bytes) { |
| stop_playback = true; |
| fprintf(log_file, "Error in fwrite due to no memory(%d)=%s\n",ferror(fp), strerror(ferror(fp))); |
| break; |
| } |
| } |
| } |
| params->hdr.data_sz = bytes_written; |
| params->hdr.riff_sz = bytes_written + 36; //sizeof(hdr) - sizeof(riff_id) - sizeof(riff_sz) |
| fseek(fp, 0L , SEEK_SET); |
| fwrite(¶ms->hdr, 1, sizeof(params->hdr), fp); |
| fclose(fp); |
| rc = qahw_in_standby(in_handle); |
| if (rc) { |
| fprintf(log_file, "in standby failed %d \n", rc); |
| fprintf(stderr, "in standby failed %d \n", rc); |
| } |
| rc = qahw_close_input_stream(in_handle); |
| if (rc) { |
| fprintf(log_file, "could not close input stream %d \n", rc); |
| fprintf(stderr, "could not close input stream %d \n", rc); |
| } |
| fprintf(log_file, "pcm data saved to file %s", params->acp.file_name); |
| } |
| return 0; |
| } |
| |
| void *drift_read(void* data) |
| { |
| struct drift_data* params = (struct drift_data*) data; |
| qahw_stream_handle_t* out_handle = params->out_handle; |
| struct qahw_avt_device_drift_param drift_param; |
| int rc = -EINVAL; |
| |
| printf("drift queried at 100ms interval\n"); |
| while (!(params->thread_exit)) { |
| memset(&drift_param, 0, sizeof(struct qahw_avt_device_drift_param)); |
| rc = qahw_out_get_param_data(out_handle, QAHW_PARAM_AVT_DEVICE_DRIFT, |
| (qahw_param_payload *)&drift_param); |
| if (!rc) { |
| printf("resync flag = %d, drift %d, av timer %lld\n", |
| drift_param.resync_flag, |
| drift_param.avt_device_drift_value, |
| drift_param.ref_timer_abs_ts); |
| } else { |
| printf("drift query failed rc = %d retry after 100ms\n", rc); |
| } |
| |
| usleep(100000); |
| if (params->enable_drift_correction && |
| drift_param.avt_device_drift_value) { |
| struct qahw_out_correct_drift param; |
| param.adjust_time = drift_param.avt_device_drift_value; |
| printf("sending drift correction value %dus\n", |
| drift_param.avt_device_drift_value); |
| rc = qahw_out_set_param_data(out_handle, |
| QAHW_PARAM_OUT_CORRECT_DRIFT, |
| (qahw_param_payload *)¶m); |
| if (rc < 0) |
| fprintf(log_file, "qahw_out_set_param_data failed with err %d %d\n", |
| rc, __LINE__); |
| } |
| } |
| return NULL; |
| } |
| |
| static int is_eof(stream_config *stream) { |
| if (stream->filename) { |
| if (feof(stream->file_stream)) { |
| fprintf(log_file, "stream %d: error in fread, error %d\n", stream->stream_index, ferror(stream->file_stream)); |
| fprintf(stderr, "stream %d: error in fread, error %d\n", stream->stream_index, ferror(stream->file_stream)); |
| return true; |
| } |
| } else if (AUDIO_DEVICE_NONE != stream->input_device) |
| /* |
| * assuming this is called after we got -ve bytes value from hal read |
| */ |
| return true; |
| return false; |
| } |
| static int read_bytes(stream_config *stream, void *buff, int size) { |
| if (stream->filename) |
| return fread(buff, 1, size, stream->file_stream); |
| else if (AUDIO_DEVICE_NONE != stream->input_device) { |
| qahw_in_buffer_t in_buf; |
| memset(&in_buf,0, sizeof(qahw_in_buffer_t)); |
| in_buf.buffer = buff; |
| in_buf.bytes = size; |
| return qahw_in_read(stream->in_handle, &in_buf); |
| } |
| return 0; |
| } |
| |
| int write_to_hal(qahw_stream_handle_t* out_handle, char *data, size_t bytes, void *params_ptr) |
| { |
| stream_config *stream_params = (stream_config*) params_ptr; |
| |
| ssize_t ret; |
| pthread_mutex_lock(&stream_params->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) { |
| fprintf(log_file, "stream %d: writing data to hal failed (ret = %zd)\n", stream_params->stream_index, ret); |
| } else if (ret != bytes) { |
| fprintf(log_file, "stream %d: provided bytes %zd, written bytes %d\n",stream_params->stream_index, bytes, ret); |
| fprintf(log_file, "stream %d: waiting for event write ready\n", stream_params->stream_index); |
| pthread_cond_wait(&stream_params->write_cond, &stream_params->write_lock); |
| fprintf(log_file, "stream %d: out of wait for event write ready\n", stream_params->stream_index); |
| } |
| |
| pthread_mutex_unlock(&stream_params->write_lock); |
| return ret; |
| } |
| |
| static bool is_assoc_active() |
| { |
| int i = 0; |
| bool is_assoc_active = false; |
| |
| for (i = 0; i < MAX_PLAYBACK_STREAMS; i++) { |
| if (stream_param[i].flags & AUDIO_OUTPUT_FLAG_ASSOCIATED) { |
| is_assoc_active = true; |
| break; |
| } |
| } |
| return is_assoc_active; |
| } |
| |
| static int get_assoc_index() |
| { |
| int i = 0; |
| |
| for (i = 0; i < MAX_PLAYBACK_STREAMS; i++) { |
| if (stream_param[i].flags & AUDIO_OUTPUT_FLAG_ASSOCIATED) { |
| break; |
| } |
| } |
| return i; |
| } |
| |
| /* Entry point function for stream playback |
| * Opens the stream |
| * Reads KV pairs, sets volume, allocates input buffer |
| * Opens proxy and effects threads if enabled |
| * Starts freading the file and writing to HAL |
| * Drains out and close the stream after EOF |
| */ |
| void *start_stream_playback (void* stream_data) |
| { |
| int rc = 0; |
| stream_config *params = (stream_config*) stream_data; |
| bool proxy_thread_active = false; |
| pthread_t proxy_thread; |
| |
| bool drift_thread_active = false; |
| pthread_t drift_query_thread; |
| struct drift_data drift_params; |
| |
| int offset = 0; |
| bool is_offload = params->flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD; |
| size_t bytes_wanted = 0; |
| size_t write_length = 0; |
| size_t bytes_remaining = 0; |
| ssize_t bytes_written = 0; |
| |
| size_t bytes_read = 0; |
| char *data_ptr = NULL; |
| bool exit = false; |
| bool read_complete_file = true; |
| ssize_t bytes_to_read = 0; |
| int32_t latency; |
| |
| |
| memset(&drift_params, 0, sizeof(struct drift_data)); |
| |
| fprintf(log_file, "stream %d: play_later %d \n", params->stream_index, params->play_later); |
| |
| if(params->play_later) { |
| pthread_mutex_lock(&dual_main_lock); |
| fprintf(log_file, "stream %d: waiting for dual main signal\n", params->stream_index); |
| pthread_cond_wait(&dual_main_cond, &dual_main_lock); |
| fprintf(log_file, "stream %d: after the dual main signal\n", params->stream_index); |
| pthread_mutex_unlock(&dual_main_lock); |
| } |
| |
| if (params->interactive_strm) { |
| params->flags = AUDIO_OUTPUT_FLAG_INTERACTIVE; |
| fprintf(stderr, "stream %s %d: Interactive stream\n", __func__, params->stream_index); |
| } |
| |
| rc = qahw_open_output_stream(params->qahw_out_hal_handle, |
| params->handle, |
| params->output_device, |
| params->flags, |
| &(params->config), |
| &(params->out_handle), |
| params->device_url); |
| |
| if (rc) { |
| fprintf(log_file, "stream %d: could not open output stream, error - %d \n", params->stream_index, rc); |
| fprintf(stderr, "stream %d: could not open output stream, error - %d \n", params->stream_index, rc); |
| pthread_exit(0); |
| } |
| |
| fprintf(log_file, "stream %d: open output stream is success, out_handle %p\n", params->stream_index, params->out_handle); |
| |
| if (kpi_mode == true) { |
| measure_kpi_values(params->out_handle, params->flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD); |
| rc = qahw_close_output_stream(params->out_handle); |
| if (rc) { |
| fprintf(log_file, "stream %d: could not close output stream, error - %d \n", params->stream_index, rc); |
| fprintf(stderr, "stream %d: could not close output stream, error - %d \n", params->stream_index, rc); |
| } |
| return NULL; |
| } |
| |
| switch(params->filetype) { |
| char kvpair[KV_PAIR_MAX_LENGTH] = {0}; |
| case FILE_WMA: |
| case FILE_VORBIS: |
| case FILE_ALAC: |
| case FILE_FLAC: |
| fprintf(log_file, "%s:calling setparam for kvpairs\n", __func__); |
| if (!(params->kvpair_values)) { |
| fprintf(log_file, "stream %d: error!!No metadata for the clip\n", params->stream_index); |
| fprintf(stderr, "stream %d: error!!No metadata for the clip\n", params->stream_index); |
| pthread_exit(0);; |
| } |
| read_kvpair(kvpair, params->kvpair_values, params->filetype); |
| rc = qahw_out_set_parameters(params->out_handle, kvpair); |
| if(rc){ |
| fprintf(log_file, "stream %d: failed to set kvpairs\n", params->stream_index); |
| fprintf(stderr, "stream %d: failed to set kvpairs\n", params->stream_index); |
| pthread_exit(0);; |
| } |
| fprintf(log_file, "stream %d: kvpairs are set\n", params->stream_index); |
| break; |
| case FILE_DTS: |
| read_complete_file = false; |
| break; |
| default: |
| break; |
| } |
| |
| if (is_offload) { |
| fprintf(log_file, "stream %d: set callback for offload stream for playback usecase\n", params->stream_index); |
| qahw_out_set_callback(params->out_handle, async_callback, params); |
| } |
| |
| // create effect thread, use thread_data to transfer command |
| if (params->ethread_func && |
| (params->flags & (AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD|AUDIO_OUTPUT_FLAG_DIRECT))) { |
| |
| fprintf(log_file, "stream %d: effect type:%s\n", params->stream_index, effect_str[params->effect_index]); |
| params->ethread_data = create_effect_thread(params->effect_index, params->ethread_func); |
| |
| // create effect command thread |
| params->cmd_data.exit = false; |
| params->cmd_data.fx_data_ptr = &(params->ethread_data); |
| pthread_attr_init(&(params->cmd_data.attr)); |
| pthread_attr_setdetachstate(&(params->cmd_data.attr), PTHREAD_CREATE_JOINABLE); |
| rc = pthread_create(&(params->cmd_data.cmd_thread), &(params->cmd_data.attr), |
| &command_thread_func, &(params->cmd_data)); |
| if (rc < 0) { |
| fprintf(log_file, "stream %d: could not create effect command thread!\n", params->stream_index); |
| fprintf(stderr, "stream %d: could not create effect command thread!\n", params->stream_index); |
| pthread_exit(0); |
| } |
| |
| fprintf(log_file, "stream %d: loading effects\n", params->stream_index); |
| if (params->ethread_data != nullptr) { |
| // load effect module |
| notify_effect_command(params->ethread_data, EFFECT_LOAD_LIB, -1, 0, NULL); |
| |
| // get effect desc |
| notify_effect_command(params->ethread_data, EFFECT_GET_DESC, -1, 0, NULL); |
| |
| // create effect |
| params->ethread_data->io_handle = params->handle; |
| notify_effect_command(params->ethread_data, EFFECT_CREATE, -1, 0, NULL); |
| |
| // broadcast device info |
| notify_effect_command(params->ethread_data, EFFECT_CMD, QAHW_EFFECT_CMD_SET_DEVICE, sizeof(audio_devices_t), &(params->output_device)); |
| |
| // Enable and Set default values |
| params->ethread_data->default_value = params->effect_preset_strength; |
| params->ethread_data->default_flag = true; |
| notify_effect_command(params->ethread_data, EFFECT_CMD, QAHW_EFFECT_CMD_ENABLE, 0, NULL); |
| } |
| } |
| |
| if (params->output_device & AUDIO_DEVICE_OUT_PROXY) { |
| proxy_params.acp.qahw_mod_handle = params->qahw_out_hal_handle; |
| proxy_params.acp.handle = stream_handle; |
| stream_handle--; |
| proxy_params.acp.input_device = AUDIO_DEVICE_IN_PROXY; |
| proxy_params.acp.flags = AUDIO_INPUT_FLAG_NONE; |
| proxy_params.acp.config.channel_mask = audio_channel_in_mask_from_count(AFE_PROXY_CHANNEL_COUNT); |
| proxy_params.acp.config.sample_rate = AFE_PROXY_SAMPLING_RATE; |
| proxy_params.acp.config.format = AUDIO_FORMAT_PCM_16_BIT; |
| proxy_params.acp.kStreamName = "input_stream"; |
| proxy_params.acp.kInputSource = AUDIO_SOURCE_UNPROCESSED; |
| proxy_params.acp.thread_exit = false; |
| fprintf(log_file, "stream %d: create thread to read data from proxy\n", params->stream_index); |
| rc = pthread_create(&proxy_thread, NULL, proxy_read, (void *)&proxy_params); |
| if (!rc) |
| proxy_thread_active = true; |
| } else if (params->drift_query && !drift_thread_active) { |
| struct qahw_out_enable_drift_correction drift_enable_param; |
| |
| drift_params.out_handle = params->out_handle; |
| drift_params.thread_exit = false; |
| fprintf(log_file, "create thread to read avtimer vs device drift\n"); |
| if(params->drift_correction) { |
| drift_params.enable_drift_correction = true; |
| drift_enable_param.enable = true; |
| rc = qahw_out_set_param_data(params->out_handle, |
| QAHW_PARAM_OUT_ENABLE_DRIFT_CORRECTION, |
| (qahw_param_payload *)&drift_enable_param); |
| if (rc < 0) { |
| fprintf(log_file, "qahw_out_set_param_data failed with err %d %d\n", |
| rc, __LINE__); |
| drift_enable_param.enable = false; |
| } |
| } |
| rc = pthread_create(&drift_query_thread, NULL, drift_read, (void *)&drift_params); |
| if (!rc) |
| drift_thread_active = true; |
| else |
| fprintf(log_file, "drift query thread creation failure %d\n", rc); |
| } |
| |
| rc = qahw_out_set_volume(params->out_handle, vol_level, vol_level); |
| if (rc < 0) { |
| fprintf(log_file, "stream %d: unable to set volume\n", params->stream_index); |
| fprintf(stderr, "stream %d: unable to set volume\n", params->stream_index); |
| } |
| |
| if (params->pan_scale_ctrl == QAHW_PARAM_OUT_MIX_MATRIX_PARAMS) { |
| rc = qahw_out_set_param_data(params->out_handle, QAHW_PARAM_OUT_MIX_MATRIX_PARAMS, |
| (qahw_param_payload *) ¶ms->mm_params_pan_scale); |
| if (rc != 0) { |
| fprintf(log_file, "QAHW_PARAM_OUT_MIX_MATRIX_PARAMS could not be sent!\n"); |
| } |
| } |
| if (params->mix_ctrl == QAHW_PARAM_CH_MIX_MATRIX_PARAMS) { |
| rc = qahw_out_set_param_data(params->out_handle, QAHW_PARAM_CH_MIX_MATRIX_PARAMS, |
| (qahw_param_payload *) ¶ms->mm_params_downmix); |
| if (rc != 0) { |
| fprintf(log_file, "QAHW_PARAM_CH_MIX_MATRIX_PARAMS could not be sent!\n"); |
| } |
| } |
| |
| bytes_wanted = qahw_out_get_buffer_size(params->out_handle); |
| data_ptr = (char *) malloc (bytes_wanted); |
| if (data_ptr == NULL) { |
| fprintf(log_file, "stream %d: failed to allocate data buffer\n", params->stream_index); |
| fprintf(stderr, "stream %d: failed to allocate data buffer\n", params->stream_index); |
| pthread_exit(0); |
| } |
| |
| latency = qahw_out_get_latency(params->out_handle); |
| fprintf(log_file, "playback latency before starting a session %dms!!\n", |
| latency); |
| |
| if (event_trigger == true) |
| tigger_event(params->out_handle); |
| |
| bytes_to_read = get_bytes_to_read(params->file_stream, params->filetype); |
| if (bytes_to_read <= 0) |
| read_complete_file = true; |
| |
| while (!exit && !stop_playback) { |
| if (!bytes_remaining) { |
| fprintf(log_file, "\nstream %d: reading bytes %zd\n", params->stream_index, bytes_wanted); |
| bytes_read = read_bytes(params, data_ptr, bytes_wanted); |
| fprintf(log_file, "stream %d: read bytes %zd\n", params->stream_index, bytes_read); |
| if ((!read_complete_file && (bytes_to_read <= 0)) || (bytes_read <= 0)) { |
| fprintf(log_file, "stream %d: end of file\n", params->stream_index); |
| if (is_offload) { |
| params->drain_received = false; |
| qahw_out_drain(params->out_handle, QAHW_DRAIN_ALL); |
| if(!params->drain_received) { |
| pthread_mutex_lock(¶ms->drain_lock); |
| pthread_cond_wait(¶ms->drain_cond, ¶ms->drain_lock); |
| pthread_mutex_unlock(¶ms->drain_lock); |
| } |
| fprintf(log_file, "stream %d: out of compress drain\n", params->stream_index); |
| } |
| /* |
| * Caution: Below ADL log shouldnt be altered without notifying |
| * automation APT since it used for automation testing |
| */ |
| fprintf(log_file, "ADL: stream %d: playback completed successfully\n", params->stream_index); |
| exit = true; |
| continue; |
| } else { |
| if (!read_complete_file) { |
| bytes_to_read -= bytes_read; |
| if ((bytes_to_read > 0) && (bytes_to_read < bytes_wanted)) |
| bytes_wanted = bytes_to_read; |
| } |
| } |
| bytes_remaining = write_length = bytes_read; |
| } |
| |
| offset = write_length - bytes_remaining; |
| fprintf(log_file, "stream %d: writing to hal %zd bytes, offset %d, write length %zd\n", |
| params->stream_index, bytes_remaining, offset, write_length); |
| |
| |
| bytes_written = bytes_remaining; |
| bytes_written = write_to_hal(params->out_handle, data_ptr+offset, bytes_remaining, params); |
| if (bytes_written < 0) { |
| fprintf(stderr, "write failed %d", bytes_written); |
| exit = true; |
| continue; |
| } |
| bytes_remaining -= bytes_written; |
| |
| latency = qahw_out_get_latency(params->out_handle); |
| fprintf(log_file, "stream %d: bytes_written %zd, bytes_remaining %zd latency %d\n", |
| params->stream_index, bytes_written, bytes_remaining, latency); |
| } |
| |
| |
| if (params->ethread_data != nullptr) { |
| fprintf(log_file, "stream %d: un-loading effects\n", params->stream_index); |
| // disable effect |
| notify_effect_command(params->ethread_data, EFFECT_CMD, QAHW_EFFECT_CMD_DISABLE, 0, NULL); |
| |
| // release effect |
| notify_effect_command(params->ethread_data, EFFECT_RELEASE, -1, 0, NULL); |
| |
| // unload effect module |
| notify_effect_command(params->ethread_data, EFFECT_UNLOAD_LIB, -1, 0, NULL); |
| |
| // destroy effect thread |
| destroy_effect_thread(params->ethread_data); |
| |
| free(params->ethread_data); |
| params->ethread_data = NULL; |
| |
| // destory effect command thread |
| params->cmd_data.exit = true; |
| usleep(100000); // give a chance for thread to exit gracefully |
| rc = pthread_cancel(params->cmd_data.cmd_thread); |
| if (rc != 0) { |
| fprintf(log_file, "Fail to cancel thread!\n"); |
| fprintf(stderr, "Fail to cancel thread!\n"); |
| } |
| rc = pthread_join(params->cmd_data.cmd_thread, NULL); |
| if (rc < 0) { |
| fprintf(log_file, "Fail to join effect command thread!\n"); |
| fprintf(stderr, "Fail to join effect command thread!\n"); |
| } |
| } |
| |
| 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. |
| */ |
| usleep(500000); |
| proxy_params.acp.thread_exit = true; |
| fprintf(log_file, "wait for proxy thread exit\n"); |
| pthread_join(proxy_thread, NULL); |
| } |
| |
| if (drift_thread_active) { |
| usleep(500000); |
| drift_params.thread_exit = true; |
| pthread_join(drift_query_thread, NULL); |
| } |
| rc = qahw_out_standby(params->out_handle); |
| if (rc) { |
| fprintf(log_file, "stream %d: out standby failed %d \n", params->stream_index, rc); |
| fprintf(stderr, "stream %d: out standby failed %d \n", params->stream_index, rc); |
| } |
| |
| fprintf(log_file, "stream %d: closing output stream\n", params->stream_index); |
| rc = qahw_close_output_stream(params->out_handle); |
| if (rc) { |
| fprintf(log_file, "stream %d: could not close output stream, error - %d \n", params->stream_index, rc); |
| fprintf(stderr, "stream %d: could not close output stream, error - %d \n", params->stream_index, rc); |
| } |
| |
| if (data_ptr) |
| free(data_ptr); |
| |
| fprintf(log_file, "stream %d: stream closed\n", params->stream_index); |
| fprintf(log_file, "stream %d: is_dual_main- %d\n", params->stream_index,is_dual_main); |
| if (is_dual_main) { |
| usleep(500000); |
| pthread_mutex_lock(&dual_main_lock); |
| fprintf(log_file, "Dual main signal as we reached end of current running stream\n"); |
| is_dual_main = false; |
| pthread_cond_signal(&dual_main_cond); |
| pthread_mutex_unlock(&dual_main_lock); |
| } |
| |
| return NULL; |
| |
| } |
| |
| 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: |
| case AAC_LOAS: |
| 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 if (filetype == FILE_AAC_LATM) { |
| switch (format_type) { |
| case AAC_LC: |
| aac_format = AUDIO_FORMAT_AAC_LATM_LC; |
| break; |
| case AAC_HE_V1: |
| aac_format = AUDIO_FORMAT_AAC_LATM_HE_V1; |
| break; |
| case AAC_HE_V2: |
| aac_format = AUDIO_FORMAT_AAC_LATM_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 get_file_format(stream_config *stream_info) |
| { |
| int rc = 0; |
| |
| if (!(stream_info->flags_set)) { |
| if (stream_info->interactive_strm) |
| stream_info->flags = AUDIO_OUTPUT_FLAG_INTERACTIVE; |
| else |
| stream_info->flags = AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD|AUDIO_OUTPUT_FLAG_NON_BLOCKING; |
| stream_info->flags |= AUDIO_OUTPUT_FLAG_DIRECT; |
| } |
| |
| char header[WAV_HEADER_LENGTH_MAX] = {0}; |
| int wav_header_len = 0; |
| |
| switch (stream_info->filetype) { |
| case FILE_WAV: |
| /* |
| * Read the wave header |
| */ |
| if((wav_header_len = get_wav_header_length(stream_info->file_stream)) <= 0) { |
| fprintf(log_file, "wav header length is invalid:%d\n", wav_header_len); |
| exit(1); |
| } |
| fseek(stream_info->file_stream, 0, SEEK_SET); |
| rc = fread (header, wav_header_len , 1, stream_info->file_stream); |
| if (rc != 1) { |
| fprintf(log_file, "Error fread failed\n"); |
| fprintf(stderr, "Error fread failed\n"); |
| exit(1); |
| } |
| if (strncmp (header, "RIFF", 4) && strncmp (header+8, "WAVE", 4)) { |
| fprintf(log_file, "Not a wave format\n"); |
| fprintf(stderr, "Not a wave format\n"); |
| exit (1); |
| } |
| memcpy (&stream_info->channels, &header[22], 2); |
| memcpy (&stream_info->config.offload_info.sample_rate, &header[24], 4); |
| memcpy (&stream_info->config.offload_info.bit_width, &header[34], 2); |
| if (stream_info->config.offload_info.bit_width == 32) |
| stream_info->config.offload_info.format = AUDIO_FORMAT_PCM_32_BIT; |
| else if (stream_info->config.offload_info.bit_width == 24) |
| stream_info->config.offload_info.format = AUDIO_FORMAT_PCM_24_BIT_PACKED; |
| else |
| stream_info->config.offload_info.format = AUDIO_FORMAT_PCM_16_BIT; |
| if (!(stream_info->flags_set)) |
| stream_info->flags = AUDIO_OUTPUT_FLAG_DIRECT; |
| break; |
| |
| case FILE_MP3: |
| stream_info->config.offload_info.format = AUDIO_FORMAT_MP3; |
| break; |
| |
| case FILE_AAC: |
| case FILE_AAC_ADTS: |
| case FILE_AAC_LATM: |
| if (!is_valid_aac_format_type(stream_info->aac_fmt_type)) { |
| fprintf(log_file, "Invalid format type for AAC %d\n", stream_info->aac_fmt_type); |
| fprintf(stderr, "Invalid format type for AAC %d\n", stream_info->aac_fmt_type); |
| return; |
| } |
| stream_info->config.offload_info.format = get_aac_format(stream_info->filetype, stream_info->aac_fmt_type); |
| break; |
| case FILE_FLAC: |
| stream_info->config.offload_info.format = AUDIO_FORMAT_FLAC; |
| break; |
| case FILE_ALAC: |
| stream_info->config.offload_info.format = AUDIO_FORMAT_ALAC; |
| break; |
| case FILE_VORBIS: |
| stream_info->config.offload_info.format = AUDIO_FORMAT_VORBIS; |
| break; |
| case FILE_WMA: |
| if (stream_info->wma_fmt_type == WMA) |
| stream_info->config.offload_info.format = AUDIO_FORMAT_WMA; |
| else |
| stream_info->config.offload_info.format = AUDIO_FORMAT_WMA_PRO; |
| break; |
| case FILE_MP2: |
| stream_info->config.offload_info.format = AUDIO_FORMAT_MP2; |
| break; |
| case FILE_AC3: |
| stream_info->config.offload_info.format = AUDIO_FORMAT_AC3; |
| break; |
| case FILE_EAC3: |
| case FILE_EAC3_JOC: |
| stream_info->config.offload_info.format = AUDIO_FORMAT_E_AC3; |
| break; |
| case FILE_DTS: |
| stream_info->config.offload_info.format = AUDIO_FORMAT_DTS; |
| break; |
| case FILE_APTX: |
| stream_info->config.offload_info.format = AUDIO_FORMAT_APTX; |
| break; |
| case FILE_TRUEHD: |
| stream_info->config.offload_info.format = AUDIO_FORMAT_DOLBY_TRUEHD; |
| break; |
| case FILE_IEC61937: |
| stream_info->config.offload_info.format = AUDIO_FORMAT_IEC61937; |
| break; |
| default: |
| fprintf(log_file, "Does not support given filetype\n"); |
| fprintf(stderr, "Does not support given filetype\n"); |
| usage(); |
| hal_test_qap_usage(); |
| return; |
| } |
| stream_info->config.sample_rate = stream_info->config.offload_info.sample_rate; |
| stream_info->config.format = stream_info->config.offload_info.format; |
| stream_info->config.channel_mask = stream_info->config.offload_info.channel_mask = audio_channel_out_mask_from_count(stream_info->channels); |
| return; |
| } |
| |
| int measure_kpi_values(qahw_stream_handle_t* out_handle, 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; |
| char *data = NULL; |
| int ret = 0, count = 0; |
| struct timespec ts_cold, ts_cont; |
| uint64_t tcold, tcont, scold = 0, uscold = 0, scont = 0, uscont = 0; |
| |
| if (is_offload) { |
| fprintf(log_file, "Set callback for offload stream in kpi mesaurement usecase\n"); |
| qahw_out_set_callback(out_handle, async_callback, &stream_param[PRIMARY_STREAM_INDEX]); |
| } |
| |
| FILE *fd_latency_node = fopen(LATENCY_NODE, "r+"); |
| if (fd_latency_node) { |
| ret = fwrite(LATENCY_NODE_INIT_STR, sizeof(LATENCY_NODE_INIT_STR), 1, fd_latency_node); |
| if (ret<1) |
| fprintf(log_file, "error(%d) writing to debug node!", ret); |
| fprintf(stderr, "error(%d) writing to debug node!", ret); |
| fflush(fd_latency_node); |
| } else { |
| fprintf(log_file, "debug node(%s) open failed!", LATENCY_NODE); |
| fprintf(stderr, "debug node(%s) open failed!", LATENCY_NODE); |
| return -1; |
| } |
| |
| bytes_wanted = qahw_out_get_buffer_size(out_handle); |
| data = (char *) calloc (1, bytes_wanted); |
| if (data == NULL) { |
| fprintf(log_file, "calloc failed!!\n"); |
| fprintf(stderr, "calloc failed!!\n"); |
| return -ENOMEM; |
| } |
| |
| while (count < 64) { |
| if (!bytes_remaining) { |
| bytes_remaining = write_length = bytes_wanted; |
| } |
| if (count == 0) { |
| ret = clock_gettime(CLOCK_REALTIME, &ts_cold); |
| if (ret) { |
| fprintf(log_file, "error(%d) fetching start time for cold latency", ret); |
| fprintf(stderr, "error(%d) fetching start time for cold latency", ret); |
| return -1; |
| } |
| } else if (count == 16) { |
| int *d = (int *)data; |
| d[0] = 0x01010000; |
| ret = clock_gettime(CLOCK_REALTIME, &ts_cont); |
| if (ret) { |
| fprintf(log_file, "error(%d) fetching start time for continuous latency", ret); |
| fprintf(stderr, "error(%d) fetching start time for continuous latency", ret); |
| return -1; |
| } |
| } |
| |
| offset = write_length - bytes_remaining; |
| bytes_written = write_to_hal(out_handle, data+offset, bytes_remaining, &stream_param[PRIMARY_STREAM_INDEX]); |
| bytes_remaining -= bytes_written; |
| fprintf(log_file, "bytes_written %zd, bytes_remaining %zd\n", |
| bytes_written, bytes_remaining); |
| |
| if (count == 16) { |
| int *i = (int *)data; |
| i[0] = 0x00000000; |
| } |
| count++; |
| } |
| |
| char latency_buf[200] = {0}; |
| fread((void *) latency_buf, 100, 1, fd_latency_node); |
| fclose(fd_latency_node); |
| sscanf(latency_buf, " %llu,%llu,%*llu,%*llu,%llu,%llu", &scold, &uscold, &scont, &uscont); |
| tcold = scold*1000 - ((uint64_t)ts_cold.tv_sec)*1000 + uscold/1000 - ((uint64_t)ts_cold.tv_nsec)/1000000; |
| tcont = scont*1000 - ((uint64_t)ts_cont.tv_sec)*1000 + uscont/1000 - ((uint64_t)ts_cont.tv_nsec)/1000000; |
| fprintf(log_file, "\n values from debug node %s\n", latency_buf); |
| fprintf(log_file, " cold latency %llums, continuous latency %llums,\n", tcold, tcont); |
| fprintf(log_file, " **Note: please add DSP Pipe/PP latency numbers to this, for final latency values\n"); |
| return rc; |
| } |
| |
| int tigger_event(qahw_stream_handle_t* out_handle) |
| { |
| qahw_param_payload payload; |
| struct event_data event_payload = {0}; |
| int ret = 0; |
| |
| event_payload.num_events = 1; |
| event_payload.event_id = 0x13236; |
| event_payload.module_id = 0x10940; |
| event_payload.config_mask = 1; |
| |
| payload.adsp_event_params.event_type = QAHW_STREAM_PP_EVENT; |
| payload.adsp_event_params.payload_length = sizeof(event_payload); |
| payload.adsp_event_params.payload = &event_payload; |
| |
| fprintf(log_file, "Set callback for event trigger usecase\n"); |
| ret = qahw_out_set_callback(out_handle, async_callback, |
| &stream_param[PRIMARY_STREAM_INDEX]); |
| if (ret < 0) { |
| fprintf(log_file, "qahw_out_set_callback failed with err %d\n", |
| ret); |
| goto done; |
| } |
| fprintf(log_file, "Register for event using qahw_out_set_param_data\n"); |
| ret = qahw_out_set_param_data(out_handle, QAHW_PARAM_ADSP_STREAM_CMD, |
| (qahw_param_payload *)&payload); |
| if (ret < 0) { |
| fprintf(log_file, "qahw_out_set_param_data failed with err %d\n", |
| ret); |
| goto done; |
| } |
| fprintf(log_file, "qahw_out_set_paramdata succeeded\n"); |
| |
| done: |
| return ret; |
| } |
| |
| void parse_aptx_dec_bt_addr(char *value, struct qahw_aptx_dec_param *aptx_cfg) |
| { |
| int ba[6]; |
| char *str, *tok; |
| uint32_t addr[3]; |
| int i = 0; |
| |
| tok = strtok_r(value, ":", &str); |
| while (tok != NULL) { |
| ba[i] = strtol(tok, NULL, 16); |
| i++; |
| tok = strtok_r(NULL, ":", &str); |
| } |
| addr[0] = (ba[0] << 8) | ba[1]; |
| addr[1] = ba[2]; |
| addr[2] = (ba[3] << 16) | (ba[4] << 8) | ba[5]; |
| |
| aptx_cfg->bt_addr.nap = addr[0]; |
| aptx_cfg->bt_addr.uap = addr[1]; |
| aptx_cfg->bt_addr.lap = addr[2]; |
| } |
| |
| typedef struct { |
| char *string; |
| int val; |
| } param_converter_type; |
| |
| param_converter_type format_table[] = { |
| {"AUDIO_FORMAT_PCM_16_BIT", AUDIO_FORMAT_PCM_16_BIT}, |
| {"AUDIO_FORMAT_PCM_32_BIT", AUDIO_FORMAT_PCM_32_BIT}, |
| {"AUDIO_FORMAT_PCM_8_BIT", AUDIO_FORMAT_PCM_8_BIT}, |
| {"AUDIO_FORMAT_PCM_8_24_BIT", AUDIO_FORMAT_PCM_8_24_BIT}, |
| {"AUDIO_FORMAT_PCM_24_BIT_PACKED", AUDIO_FORMAT_PCM_24_BIT_PACKED} |
| }; |
| |
| |
| int rate_table[] = {48000, 44100}; |
| |
| |
| static int get_kvpairs_string(char *kvpairs, const char *key, char *value) { |
| struct str_parms *parms = NULL; |
| |
| if (!kvpairs) |
| return -1; |
| |
| parms = str_parms_create_str(kvpairs); |
| if (parms == NULL) |
| return -1; |
| |
| if (str_parms_get_str(parms, key, value, KVPAIRS_MAX) < 0) |
| return -1; |
| |
| str_parms_destroy(parms); |
| return 1; |
| } |
| |
| static int get_pcm_format(char *kvpairs) { |
| bool match = false; |
| int i = 0; |
| char value[KVPAIRS_MAX] = {0}; |
| |
| if(!kvpairs) |
| return -1; |
| |
| |
| if (get_kvpairs_string(kvpairs, QAHW_PARAMETER_STREAM_SUP_FORMATS, value) < 0) |
| return -1; |
| |
| fprintf(log_file, "formats=%s\n", value); |
| |
| /* |
| * for now we assume usb hal/pcm device announces suport for one format ONLY |
| */ |
| for (i = 0; i < (sizeof(format_table)/sizeof(format_table[0])); i++) { |
| if(!strncmp(format_table[i].string, value, sizeof(value))) { |
| match = true; |
| break; |
| } |
| } |
| |
| if (match) |
| return format_table[i].val; |
| else |
| return -1; |
| |
| } |
| |
| |
| static int get_rate(char *kvpairs) { |
| int match = false; |
| int rate = 0; |
| int i = 0; |
| char value[KVPAIRS_MAX] = {0}; |
| |
| if(!kvpairs) |
| return -1; |
| |
| if (get_kvpairs_string(kvpairs, QAHW_PARAMETER_STREAM_SUP_SAMPLING_RATES, value) < 0) |
| return -1; |
| |
| fprintf(log_file, "sample rates=%s\n", value); |
| /* |
| * for now we assume usb hal/pcm device announces suport for one rate ONLY |
| */ |
| |
| rate = atoi(value); |
| for (i = 0; i < ARRAY_SIZE(rate_table); i++) |
| if (rate_table[i] == rate) |
| match = true; |
| |
| if (match) |
| return rate; |
| else |
| return -1; |
| } |
| |
| |
| static int get_channels(char *kvpairs) { |
| int ch = -1; |
| char value[KVPAIRS_MAX] = {0}; |
| |
| if(!kvpairs) |
| return -1; |
| |
| if (get_kvpairs_string(kvpairs, QAHW_PARAMETER_STREAM_SUP_CHANNELS, value) < 0) |
| return -1; |
| |
| fprintf(log_file, "channels=%s\n", value); |
| |
| /* |
| * this is to work around a bug in usb hal which annouces support for stereo |
| * though the pcm dev/host stream is mono. |
| */ |
| if (strstr(value, "MONO")) |
| ch = 1; |
| else if (strstr(value, "STEREO")) |
| ch = 2; |
| |
| return ch; |
| } |
| |
| |
| static int detect_stream_params(stream_config *stream) { |
| bool detection_needed = false; |
| int direction = PCM_OUT; |
| |
| int rc = 0; |
| char *param_string = NULL; |
| int ch = 0; |
| |
| if (AUDIO_DEVICE_IN_USB_DEVICE == stream->input_device || |
| AUDIO_DEVICE_OUT_USB_DEVICE == stream->output_device) |
| if (USB_MODE_DEVICE == stream->usb_mode) |
| detection_needed = true; |
| |
| if (!detection_needed) |
| /* |
| * we will go with given params through args or with default params. |
| */ |
| return true; |
| |
| if (AUDIO_DEVICE_IN_USB_DEVICE == stream->input_device) |
| direction = PCM_IN; |
| else |
| direction = PCM_OUT; |
| |
| if (direction == PCM_OUT && stream->interactive_strm) { |
| stream->flags = AUDIO_OUTPUT_FLAG_INTERACTIVE; |
| fprintf(stderr, "stream %d: Interactive stream\n", stream->stream_index); |
| } |
| fprintf(log_file, "%s: opening %s stream\n", __func__, ((direction == PCM_IN)? "input":"output")); |
| |
| if (PCM_IN == direction) |
| rc = qahw_open_input_stream(stream->qahw_in_hal_handle, |
| stream->handle, |
| stream->input_device, |
| &(stream->config), |
| &(stream->in_handle), |
| AUDIO_INPUT_FLAG_NONE, |
| stream->device_url, |
| AUDIO_SOURCE_DEFAULT); |
| else |
| rc = qahw_open_output_stream(stream->qahw_out_hal_handle, |
| stream->handle, |
| stream->output_device, |
| stream->flags, |
| &(stream->config), |
| &(stream->out_handle), |
| stream->device_url); |
| |
| if (rc) { |
| fprintf(log_file, "stream could not be opened\n"); |
| fprintf(stderr, "stream could not be opened\n"); |
| return rc; |
| } |
| |
| fprintf(log_file,"\n**Supported Parameters**\n"); |
| if (PCM_IN == direction) |
| param_string = qahw_in_get_parameters(stream->in_handle, QAHW_PARAMETER_STREAM_SUP_SAMPLING_RATES); |
| else |
| param_string = qahw_out_get_parameters(stream->out_handle, QAHW_PARAMETER_STREAM_SUP_SAMPLING_RATES); |
| |
| if ((stream->config.sample_rate = get_rate(param_string)) <= 0) { |
| fprintf(log_file, "Unable to extract sample rate val =(%d) string(%s)\n", stream->config.sample_rate, param_string); |
| fprintf(stderr, "Unable to extract sample rate val =(%d) string(%s)\n", stream->config.sample_rate, param_string); |
| return -1; |
| } |
| if (PCM_IN == direction) |
| param_string = qahw_in_get_parameters(stream->in_handle, QAHW_PARAMETER_STREAM_SUP_CHANNELS); |
| else |
| param_string = qahw_out_get_parameters(stream->out_handle, QAHW_PARAMETER_STREAM_SUP_CHANNELS); |
| |
| if ((ch = get_channels(param_string)) <= 0) { |
| fprintf(log_file, "Unable to extract channels =(%d) string(%s)\n", ch, param_string == NULL ? "null":param_string); |
| fprintf(stderr, "Unable to extract channels =(%d) string(%s)\n", ch, param_string == NULL ? "null":param_string); |
| return -1; |
| } |
| stream->config.channel_mask = audio_channel_in_mask_from_count(ch); |
| |
| if (PCM_IN == direction) |
| param_string = qahw_in_get_parameters(stream->in_handle, QAHW_PARAMETER_STREAM_SUP_FORMATS); |
| else |
| param_string = qahw_out_get_parameters(stream->out_handle, QAHW_PARAMETER_STREAM_SUP_FORMATS); |
| |
| if ((stream->config.format = get_pcm_format(param_string)) <= 0) { |
| fprintf(log_file, "Unable to extract pcm format val =(%d) string(%s)\n", stream->config.format, param_string); |
| fprintf(stderr, "Unable to extract pcm format val =(%d) string(%s)\n", stream->config.format, param_string); |
| return -1; |
| } |
| stream->config.offload_info.format = stream->config.format; |
| fprintf(log_file, "\n**Extracted Parameters**\nrate=%d\nch=%d,ch_mask=0x%x\nformats=%d\n\n", |
| stream->config.sample_rate, |
| ch, stream->config.channel_mask, |
| stream->config.format); |
| /* |
| * Detection done now close usb stream it will be re-open later |
| */ |
| fprintf(log_file, "%s:closing the usb stream\n", __func__); |
| |
| if (PCM_IN == direction) |
| rc = qahw_close_input_stream(stream->in_handle); |
| else |
| rc = qahw_close_output_stream(stream->out_handle); |
| |
| if (rc) { |
| fprintf(log_file, "%s:stream could not be closed\n", __func__); |
| fprintf(stderr, "%s:stream could not be closed\n", __func__); |
| return rc; |
| } |
| stream->config.offload_info.sample_rate = stream->config.sample_rate; |
| stream->config.offload_info.format = stream->config.format; |
| stream->config.offload_info.channel_mask = stream->config.channel_mask; |
| return rc; |
| |
| } |
| void usage() { |
| printf(" \n Command \n"); |
| printf(" \n hal_play_test -f file-path <options> - Plays audio file from the path provided\n"); |
| printf(" \n Options\n"); |
| printf(" -f --file-path <file path> - file path to be used for playback.\n"); |
| printf(" file path must be provided unless -K(--kpi) is used\n\n"); |
| printf(" file path must be provided unless -s(input data is not from a file) is used\n\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(" -b --bitwidth <bitwidth> - Give either 16 or 24.Default value is 16.\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(" -s --source-device <decimal value> - see system/media/audio/include/system/audio.h for device values\n"); |
| printf(" obtain data from a device instead of an SD card file\n\n"); |
| printf(" for example catpure data from USB HAL(device) and play it on Regular HAL(device)\n\n"); |
| printf(" -t --file-type <file type> - 1:WAV 2:MP3 3:AAC 4:AAC_ADTS 5:FLAC\n"); |
| printf(" 6:ALAC 7:VORBIS 8:WMA 10:AAC_LATM \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(" -w --wma-type <wma type> - Required for WMA clips.Default vlaue is 1\n"); |
| printf(" 1: WMA 2: WMAPRO 3:WMA_LOSSLESS \n\n"); |
| printf(" -k --kvpairs <values> - Metadata information of clip\n"); |
| printf(" See Example for more info\n\n"); |
| printf(" -l --log-file <ABSOLUTE FILEPATH> - File path for debug msg, to print\n"); |
| printf(" on console use stdout or 1 \n\n"); |
| printf(" -D --dump-file <ABSOLUTE FILEPATH> - File path to dump pcm data from proxy\n"); |
| printf(" -F --flags <int value for output flags> - Output flag to be used\n\n"); |
| 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 --event-trigger - Trigger DTMF event during playback\n"); |
| printf(" -e --effect-type <effect type> - Effect used for test\n"); |
| printf(" 0:bassboost 1:virtualizer 2:equalizer 3:visualizer(NA) 4:reverb 5:audiosphere others:null\n\n"); |
| printf(" -p --effect-preset <effect preset type> - Effect preset type for respective effect-type\n"); |
| printf(" -S --effect-strength <effect strength> - Effect strength for respective effect-type\n"); |
| printf(" -A --bt-addr <bt device addr> - Required to set bt device adress for aptx decoder\n\n"); |
| printf(" -q --drift query - Required for querying avtime vs hdmi drift\n"); |
| printf(" -Q --drift query and correction - Enable Drift query and correction\n"); |
| printf(" -P - Argument to do multi-stream playback, currently 2 streams are supported to run concurrently\n"); |
| printf(" Put -P and mention required attributes for the next stream\n"); |
| printf(" 0:bassboost 1:virtualizer 2:equalizer 3:visualizer(NA) 4:reverb 5:audiosphere others:null"); |
| printf(" PLUS - For multi-stream playback, currently 2 streams are supported to play concurrently\n"); |
| printf(" Put PLUS and mention required attributes for the next files"); |
| printf(" -u --device-nodeurl - URL of PCM device\n"); |
| printf(" in the following format card=x;device=x"); |
| printf(" this option is mandatory in working with USB HAL"); |
| printf(" -m --mode - usb operating mode(Device Mode is default)\n"); |
| printf(" 0:Device Mode(host drives the stream and its params and so no need to give params as input)\n"); |
| printf(" 1:Host Mode(user can give stream and stream params via a stream(SD card file) or setup loopback with given params\n"); |
| printf(" -i --intr-strm - interactive stream indicator\n"); |
| printf(" -C --Device Config - Device Configuration params\n"); |
| printf(" Params should be in the order defined in struct qahw_device_cfg_param. Order is: \n"); |
| printf(" <sample_rate>, <channels>, <bit_width>, <format>, <device>, <channel_map[channels]>, <channel_allocation> \n"); |
| printf(" Example(6 channel HDMI config): hal_play_test -f /data/ChID16bit_5.1ch_48k.wav -v 0.9 -d 1024 -c 6 -C 48000 6 16 1 1024 1 2 6 3 4 5 19\n"); |
| printf(" \n Examples \n"); |
| printf(" hal_play_test -f /data/Anukoledenadu.wav -> plays Wav stream with default params\n\n"); |
| printf(" hal_play_test -f /data/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 -f /data/v1-CBR-32kHz-stereo-40kbps.mp3 -t 2 -d 33554432 -v 0.01 -r 32000 -c 2 -D /data/proxy_dump.wav\n"); |
| printf(" -> plays MP3 stream(-t = 2) on BT device in non-split path (-d = 33554432)\n"); |
| printf(" -> 2 channels and 32000 sample rate\n"); |
| printf(" -> dumps pcm data to file at /data/proxy_dump.wav\n\n"); |
| printf(" hal_play_test -f /data/v1-CBR-32kHz-stereo-40kbps.mp3 -t 2 -d 128 -v 0.01 -r 32000 -c 2 \n"); |
| printf(" -> plays MP3 stream(-t = 2) on BT device in split path (-d = 128)\n"); |
| printf(" -> 2 channels and 32000 sample rate\n"); |
| printf(" hal_play_test -f /data/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 -f /data/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"); |
| printf(" hal_play_test -f /data/2.0_16bit_48khz.m4a -k 1536000,16,0,0,4096,14,16388,0,10,2,40,48000,1536000,48000 -t 6 -r 48000 -c 2 -v 0.5 \n"); |
| printf(" -> Play alac clip (-t = 6)\n"); |
| printf(" -> kvpair(-k) values represent media-info of clip & values should be in below mentioned sequence\n"); |
| printf(" ->alac_avg_bit_rate,alac_bit_depth,alac_channel_layout,alac_compatible_version,\n"); |
| printf(" ->alac_frame_length,alac_kb,alac_max_frame_bytes,alac_max_run,alac_mb,\n"); |
| printf(" ->alac_num_channels,alac_pb,alac_sampling_rate,avg_bit_rate,sample_rate\n\n"); |
| printf(" hal_play_test -f /data/DIL CHAHTA HAI.flac -k 0,4096,13740,4096,14 -t 5 -r 48000 -c 2 -v 0.5 \n"); |
| printf(" -> Play flac clip (-t = 5)\n"); |
| printf(" -> kvpair(-k) values represent media-info of clip & values should be in below mentioned sequence\n"); |
| printf(" ->avg_bit_rate,flac_max_blk_size,flac_max_frame_size\n"); |
| printf(" ->flac_min_blk_size,flac_min_frame_size,sample_rate\n"); |
| printf(" hal_play_test -f /data/vorbis.mka -k 500000,48000,1 -t 7 -r 48000 -c 2 -v 0.5 \n"); |
| printf(" -> Play vorbis clip (-t = 7)\n"); |
| printf(" -> kvpair(-k) values represent media-info of clip & values should be in below mentioned sequence\n"); |
| printf(" ->avg_bit_rate,sample_rate,vorbis_bitstream_fmt\n"); |
| printf(" hal_play_test -f /data/file.wma -k 192000,48000,16,8192,3,15,0,0,353 -t 8 -w 1 -r 48000 -c 2 -v 0.5 \n"); |
| printf(" -> Play wma clip (-t = 8)\n"); |
| printf(" -> kvpair(-k) values represent media-info of clip & values should be in below mentioned sequence\n"); |
| 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 -f /data/03_Kuch_Khaas_BE.btaptx -t 9 -d 2 -v 0.2 -r 44100 -c 2 -A 00:02:5b:00:ff:03 \n"); |
| printf(" -> Play aptx clip (-t = 9)\n"); |
| printf(" -> 2 channels and 44100 sample rate\n"); |
| printf(" -> BT addr: bt_addr=00:02:5b:00:ff:03\n\n"); |
| printf(" hal_play_test -f /data/silence.ac3 -t 9 -r 48000 -c 2 -v 0.05 -F 16433 -P -f /data/music_48k.ac3 -t 9 -r 48000 -c 2 -F 32817\n"); |
| printf(" -> Plays a silence clip as main stream and music clip as associated\n\n"); |
| printf(" hal_play_test -K -F 4 -> Measure latency KPIs for low latency output\n\n"); |
| printf(" hal_play_test -f /data/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"); |
| printf(" hal_play_test -d 2 -v 0.05 -s 2147487744 -u \"card=1\\;device=0\" \n"); |
| printf(" -> capture audio stream from usb device(HAL)@ 44.1 KHz, 2ch\n"); |
| printf(" -> and loop/play it back on to primary HAL \n\n"); |
| printf(" -> Device Mode is default\n"); |
| printf(" -> card=1\\;device=0 -> specifies the URL, pls not that the ';' is escaped\n\n"); |
| printf(" hal_play_test -f /data/Anukoledenadu.wav -d 16384 -u \"card=1\\;device=0\" \n"); |
| printf(" -> Play PCM to USB out\n\n"); |
| printf(" hal_play_test -d 16384 -u \"card=1\\;device=0\" -s 2\n"); |
| printf(" ->Capture PCM from Local Mic and play it on USB(usb to primary hal loopback)\n\n"); |
| printf(" hal_play_test -d 16384 -u \"card=1\\;device=0\" -s 2 -P -d 2 -v 0.05 -s 2147487744 -u \"card=1\\;device=0\"\n"); |
| printf(" ->full duplex, setup both primary to usb and usb to primary loopbacks\n"); |
| printf(" ->Note:-P separates the steam params for both the loopbacks\n"); |
| printf(" ->Note:all the USB device commmands(above) should be accompanied with the host side commands\n\n"); |
| printf("hal_play_test -f interactive_audio.wav -d 2 -l out.txt -k \"mixer_ctrl=pan_scale;c=1;o=6;I=fc;O=fl,fr,fc,lfe,bl,br;M=0.5,0.5,0,0,0,0\" -i 1\n"); |
| printf(" ->kv_pair for downmix or pan_scale should folow the above sequence, one can pass downmix & pan_scale params/coeff matrices. For each control params should be sent separately \n"); |
| } |
| |
| int get_wav_header_length (FILE* file_stream) |
| { |
| int subchunk_size = 0, wav_header_len = 0; |
| |
| fseek(file_stream, 16, SEEK_SET); |
| if(fread(&subchunk_size, 4, 1, file_stream) != 1) { |
| fprintf(log_file, "Unable to read subchunk:\n"); |
| fprintf(stderr, "Unable to read subchunk:\n"); |
| exit (1); |
| } |
| if(subchunk_size < 16) { |
| fprintf(log_file, "This is not a valid wav file \n"); |
| fprintf(stderr, "This is not a valid wav file \n"); |
| } else { |
| wav_header_len = FORMAT_DESCRIPTOR_SIZE + SUBCHUNK1_SIZE(subchunk_size) + SUBCHUNK2_SIZE; |
| } |
| return wav_header_len; |
| } |
| |
| /* convert big-endian to little-endian */ |
| uint64_t convert_BE_to_LE( uint64_t in) |
| { |
| uint64_t out; |
| char *p_in = (char *) ∈ |
| char *p_out = (char *) &out; |
| p_out[0] = p_in[7]; |
| p_out[1] = p_in[6]; |
| p_out[2] = p_in[5]; |
| p_out[3] = p_in[4]; |
| p_out[4] = p_in[3]; |
| p_out[5] = p_in[2]; |
| p_out[6] = p_in[1]; |
| p_out[7] = p_in[0]; |
| return out; |
| } |
| |
| static ssize_t get_bytes_to_read(FILE* file, int file_type) |
| { |
| char keyword[DTSHD_META_KEYWORD_SIZE + 1]; |
| bool is_dtshd_stream =false; |
| uint64_t read_chunk_size = 0; |
| uint64_t chunk_size = 0; |
| ssize_t file_read_size = -1; |
| ssize_t header_read_size = -1; |
| long int pos; |
| int ret = 0; |
| |
| if (file_type == FILE_DTS) { |
| |
| //first locate the ASCII header "DTSHDHDR"identifier |
| while (!feof(file) && (header_read_size < 1024) & |
| (fread(&keyword, sizeof(char), DTSHD_META_KEYWORD_SIZE, file) |
| == DTSHD_META_KEYWORD_SIZE)) { |
| //update the number of bytes was read for identifying the header |
| header_read_size = ftell(file); |
| |
| if (strncmp(keyword, DTSHD_CHUNK_HEADER_KEYWORD, |
| DTSHD_META_KEYWORD_SIZE) == 0) { |
| // read the 8-byte size field |
| if (fread(&read_chunk_size, sizeof(char), |
| DTSHD_META_KEYWORD_SIZE, file) == DTSHD_META_KEYWORD_SIZE) { |
| is_dtshd_stream = true; |
| chunk_size = convert_BE_to_LE(read_chunk_size); |
| pos = ftell(file); |
| fseek(file, chunk_size, SEEK_CUR); |
| fprintf(stderr,"DTS header chunk offset:%lu and chunk_size:%llu \n", |
| pos, chunk_size); |
| break; |
| } |
| else { |
| printf(" file read error \n"); |
| break; |
| } //end reading chunk size |
| } |
| } |
| |
| if (!is_dtshd_stream) { |
| fprintf(stderr, "raw dts hd stream"); |
| fseek(file, 0, SEEK_SET); |
| return file_read_size; |
| } |
| /* parsing each chunk data */ |
| while (!feof(file) && |
| fread(&keyword, sizeof(uint8_t), DTSHD_META_KEYWORD_SIZE, file) |
| == DTSHD_META_KEYWORD_SIZE) { |
| /* check for the stream audio data */ |
| ret = strncmp(keyword, |
| DTSHD_CHUNK_STREAM_KEYWORD, |
| DTSHD_META_KEYWORD_SIZE); |
| if (!ret) { |
| ret = fread(&read_chunk_size, 1, DTSHD_META_KEYWORD_SIZE, file); |
| chunk_size = convert_BE_to_LE(read_chunk_size); |
| if (ret != DTSHD_META_KEYWORD_SIZE) { |
| fprintf(stderr,"%s %d file read error ret %\n", |
| __func__, __LINE__, ret); |
| file_read_size = -EINVAL; |
| break; |
| } |
| file_read_size = chunk_size; |
| fprintf(stderr, "DTS read_chunk_size %llu and file_read_size: %zd\n", |
| chunk_size, |
| file_read_size); |
| break; |
| } else { |
| fprintf(log_file, "Identified chunk of %c %c %c %c %c %c %c %c \n", |
| keyword[0], keyword[1], keyword[2], keyword[3], |
| keyword[4], keyword[5], keyword[6], keyword[7] ); |
| ret = fread(&read_chunk_size, 1, DTSHD_META_KEYWORD_SIZE, file); |
| pos = ftell(file); |
| chunk_size = convert_BE_to_LE(read_chunk_size); |
| fseek(file, chunk_size, SEEK_CUR); |
| } |
| } |
| } |
| return file_read_size; |
| } |
| |
| qahw_module_handle_t * load_hal(audio_devices_t dev) { |
| qahw_module_handle_t *hal = NULL; |
| |
| if ((AUDIO_DEVICE_IN_USB_DEVICE == dev) || |
| (AUDIO_DEVICE_OUT_USB_DEVICE == dev)){ |
| if (!usb_hal_handle) { |
| fprintf(log_file,"\nLoading usb HAL\n"); |
| if ((usb_hal_handle = qahw_load_module(QAHW_MODULE_ID_USB)) == NULL) { |
| fprintf(log_file,"failure in Loading usb HAL\n"); |
| fprintf(stderr,"failure in Loading usb HAL\n"); |
| return NULL; |
| } |
| } |
| hal = usb_hal_handle; |
| } |
| /* else if ((AUDIO_DEVICE_IN_BLUETOOTH_A2DP == dev) || |
| (AUDIO_DEVICE_OUT_BLUETOOTH_A2DP == dev)){ |
| if (!bt_hal_handle) { |
| fprintf(log_file,"Loading BT HAL\n"); |
| if ((bt_hal_handle = qahw_load_module(QAHW_MODULE_ID_A2DP)) == NULL) { |
| fprintf(log_file,"failure in Loading BT HAL\n"); |
| fprintf(stderr,"failure in Loading BT HAL\n"); |
| return NULL; |
| } |
| } |
| hal = bt_hal_handle; |
| }*/ |
| else { |
| if (!primary_hal_handle) { |
| fprintf(log_file,"\nLoading Primary HAL\n"); |
| if ((primary_hal_handle = qahw_load_module(QAHW_MODULE_ID_PRIMARY)) == NULL) { |
| fprintf(log_file,"failure in Loading Primary HAL\n"); |
| fprintf(stderr,"failure in Loading primary HAL\n"); |
| return NULL; |
| } |
| } |
| hal = primary_hal_handle; |
| } |
| return hal; |
| } |
| /* |
| * this function unloads all the loaded hal modules so this should be called |
| * after all the stream playback are concluded. |
| */ |
| int unload_hals() { |
| |
| if (usb_hal_handle) { |
| fprintf(log_file,"\nUnLoading usb HAL\n"); |
| if (qahw_unload_module(usb_hal_handle) < 0) { |
| fprintf(log_file,"failure in Un Loading usb HAL\n"); |
| fprintf(stderr,"failure in Un Loading usb HAL\n"); |
| return -1; |
| } |
| } |
| if (bt_hal_handle) { |
| fprintf(log_file,"UnLoading BT HAL\n"); |
| if (qahw_unload_module(bt_hal_handle) < 0) { |
| fprintf(log_file,"failure in UnLoading BT HAL\n"); |
| fprintf(stderr,"failure in Un Loading BT HAL\n"); |
| return -1; |
| } |
| } |
| if (primary_hal_handle) { |
| fprintf(log_file,"\nUnLoading Primary HAL\n"); |
| if (qahw_unload_module(primary_hal_handle) < 0) { |
| fprintf(log_file,"failure in Un Loading Primary HAL\n"); |
| fprintf(stderr,"failure in Un Loading primary HAL\n"); |
| return -1; |
| } |
| } |
| return 1; |
| } |
| |
| audio_channel_mask_t get_channel_mask_for_name(char *name) { |
| audio_channel_mask_t channel_type = AUDIO_CHANNEL_INVALID; |
| if (NULL == name) |
| return channel_type; |
| else if (strncmp(name, "fl", 2) == 0) |
| channel_type = QAHW_PCM_CHANNEL_FL; |
| else if (strncmp(name, "fr", 2) == 0) |
| channel_type = QAHW_PCM_CHANNEL_FR; |
| else if (strncmp(name, "fc", 2) == 0) |
| channel_type = QAHW_PCM_CHANNEL_FC; |
| else if (strncmp(name, "lfe", 2) == 0) |
| channel_type = QAHW_PCM_CHANNEL_LFE; |
| else if (strncmp(name, "bl", 2) == 0) |
| channel_type = QAHW_PCM_CHANNEL_LB; |
| else if (strncmp(name, "br", 2) == 0) |
| channel_type = QAHW_PCM_CHANNEL_RB; |
| else if (strncmp(name, "flc", 3) == 0) |
| channel_type = QAHW_PCM_CHANNEL_FLC; |
| else if (strncmp(name, "frc", 3) == 0) |
| channel_type = QAHW_PCM_CHANNEL_FRC; |
| else if (strncmp(name, "cs", 2) == 0) |
| channel_type = QAHW_PCM_CHANNEL_CS; |
| else if (strncmp(name, "ls", 2) == 0) |
| channel_type = QAHW_PCM_CHANNEL_LS; |
| else if (strncmp(name, "rs", 2) == 0) |
| channel_type = QAHW_PCM_CHANNEL_RS; |
| else if (strncmp(name, "ts", 2) == 0) |
| channel_type = QAHW_PCM_CHANNEL_TS; |
| else if (strncmp(name, "cvh", 3) == 0) |
| channel_type = QAHW_PCM_CHANNEL_CVH; |
| else if (strncmp(name, "ms", 3) == 0) |
| channel_type = QAHW_PCM_CHANNEL_MS; |
| else if (strncmp(name, "rlc", 3) == 0) |
| channel_type = QAHW_PCM_CHANNEL_RLC; |
| else if (strncmp(name, "rrc", 3) == 0) |
| channel_type = QAHW_PCM_CHANNEL_RRC; |
| |
| return channel_type; |
| } |
| |
| int extract_channel_mapping(uint32_t *channel_map, const char * arg_string){ |
| |
| char *token_string = NULL; |
| char *init_ptr = NULL; |
| char *token = NULL; |
| char *saveptr = NULL; |
| |
| if (NULL == channel_map) |
| return -EINVAL; |
| |
| if (NULL == arg_string) |
| return EINVAL; |
| |
| token_string = strdup(arg_string); |
| |
| if(token_string != NULL) { |
| init_ptr = token_string; |
| token = strtok_r(token_string, ",", &saveptr); |
| int index = 0; |
| if (NULL == token) |
| return -EINVAL; |
| else |
| channel_map[index++] = get_channel_mask_for_name(token); |
| |
| while(NULL !=(token = strtok_r(NULL,",",&saveptr))) |
| channel_map[index++] = get_channel_mask_for_name(token); |
| |
| free(init_ptr); |
| init_ptr = NULL; |
| token_string = NULL; |
| } else |
| return -EINVAL; |
| return 0; |
| } |
| |
| int extract_mixer_coeffs(qahw_mix_matrix_params_t * mm_params, const char * arg_string){ |
| |
| char *token_string = NULL; |
| char *init_ptr = NULL; |
| char *token = NULL; |
| char *saveptr = NULL; |
| int i = 0; |
| int j = 0; |
| |
| if (NULL == mm_params) |
| return -EINVAL; |
| |
| if (NULL == arg_string) |
| return -EINVAL; |
| |
| token_string = strdup(arg_string); |
| |
| if(token_string != NULL) { |
| init_ptr = token_string; |
| token = strtok_r(token_string, ",", &saveptr); |
| int index = 0; |
| if (NULL == token) |
| return -EINVAL; |
| else { |
| mm_params->mixer_coeffs[i][j] = atof(token); |
| j++; |
| } |
| |
| while(NULL !=(token = strtok_r(NULL,",",&saveptr))) { |
| if(j == mm_params->num_input_channels) { |
| j=0; |
| i++; |
| } |
| if(i == mm_params->num_output_channels) |
| break; |
| mm_params->mixer_coeffs[i][j++] = atof(token); |
| } |
| free(init_ptr); |
| init_ptr = NULL; |
| token_string = NULL; |
| } else |
| return -EINVAL; |
| } |
| |
| #ifdef QAP |
| int start_playback_through_qap(char * kvp_string, int num_of_streams, qahw_module_handle_t *hal_handle) { |
| stream_config *stream = NULL; |
| int rc = 0; |
| int i; |
| |
| fprintf(stdout, "kvp_string %s and num_of_streams %d\n", kvp_string, num_of_streams); |
| for (i = 0; i < num_of_streams; i++) { |
| stream = &stream_param[i]; |
| if (stream->filename) { |
| if ((stream->file_stream = fopen(stream->filename, "r"))== NULL) { |
| fprintf(log_file, "Cannot open audio file %s\n", stream->filename); |
| fprintf(stderr, "Cannot open audio file %s\n", stream->filename); |
| return -EINVAL; |
| } |
| } |
| get_file_format(stream); |
| fprintf(stdout, "Playing from:%s\n", stream->filename); |
| qap_module_handle_t qap_module_handle = NULL; |
| if (!qap_wrapper_session_active) { |
| rc = qap_wrapper_session_open(kvp_string, stream, num_of_streams, hal_handle); |
| if (rc != 0) { |
| fprintf(stderr, "Session Open failed\n"); |
| return -EINVAL; |
| } |
| qap_wrapper_session_active = true; |
| } |
| |
| if (qap_wrapper_session_active) { |
| stream->qap_module_handle = qap_wrapper_stream_open(stream); |
| if (stream->qap_module_handle == NULL) { |
| fprintf(stderr, "QAP Stream open Failed\n"); |
| } else { |
| fprintf(stdout, "QAP module handle is %p and file name is %s\n", stream->qap_module_handle, stream->filename); |
| rc = pthread_create(&playback_thread[i], NULL, qap_wrapper_start_stream, (void *)&stream_param[i]); |
| if (rc) { |
| fprintf(stderr, "stream %d: failed to create thread\n", stream->stream_index); |
| return -EINVAL; |
| } |
| thread_active[i] = true; |
| } |
| } |
| } |
| return 0; |
| } |
| #endif |
| |
| int main(int argc, char* argv[]) { |
| char *ba = NULL; |
| qahw_param_payload payload; |
| qahw_param_id param_id; |
| struct qahw_aptx_dec_param aptx_params; |
| int rc = 0; |
| int i = 0; |
| int iter_i = 0; |
| int iter_j = 0; |
| int chmap_iter = 0; |
| |
| kpi_mode = false; |
| char mixer_ctrl_name[64] = {0}; |
| char input_ch[64] = {0}; |
| char output_ch[64] = {0}; |
| char input_ch_map[64] = {0}; |
| char output_ch_map[64] = {0}; |
| char mixer_coeff[64] = {0}; |
| event_trigger = false; |
| bool wakelock_acquired = false; |
| |
| log_file = stdout; |
| proxy_params.acp.file_name = "/data/pcm_dump.wav"; |
| stream_config *stream = NULL; |
| |
| struct qahw_device_cfg_param device_cfg_params; |
| bool send_device_config = false; |
| |
| init_streams(); |
| |
| int num_of_streams = 1; |
| char kvp_string[KV_PAIR_MAX_LENGTH] = {0}; |
| |
| struct option long_options[] = { |
| /* These options set a flag. */ |
| {"file-path", required_argument, 0, 'f'}, |
| {"output-device", required_argument, 0, 'd'}, |
| {"input-device", required_argument, 0, 's'}, |
| {"sample-rate", required_argument, 0, 'r'}, |
| {"channels", required_argument, 0, 'c'}, |
| {"bitwidth", required_argument, 0, 'b'}, |
| {"volume", required_argument, 0, 'v'}, |
| {"enable-dump", required_argument, 0, 'V'}, |
| {"log-file", required_argument, 0, 'l'}, |
| {"dump-file", required_argument, 0, 'D'}, |
| {"file-type", required_argument, 0, 't'}, |
| {"aac-type", required_argument, 0, 'a'}, |
| {"wma-type", required_argument, 0, 'w'}, |
| {"kvpairs", required_argument, 0, 'k'}, |
| {"flags", required_argument, 0, 'F'}, |
| {"kpi-mode", no_argument, 0, 'K'}, |
| {"plus", no_argument, 0, 'P'}, |
| {"event-trigger", no_argument, 0, 'E'}, |
| {"effect-path", required_argument, 0, 'e'}, |
| {"bt-addr", required_argument, 0, 'A'}, |
| {"query drift", no_argument, 0, 'q'}, |
| {"drift correction", no_argument, 0, 'Q'}, |
| {"device-nodeurl",required_argument, 0, 'u'}, |
| {"mode", required_argument, 0, 'm'}, |
| {"effect-preset", required_argument, 0, 'p'}, |
| {"effect-strength", required_argument, 0, 'S'}, |
| {"render-format", required_argument, 0, 'x'}, |
| {"timestamp-file", required_argument, 0, 'y'}, |
| {"intr-strm", required_argument, 0, 'i'}, |
| {"device-config", required_argument, 0, 'C'}, |
| {"play-list", required_argument, 0, 'g'}, |
| {"help", no_argument, 0, 'h'}, |
| {0, 0, 0, 0} |
| }; |
| |
| int opt = 0; |
| int option_index = 0; |
| |
| proxy_params.hdr.riff_id = ID_RIFF; |
| proxy_params.hdr.riff_sz = 0; |
| proxy_params.hdr.riff_fmt = ID_WAVE; |
| proxy_params.hdr.fmt_id = ID_FMT; |
| proxy_params.hdr.fmt_sz = 16; |
| proxy_params.hdr.audio_format = FORMAT_PCM; |
| proxy_params.hdr.num_channels = 2; |
| proxy_params.hdr.sample_rate = 44100; |
| proxy_params.hdr.byte_rate = proxy_params.hdr.sample_rate * proxy_params.hdr.num_channels * 2; |
| proxy_params.hdr.block_align = proxy_params.hdr.num_channels * 2; |
| proxy_params.hdr.bits_per_sample = 16; |
| proxy_params.hdr.data_id = ID_DATA; |
| proxy_params.hdr.data_sz = 0; |
| |
| while ((opt = getopt_long(argc, |
| argv, |
| "-f:r:c:b:d:s:v:V:l:t:a:w:k:PD:KF:Ee:A:u:m:S:C:p::x:y:qQh:i:h:g:", |
| long_options, |
| &option_index)) != -1) { |
| |
| fprintf(log_file, "for argument %c, value is %s\n", opt, optarg); |
| |
| switch (opt) { |
| case 'f': |
| stream_param[i].filename = optarg; |
| break; |
| case 'r': |
| stream_param[i].config.offload_info.sample_rate = atoi(optarg); |
| stream_param[i].config.sample_rate = atoi(optarg); |
| break; |
| case 'c': |
| stream_param[i].channels = atoi(optarg); |
| stream_param[i].config.channel_mask = audio_channel_out_mask_from_count(atoi(optarg)); |
| break; |
| case 'b': |
| stream_param[i].config.offload_info.bit_width = atoi(optarg); |
| break; |
| case 'd': |
| stream_param[i].output_device = atoll(optarg); |
| break; |
| case 's': |
| stream_param[i].input_device = atoll(optarg); |
| break; |
| case 'v': |
| vol_level = atof(optarg); |
| break; |
| case 'V': |
| enable_dump = atof(optarg); |
| break; |
| case 'l': |
| log_filename = optarg; |
| if (strcasecmp(log_filename, "stdout") && |
| strcasecmp(log_filename, "1") && |
| (log_file = fopen(log_filename,"wb")) == NULL) { |
| fprintf(log_file, "Cannot open log file %s\n", log_filename); |
| fprintf(stderr, "Cannot open log file %s\n", log_filename); |
| /* continue to log to std out. */ |
| log_file = stdout; |
| } |
| break; |
| case 'i': |
| stream_param[i].interactive_strm = atoi(optarg); |
| break; |
| case 't': |
| stream_param[i].filetype = atoi(optarg); |
| break; |
| case 'a': |
| stream_param[i].aac_fmt_type = atoi(optarg); |
| break; |
| case 'w': |
| stream_param[i].wma_fmt_type = atoi(optarg); |
| break; |
| case 'k': |
| get_kvpairs_string(optarg, "mixer_ctrl", mixer_ctrl_name); |
| printf("%s, mixer_ctrl_name- %s\n", __func__, mixer_ctrl_name); |
| if(strncmp(mixer_ctrl_name, "downmix", 7) == 0) { |
| stream_param[i].mix_ctrl = QAHW_PARAM_CH_MIX_MATRIX_PARAMS; |
| |
| get_kvpairs_string(optarg, "c", input_ch); |
| stream_param[i].mm_params_downmix.num_input_channels = atoi(input_ch); |
| get_kvpairs_string(optarg, "o", output_ch); |
| stream_param[i].mm_params_downmix.num_output_channels = atoi(output_ch); |
| get_kvpairs_string(optarg, "I", input_ch_map); |
| get_kvpairs_string(optarg, "O", output_ch_map); |
| get_kvpairs_string(optarg, "M", mixer_coeff); |
| |
| extract_channel_mapping(stream_param[i].mm_params_downmix.input_channel_map, input_ch_map); |
| stream_param[i].mm_params_downmix.has_input_channel_map = 1; |
| fprintf(log_file, "\ndownmix Input channel mapping: "); |
| for (iter_i= 0; iter_i < stream_param[i].mm_params_downmix.num_input_channels; iter_i++) { |
| fprintf(log_file, "0x%x, ", stream_param[i].mm_params_downmix.input_channel_map[iter_i]); |
| } |
| |
| extract_channel_mapping(stream_param[i].mm_params_downmix.output_channel_map, output_ch_map); |
| stream_param[i].mm_params_downmix.has_output_channel_map = 1; |
| fprintf(log_file, "\ndownmix Output channel mapping: "); |
| for (iter_i = 0; iter_i < stream_param[i].mm_params_downmix.num_output_channels; iter_i++) |
| fprintf(log_file, "0x%x, ", stream_param[i].mm_params_downmix.output_channel_map[iter_i]); |
| |
| |
| extract_mixer_coeffs(&stream_param[i].mm_params_downmix, mixer_coeff); |
| stream_param[i].mm_params_downmix.has_mixer_coeffs = 1; |
| fprintf(log_file, "\ndownmix mixer coeffs:\n"); |
| for (iter_i = 0; iter_i < stream_param[i].mm_params_downmix.num_output_channels; iter_i++){ |
| for (iter_j = 0; iter_j < stream_param[i].mm_params_downmix.num_input_channels; iter_j++){ |
| fprintf(log_file, "%.2f ",stream_param[i].mm_params_downmix.mixer_coeffs[iter_i][iter_j]); |
| } |
| fprintf(log_file, "\n"); |
| } |
| |
| } else if(strncmp(mixer_ctrl_name, "pan_scale", 9) == 0) { |
| stream_param[i].pan_scale_ctrl = QAHW_PARAM_OUT_MIX_MATRIX_PARAMS; |
| |
| get_kvpairs_string(optarg, "c", input_ch); |
| stream_param[i].mm_params_pan_scale.num_input_channels = atoi(input_ch); |
| get_kvpairs_string(optarg, "o", output_ch); |
| stream_param[i].mm_params_pan_scale.num_output_channels = atoi(output_ch); |
| get_kvpairs_string(optarg, "I", input_ch_map); |
| get_kvpairs_string(optarg, "O", output_ch_map); |
| get_kvpairs_string(optarg, "M", mixer_coeff); |
| |
| extract_channel_mapping(stream_param[i].mm_params_pan_scale.input_channel_map, input_ch_map); |
| stream_param[i].mm_params_pan_scale.has_input_channel_map = 1; |
| fprintf(log_file, "\n pan_sclae Input channel mapping: "); |
| for (iter_i= 0; iter_i < stream_param[i].mm_params_pan_scale.num_input_channels; iter_i++) { |
| fprintf(log_file, "0x%x, ", stream_param[i].mm_params_pan_scale.input_channel_map[iter_i]); |
| } |
| |
| extract_channel_mapping(stream_param[i].mm_params_pan_scale.output_channel_map, output_ch_map); |
| stream_param[i].mm_params_pan_scale.has_output_channel_map = 1; |
| fprintf(log_file, "\n pan_scale Output channel mapping: "); |
| for (iter_i = 0; iter_i < stream_param[i].mm_params_pan_scale.num_output_channels; iter_i++) |
| fprintf(log_file, "0x%x, ", stream_param[i].mm_params_pan_scale.output_channel_map[iter_i]); |
| |
| extract_mixer_coeffs(&stream_param[i].mm_params_pan_scale, mixer_coeff); |
| stream_param[i].mm_params_pan_scale.has_mixer_coeffs = 1; |
| fprintf(log_file, "\n pan_scale mixer coeffs:\n"); |
| for (iter_i = 0; iter_i < stream_param[i].mm_params_pan_scale.num_output_channels; iter_i++){ |
| for (iter_j = 0; iter_j < stream_param[i].mm_params_pan_scale.num_input_channels; iter_j++){ |
| fprintf(log_file, "%.2f ",stream_param[i].mm_params_pan_scale.mixer_coeffs[iter_i][iter_j]); |
| } |
| fprintf(log_file, "\n"); |
| } |
| |
| } else { |
| stream_param[i].kvpair_values = optarg; |
| } |
| break; |
| case 'D': |
| proxy_params.acp.file_name = optarg; |
| break; |
| case 'K': |
| kpi_mode = true; |
| break; |
| case 'F': |
| stream_param[i].flags = atoll(optarg); |
| stream_param[i].flags_set = true; |
| break; |
| case 'E': |
| event_trigger = true; |
| break; |
| case 'e': |
| stream_param[i].effect_index = atoi(optarg); |
| if (stream_param[i].effect_index < 0 || stream_param[i].effect_index >= EFFECT_MAX) { |
| fprintf(log_file, "Invalid effect type %d\n", stream_param[i].effect_index); |
| fprintf(stderr, "Invalid effect type %d\n", stream_param[i].effect_index); |
| stream_param[i].effect_index = -1; |
| } else if (stream_param[i].effect_index == 3) { |
| // visualizer is a special effect that is not perceivable by hearing |
| // hence, add as an exception in test app. |
| fprintf(log_file, "visualizer effect testing is not available\n"); |
| stream_param[i].effect_index = -1; |
| } else { |
| stream_param[i].ethread_func = effect_thread_funcs[stream_param[i].effect_index]; |
| } |
| break; |
| case 'p': |
| stream_param[i].effect_preset_strength = atoi(optarg); |
| break; |
| case 'S': |
| stream_param[i].effect_preset_strength = atoi(optarg); |
| break; |
| case 'A': |
| ba = optarg; |
| break; |
| case 'q': |
| stream_param[i].drift_query = true; |
| break; |
| case 'Q': |
| stream_param[i].drift_query = true; |
| stream_param[i].drift_correction = true; |
| break; |
| case 'P': |
| if(i >= MAX_PLAYBACK_STREAMS - 1) { |
| fprintf(log_file, "cannot have more than %d streams\n", MAX_PLAYBACK_STREAMS); |
| fprintf(stderr, "cannot have more than %d streams\n", MAX_PLAYBACK_STREAMS); |
| return 0; |
| } |
| i++; |
| fprintf(log_file, "Stream index incremented to %d\n", i); |
| break; |
| case 'u': |
| stream_param[i].device_url = optarg; |
| break; |
| case 'm': |
| stream_param[i].usb_mode = atoi(optarg); |
| break; |
| case 'x': |
| render_format = atoi(optarg); |
| break; |
| case 'y': |
| stream_param[i].timestamp_filename = optarg; |
| break; |
| case 'C': |
| fprintf(log_file, " In Device config \n"); |
| fprintf(stderr, " In Device config \n"); |
| send_device_config = true; |
| |
| //Read Sample Rate |
| if (optind < argc && *argv[optind] != '-') { |
| device_cfg_params.sample_rate = atoi(optarg); |
| fprintf(log_file, " Device config :::: sample_rate - %d \n", device_cfg_params.sample_rate); |
| fprintf(stderr, " Device config :::: sample_rate - %d \n", device_cfg_params.sample_rate); |
| } |
| |
| //Read Channels |
| if (optind < argc && *argv[optind] != '-') { |
| device_cfg_params.channels = atoi(argv[optind]); |
| optind++; |
| fprintf(log_file, " Device config :::: channels - %d \n", device_cfg_params.channels); |
| fprintf(stderr, " Device config :::: channels - %d \n", device_cfg_params.channels); |
| } |
| |
| //Read Bit width |
| if (optind < argc && *argv[optind] != '-') { |
| device_cfg_params.bit_width = atoi(argv[optind]); |
| optind++; |
| fprintf(log_file, " Device config :::: bit_width - %d \n", device_cfg_params.bit_width); |
| fprintf(stderr, " Device config :::: bit_width - %d \n", device_cfg_params.bit_width); |
| } |
| |
| //Read Format |
| if (optind < argc && *argv[optind] != '-') { |
| device_cfg_params.format = atoi(argv[optind]); |
| optind++; |
| fprintf(log_file, " Device config :::: format - %d \n", device_cfg_params.format); |
| fprintf(stderr, " Device config :::: format - %d \n", device_cfg_params.format); |
| } |
| |
| //Read Device |
| if (optind < argc && *argv[optind] != '-') { |
| device_cfg_params.device = atoi(argv[optind]); |
| optind++; |
| fprintf(log_file, " Device config :::: device - %d \n", device_cfg_params.device); |
| fprintf(stderr, " Device config :::: device - %d \n", device_cfg_params.device); |
| } |
| |
| //Read Channel Map |
| while ((optind < argc && *argv[optind] != '-') && (chmap_iter < device_cfg_params.channels)) { |
| device_cfg_params.channel_map[chmap_iter] = atoi(argv[optind]); |
| optind++; |
| fprintf(log_file, " Device config :::: channel_map[%d] - %d \n", chmap_iter, device_cfg_params.channel_map[chmap_iter]); |
| fprintf(stderr, " Device config :::: channel_map[%d] - %d \n", chmap_iter, device_cfg_params.channel_map[chmap_iter]); |
| chmap_iter++; |
| } |
| |
| //Read Channel Allocation |
| if (optind < argc && *argv[optind] != '-') { |
| device_cfg_params.channel_allocation = atoi(argv[optind]); |
| optind++; |
| fprintf(log_file, " Device config :::: channel_allocation - %d \n", device_cfg_params.channel_allocation); |
| fprintf(stderr, " Device config :::: channel_allocation - %d \n", device_cfg_params.channel_allocation); |
| } |
| break; |
| case 'g': |
| break; |
| case 'h': |
| usage(); |
| hal_test_qap_usage(); |
| return 0; |
| break; |
| |
| } |
| } |
| |
| wakelock_acquired = request_wake_lock(wakelock_acquired, true); |
| num_of_streams = i+1; |
| /* Caution: Below ADL log shouldnt be altered without notifying automation APT since it used |
| * for automation testing |
| */ |
| fprintf(log_file, "ADL: Starting audio hal tests for streams : %d\n", num_of_streams); |
| |
| if (kpi_mode == true && num_of_streams > 1) { |
| fprintf(log_file, "kpi-mode is not supported for multi-playback usecase\n"); |
| fprintf(stderr, "kpi-mode is not supported for multi-playback usecase\n"); |
| goto exit; |
| } else if (event_trigger == true && num_of_streams > 1) { |
| fprintf(log_file, "event_trigger is not supported for multi-playback usecase\n"); |
| fprintf(stderr, "event_trigger is not supported for multi-playback usecase\n"); |
| goto exit; |
| } |
| |
| if (num_of_streams > 1 && stream_param[num_of_streams-1].output_device & AUDIO_DEVICE_OUT_PROXY) { |
| fprintf(log_file, "Proxy thread is not supported for multi-playback usecase\n"); |
| fprintf(stderr, "Proxy thread is not supported for multi-playback usecase\n"); |
| goto exit; |
| } |
| |
| /* Register the SIGINT to close the App properly */ |
| if (signal(SIGINT, stop_signal_handler) == SIG_ERR) { |
| fprintf(log_file, "Failed to register SIGINT:%d\n",errno); |
| fprintf(stderr, "Failed to register SIGINT:%d\n",errno); |
| } |
| |
| /* Check for Dual main content */ |
| if (num_of_streams >= 2) { |
| is_dual_main = true; |
| |
| for(i = 0; i < num_of_streams; i++) { |
| fprintf(log_file, "is_dual_main - %d stream_param[i].flags - %d\n", is_dual_main, stream_param[i].flags); |
| is_dual_main = is_dual_main && (stream_param[i].flags & AUDIO_OUTPUT_FLAG_MAIN); |
| fprintf(log_file, "is_dual_main - %d stream_param[i].flags - %d\n", is_dual_main, stream_param[i].flags); |
| } |
| |
| } |
| |
| if (is_qap_session_active(argc, argv, kvp_string)) { |
| char *file_name = NULL; |
| char *cmd_kvp_str[100] = {NULL}; |
| char *play_list_kvp_str[100] = {NULL}; |
| int i = 0, j = 0; |
| qahw_module_handle_t *qap_out_hal_handle = NULL; |
| |
| stream = &stream_param[i]; |
| qap_out_hal_handle = load_hal(stream->output_device); |
| if (qap_out_hal_handle == NULL) { |
| fprintf(stderr, "Failed log load HAL\n"); |
| goto exit; |
| } |
| |
| file_name = (char*) check_for_playlist(kvp_string); |
| fprintf(stderr, "%s file_name is %s \n", __FUNCTION__, file_name); |
| if (file_name != NULL) { |
| FILE *fp = fopen(file_name, "r+"); |
| if (fp != NULL) { |
| get_play_list(fp, &stream_param, &num_of_streams, cmd_kvp_str); |
| for (j = 0; j < num_of_streams; j++) { |
| play_list_kvp_str[j] = strdup(cmd_kvp_str[j]); |
| } |
| rc = start_playback_through_qap_playlist(play_list_kvp_str, num_of_streams, kvp_string, stream_param, qap_wrapper_session_active, qap_out_hal_handle); |
| if (rc != 0) { |
| fprintf(stderr, "QAP playback failed\n"); |
| } |
| } else { |
| fprintf(stderr, "%s file open failed\nnd errno is %d", __FUNCTION__, errno); |
| } |
| } else { |
| rc = start_playback_through_qap(kvp_string, num_of_streams, qap_out_hal_handle); |
| if (rc != 0) { |
| fprintf(stderr, "QAP playback failed\n"); |
| } |
| } |
| goto exit; |
| } |
| for (i = 0; i < num_of_streams; i++) { |
| stream = &stream_param[i]; |
| |
| if ((kpi_mode == false) && |
| (AUDIO_DEVICE_NONE == stream->input_device)){ |
| if (stream_param[PRIMARY_STREAM_INDEX].filename == nullptr) { |
| fprintf(log_file, "Primary file name is must for non kpi-mode\n"); |
| fprintf(stderr, "Primary file name is must for non kpi-mode\n"); |
| goto exit; |
| } |
| } |
| |
| if (stream->output_device != AUDIO_DEVICE_NONE) |
| if ((stream->qahw_out_hal_handle = load_hal(stream->output_device)) <= 0) |
| goto exit; |
| |
| if (stream->input_device != AUDIO_DEVICE_NONE) |
| if ((stream->qahw_in_hal_handle = load_hal(stream->input_device))== 0) |
| goto exit; |
| |
| if ((AUDIO_DEVICE_NONE != stream->output_device) && |
| (AUDIO_DEVICE_NONE != stream->input_device)) |
| /* |
| * hal loopback at what params we need to probably detect. |
| */ |
| if(detect_stream_params(stream) < 0) |
| goto exit; |
| |
| if (stream->filename) { |
| if ((stream->file_stream = fopen(stream->filename, "r"))== NULL) { |
| fprintf(log_file, "Cannot open audio file %s\n", stream->filename); |
| fprintf(stderr, "Cannot open audio file %s\n", stream->filename); |
| goto exit; |
| } |
| fprintf(log_file, "Playing from:%s\n", stream->filename); |
| get_file_format(&stream_param[i]); |
| } else if (AUDIO_DEVICE_NONE != stream->input_device) { |
| fprintf(log_file, "Playing from device:%x\n", stream->input_device); |
| fprintf(log_file, "Playing from url:%s\n", stream->device_url); |
| fprintf(log_file, "setting up input hal and stream:%s\n", stream->device_url); |
| |
| rc = qahw_open_input_stream(stream->qahw_in_hal_handle, |
| stream->handle, |
| stream->input_device, |
| &(stream->config), |
| &(stream->in_handle), |
| AUDIO_INPUT_FLAG_NONE, |
| stream->device_url, |
| AUDIO_SOURCE_UNPROCESSED); |
| if (rc) { |
| fprintf(log_file, "input stream could not be re-opened\n"); |
| fprintf(stderr, "input stream could not be re-opened\n"); |
| return rc; |
| } |
| stream->flags = AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD|AUDIO_OUTPUT_FLAG_NON_BLOCKING; |
| stream->flags |= AUDIO_OUTPUT_FLAG_DIRECT; |
| } else if (kpi_mode == true) |
| stream->config.format = stream->config.offload_info.format = AUDIO_FORMAT_PCM_16_BIT; |
| |
| if (stream->output_device & AUDIO_DEVICE_OUT_PROXY) |
| fprintf(log_file, "Saving pcm data to file: %s\n", proxy_params.acp.file_name); |
| |
| /* Set device connection state for HDMI */ |
| if ((stream->output_device & AUDIO_DEVICE_OUT_AUX_DIGITAL) || |
| (stream->output_device & AUDIO_DEVICE_OUT_BLUETOOTH_A2DP)) { |
| char param[100] = {0}; |
| uint32_t device = 0; |
| |
| if (stream->output_device & AUDIO_DEVICE_OUT_AUX_DIGITAL) |
| device = AUDIO_DEVICE_OUT_AUX_DIGITAL; |
| else if (stream->output_device & AUDIO_DEVICE_OUT_BLUETOOTH_A2DP) |
| device = AUDIO_DEVICE_OUT_BLUETOOTH_A2DP; |
| |
| snprintf(param, sizeof(param), "%s=%d", "connect", device); |
| qahw_set_parameters(stream->qahw_out_hal_handle, param); |
| fprintf(log_file, "Sending Connect Event: %s\n", param); |
| fprintf(stderr, "Sending Connect Event: %s\n", param); |
| } |
| |
| fprintf(log_file, "stream %d: File Type:%d\n", stream->stream_index, stream->filetype); |
| fprintf(log_file, "stream %d: Audio Format:%d\n", stream->stream_index, stream->config.offload_info.format); |
| fprintf(log_file, "stream %d: Output Device:%d\n", stream->stream_index, stream->output_device); |
| fprintf(log_file, "stream %d: Output Flags:%d\n", stream->stream_index, stream->flags); |
| fprintf(log_file, "stream %d: Sample Rate:%d\n", stream->stream_index, stream->config.offload_info.sample_rate); |
| fprintf(log_file, "stream %d: Channels:%d\n", stream->stream_index, stream->channels); |
| fprintf(log_file, "stream %d: Bitwidth:%d\n", stream->stream_index, stream->config.offload_info.bit_width); |
| fprintf(log_file, "stream %d: AAC Format Type:%d\n", stream->stream_index, stream->aac_fmt_type); |
| fprintf(log_file, "stream %d: Kvpair Values:%s\n", stream->stream_index, stream->kvpair_values); |
| fprintf(log_file, "Log file:%s\n", log_filename); |
| fprintf(log_file, "Volume level:%f\n", vol_level); |
| |
| stream->config.offload_info.version = AUDIO_OFFLOAD_INFO_VERSION_CURRENT; |
| stream->config.offload_info.size = sizeof(audio_offload_info_t); |
| |
| if (stream->filetype == FILE_APTX) { |
| if (ba != NULL) { |
| parse_aptx_dec_bt_addr(ba, &aptx_params); |
| payload = (qahw_param_payload)aptx_params; |
| param_id = QAHW_PARAM_APTX_DEC; |
| fprintf(log_file, "Send BT addr nap %d, uap %d lap %d to HAL\n", aptx_params.bt_addr.nap, |
| aptx_params.bt_addr.uap, aptx_params.bt_addr.lap); |
| rc = qahw_set_param_data(stream->qahw_out_hal_handle, param_id, &payload); |
| if (rc != 0) |
| fprintf(log_file, "Error.Failed Set BT addr\n"); |
| fprintf(stderr, "Error.Failed Set BT addr\n"); |
| } else { |
| fprintf(log_file, "BT addr is NULL, Need valid BT addr for aptx file playback to work\n"); |
| fprintf(stderr, "BT addr is NULL, Need valid BT addr for aptx file playback to work\n"); |
| goto exit; |
| } |
| } |
| |
| if (send_device_config) { |
| payload = (qahw_param_payload)device_cfg_params; |
| rc = qahw_set_param_data(stream->qahw_out_hal_handle, QAHW_PARAM_DEVICE_CONFIG, &payload); |
| if (rc != 0) { |
| fprintf(log_file, "Set Device Config Failed\n"); |
| fprintf(stderr, "Set Device Config Failed\n"); |
| } |
| } |
| |
| if (is_dual_main && i >= 2 ) { |
| stream_param[i].play_later = true; |
| fprintf(log_file, "stream %d: play_later = %d\n", i, stream_param[i].play_later); |
| } |
| |
| rc = pthread_create(&playback_thread[i], NULL, start_stream_playback, (void *)&stream_param[i]); |
| if (rc) { |
| fprintf(log_file, "stream %d: failed to create thread\n", stream->stream_index); |
| fprintf(stderr, "stream %d: failed to create thread\n", stream->stream_index); |
| exit(0); |
| } |
| |
| thread_active[i] = true; |
| |
| } |
| |
| exit: |
| for (i=0; i<MAX_PLAYBACK_STREAMS; i++) { |
| if(thread_active[i]) |
| pthread_join(playback_thread[i], NULL); |
| } |
| |
| if(qap_wrapper_session_active) { |
| qap_wrapper_session_close(); |
| } |
| |
| /* |
| * reset device connection state for HDMI and close the file streams |
| */ |
| for (i = 0; i < num_of_streams; i++) { |
| if ((stream_param[i].output_device == AUDIO_DEVICE_OUT_AUX_DIGITAL) || |
| (stream_param[i].output_device == AUDIO_DEVICE_OUT_BLUETOOTH_A2DP)) { |
| char param[100] = {0}; |
| snprintf(param, sizeof(param), "%s=%d", "disconnect", stream_param[i].output_device); |
| qahw_set_parameters(stream_param[i].qahw_out_hal_handle, param); |
| } |
| |
| if (stream_param[i].file_stream != nullptr) |
| fclose(stream_param[i].file_stream); |
| else if (AUDIO_DEVICE_NONE != stream_param[i].input_device) { |
| if (stream != NULL && stream->in_handle) { |
| rc = qahw_close_input_stream(stream->in_handle); |
| if (rc) { |
| fprintf(log_file, "input stream could not be closed\n"); |
| fprintf(stderr, "input stream could not be closed\n"); |
| return rc; |
| } |
| } |
| } |
| } |
| |
| rc = unload_hals(); |
| |
| if ((log_file != stdout) && (log_file != nullptr)) |
| fclose(log_file); |
| |
| wakelock_acquired = request_wake_lock(wakelock_acquired, false); |
| /* Caution: Below ADL log shouldnt be altered without notifying automation APT since it used |
| * for automation testing |
| */ |
| fprintf(log_file, "\nADL: BYE BYE\n"); |
| return 0; |
| } |