/* qcptest.c - native QCELP/EVRC test application * * Based on native pcm test application platform/system/extras/sound/playwav.c * * Copyright (C) 2008 The Android Open Source Project * Copyright (c) 2009-2010, 2012 The Linux Foundation. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * 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. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "audiotest_def.h" #define QCELP_DEVICE_NODE "/dev/msm_qcelp" #define EVRC_DEVICE_NODE "/dev/msm_evrc" #define QCELP_PKT_SIZE 36 #define EVRC_PKT_SIZE 24 #define EOS 0x00000001 static int in_size =0; static int out_size =0; static int file_write=0; static int eos_ack=0; struct riff_chunk { char riff[4]; unsigned int s_riff; char qlcm[4]; } __attribute__ ((packed)); struct q_info { /* UNIQUE ID of the codec */ unsigned int data1; unsigned short data2; unsigned short data3; char data4[8]; /* Codec Info */ unsigned short ver; char name[80]; unsigned short abps; /* average bits per sec of the codec */ unsigned short bytes_per_pkt; unsigned short samp_per_block; unsigned short samp_per_sec; unsigned short bits_per_samp; /* Rate Header fmt info */ unsigned char vr_num_of_rates; unsigned char rvd1[3]; unsigned short vr_bytes_per_pkt[8]; unsigned int rvd2[5]; } __attribute__ ((packed)); struct fmt_chunk { char fmt[4]; unsigned int s_fmt; char mjr; char mnr; struct q_info info; } __attribute__ ((packed)); struct qcp_header { struct riff_chunk riff; struct fmt_chunk fmt; } __attribute__ ((packed)); struct vrat_chunk { unsigned char vrat[4]; unsigned int s_vrat; unsigned int v_rate; unsigned int size_in_pkts; } __attribute__ ((packed)); struct data_chunk { unsigned char data[4]; unsigned int s_data; } __attribute__ ((packed)); struct type_size { unsigned char size; unsigned char rate; }; /* http://ccrma.stanford.edu/courses/422/projects/WaveFormat/ */ struct wav_header { /* Simple wave header */ char Chunk_ID[4]; /* Store "RIFF" */ unsigned int Chunk_size; char Riff_type[4]; /* Store "WAVE" */ char Chunk_ID1[4]; /* Store "fmt " */ unsigned int Chunk_fmt_size; unsigned short Compression_code; /*1 - 65,535, 1 - pcm */ unsigned short Number_Channels; /* 1 - 65,535 */ unsigned int Sample_rate; /* 1 - 0xFFFFFFFF */ unsigned int Bytes_Sec; /*1 - 0xFFFFFFFF */ unsigned short Block_align; /* 1 - 65,535 */ unsigned short Significant_Bits_sample; /* 1 - 65,535 */ char Chunk_ID2[4]; /* Store "data" */ unsigned int Chunk_data_size; } __attribute__ ((packed)); static struct wav_header append_header = { {'R', 'I', 'F', 'F'}, 0, {'W', 'A', 'V', 'E'}, {'f', 'm', 't', ' '}, 16, 1, 1, 8000, 16000, 2, 16, {'d', 'a', 't', 'a'}, 0 }; static pthread_mutex_t avail_lock; static pthread_cond_t avail_cond; static pthread_mutex_t consumed_lock; static pthread_cond_t consumed_cond; static int data_is_available = 0; static int data_is_consumed = 0; static int in_free_indx; static int in_data_indx; static int out_free_indx; static int out_data_indx; struct meta_in{ unsigned short offset; long long timestamp; unsigned int nflags; } __attribute__ ((packed)); struct meta_out{ unsigned short offset; long long timestamp; unsigned int nflags; unsigned short errflag; unsigned short sample_frequency; unsigned short channel; unsigned int tick_count; } __attribute__ ((packed)); typedef struct TIMESTAMP{ unsigned long LowPart; unsigned long HighPart; } __attribute__ ((packed)) TIMESTAMP; struct meta_in_q6{ unsigned char rsv[18]; unsigned short offset; TIMESTAMP ntimestamp; unsigned int nflags; } __attribute__ ((packed)); struct meta_out_dsp{ unsigned int offset_to_frame; unsigned int frame_size; unsigned int encoded_pcm_samples; unsigned int msw_ts; unsigned int lsw_ts; unsigned int nflags; } __attribute__ ((packed)); struct dec_meta_out{ unsigned int rsv[7]; unsigned int num_of_frames; struct meta_out_dsp meta_out_dsp[]; } __attribute__ ((packed)); #define QCPTEST_IBUFSZ (32*1024) #define QCPTEST_NUM_IBUF 2 #define QCPTEST_IPMEM_SZ (QCPTEST_IBUFSZ * QCPTEST_NUM_IBUF) #define QCPTEST_OBUFSZ (32*1024) #define QCPTEST_NUM_OBUF 2 #define QCPTEST_OPMEM_SZ (QCPTEST_OBUFSZ * QCPTEST_NUM_OBUF) #ifdef _ANDROID_ static const char *cmdfile = "/data/audio_test"; /* static const char *outfile = "/data/pcm.wav"; */ #else static const char *cmdfile = "/tmp/audio_test"; /* static const char *outfile = "/tmp/pcm.wav"; */ #endif struct msm_audio_aio_buf aio_ip_buf[QCPTEST_NUM_IBUF]; struct msm_audio_aio_buf aio_op_buf[QCPTEST_NUM_OBUF]; static void wait_for_data(void) { pthread_mutex_lock(&avail_lock); while (data_is_available == 0) { pthread_cond_wait(&avail_cond, &avail_lock); } data_is_available = 0; pthread_mutex_unlock(&avail_lock); } static void data_available(void) { pthread_mutex_lock(&avail_lock); if (data_is_available == 0) { data_is_available = 1; pthread_cond_broadcast(&avail_cond); } pthread_mutex_unlock(&avail_lock); } static void wait_for_data_consumed(void) { pthread_mutex_lock(&consumed_lock); while (data_is_consumed == 0) { pthread_cond_wait(&consumed_cond, &consumed_lock); } data_is_consumed = 0; pthread_mutex_unlock(&consumed_lock); } static void data_consumed(void ) { pthread_mutex_lock(&consumed_lock); if (data_is_consumed == 0) { data_is_consumed = 1; pthread_cond_broadcast(&consumed_cond); } pthread_mutex_unlock(&consumed_lock); } static void create_wav_header(int Datasize) { append_header.Chunk_size = Datasize + 8 + 16 + 12; append_header.Chunk_data_size = Datasize; return; } static void *qcp_dec(void *arg) { int fd, ret_val = 0; struct audio_pvt_data *audio_data = (struct audio_pvt_data *) arg; int afd = audio_data->afd; int len, total_len; struct meta_out meta; len = 0; total_len = 0; fd = open(audio_data->outfile, O_RDWR | O_CREAT, S_IRWXU | S_IRWXG | S_IRWXO); if (fd < 0) { printf("Err while opening file decoder output file \n"); pthread_exit((void *)ret_val); } printf(" qcp_read Thread \n"); lseek(fd, 44, SEEK_SET); /* Set Space for Wave Header */ do { if (audio_data->suspend == 1) { printf("enter suspend mode\n"); ioctl(afd, AUDIO_STOP, 0); while (audio_data->suspend == 1) sleep(1); ioctl(afd, AUDIO_START, 0); printf("exit suspend mode\n"); } len = read(afd, audio_data->recbuf, audio_data->recsize); #ifdef DEBUG_LOCAL printf(" Read = %d PCM samples\n", len/2); #endif if (len < 0) { if (audio_data->flush_enable == 1) { printf(" Flush in progress, sleep 5 ms \n"); usleep(5000); printf(" total len 0x%8x \n", total_len); continue; } else { printf(" error reading the PCM samples \n"); goto fail; } } else if (len != 0) { memcpy(&meta, audio_data->recbuf, sizeof(struct meta_out)); #ifdef DEBUG_LOCAL printf("\t\tMeta Out Timestamp: %lld\n", meta.timestamp); #endif if (meta.nflags == 1) { printf("Reached end of file\n"); break; } len = (len - sizeof(struct meta_out)); if (len > 0) { if (write(fd, (audio_data->recbuf + sizeof(struct meta_out)), len) != len) { printf(" error writing the PCM \ samples to file \n"); goto fail; } } } else if (len == 0) printf("Unexpected case: read count zero\n"); total_len += len; } while (1); create_wav_header(total_len); lseek(fd, 0, SEEK_SET); write(fd, (char *)&append_header, 44); close(fd); pthread_exit((void *)ret_val); fail: close(fd); pthread_exit((void *)ret_val); return NULL; } static void *event_notify(void *arg) { long ret_drv; struct audio_pvt_data *audio_data = (struct audio_pvt_data *) arg; int afd = audio_data->afd; struct msm_audio_event suspend_event; do { printf("event_notify thread started\n"); suspend_event.timeout_ms = 0; ret_drv = ioctl(afd, AUDIO_GET_EVENT, &suspend_event); if (ret_drv < 0) { printf("event_notify thread exiting: \ Got Abort event or timedout\n"); break; } else { if (suspend_event.event_type == AUDIO_EVENT_SUSPEND) { printf("event_notify: RECEIVED EVENT FROM \ DRIVER OF TYPE: AUDIO_EVENT_SUSPEND: \ %d\n", suspend_event.event_type); audio_data->suspend = 1; sleep(1); } else if (suspend_event.event_type == AUDIO_EVENT_RESUME) { printf("event_notify: RECEIVED EVENT FROM \ DRIVER OF TYPE: AUDIO_EVENT_RESUME : \ %d\n", suspend_event.event_type); audio_data->suspend = 0; } } } while (1); return NULL; } /* Get File content and create meta */ static int fill_buffer_8660(void *buf, unsigned sz, void *cookie) { struct meta_in_q6 meta; struct audio_pvt_data *audio_data = (struct audio_pvt_data *) cookie; unsigned cpy_size = (sz < audio_data->avail?sz:audio_data->avail); #ifdef DEBUG_LOCAL char *temp; printf("%s:frame count %d\n", __func__, audio_data->frame_count); #endif if (audio_data->mode) { meta.ntimestamp.HighPart = 0; meta.ntimestamp.LowPart = (unsigned long long)(audio_data->frame_count * 0x10000); meta.offset = sizeof(struct meta_in_q6); audio_data->frame_count++; #ifdef DEBUG_LOCAL printf("Meta In High part is %lu\n", meta.ntimestamp.HighPart); printf("Meta In Low part is %lu\n", meta.ntimestamp.LowPart); printf("Meta In ntimestamp: %llu\n", (((unsigned long long) meta.ntimestamp.HighPart << 32) + meta.ntimestamp.LowPart)); printf("meta in size %d\n", sizeof(struct meta_in_q6)); #endif if (audio_data->avail == 0) { /* End of file, send EOS */ meta.nflags = EOS; memcpy(buf, &meta, sizeof(struct meta_in_q6)); return (sizeof(struct meta_in_q6)); } meta.nflags = 0; memcpy(buf, &meta, sizeof(struct meta_in_q6)); memcpy(((char *)buf + sizeof(struct meta_in_q6)), audio_data->next, cpy_size); #ifdef DEBUG_LOCAL temp = ((char*)buf + sizeof(struct meta_in_q6)); printf("\nFirst three bytes 0x%2x:0x%2x:0x%2x\n", *temp, *(temp+1), *(temp+2)); #endif } else { if (audio_data->avail == 0) { return 0; } audio_data->frame_count++; memcpy((char *)buf, audio_data->next, cpy_size); #ifdef DEBUG_LOCAL temp = (buf); printf("\nFirst three bytes 0x%2x:0x%2x:0x%2x\n", *temp, *(temp+1), *(temp+2)); #endif } audio_data->next += cpy_size; audio_data->avail -= cpy_size; if (audio_data->mode) return cpy_size + sizeof(struct meta_in_q6); else return cpy_size; } static void *qcp_read_thread_8660(void *arg) { struct audio_pvt_data *audio_data = (struct audio_pvt_data *) arg; int afd = audio_data->afd; int total_len; int fd = 0; struct dec_meta_out *meta_out_ptr; struct meta_out_dsp *meta_out_dsp; struct msm_audio_aio_buf aio_buf; struct msm_audio_config config; #ifdef AUDIOV2 unsigned short dec_id; #endif unsigned int first_frame_offset, idx; unsigned int total_frame_size; total_len = 0; if(file_write) { // Log PCM samples to a file fd = open(audio_data->outfile, O_RDWR | O_CREAT, S_IRWXU | S_IRWXG | S_IRWXO); if (fd < 0) { perror("Cannot open audio sink device"); return ((void*)-1); } lseek(fd, 44, SEEK_SET); /* Set Space for Wave Header */ } else { // Log PCM samples to pcm out driver fd = open(audio_data->outfile, O_WRONLY); if (fd < 0) { perror("Cannot open audio sink device"); return ((void*)-1); } #ifdef AUDIOV2 if (ioctl(fd, AUDIO_GET_SESSION_ID, &dec_id)) { perror("could not get pcm decoder session id\n"); goto err_state; } printf("pcm decoder session id %d\n", dec_id); #if defined(TARGET_USES_QCOM_MM_AUDIO) if (devmgr_register_session(dec_id, DIR_RX) < 0) { perror("could not route pcm decoder stream\n"); goto err_state; } #endif #endif if (ioctl(fd, AUDIO_GET_CONFIG, &config)) { perror("could not get pcm config"); goto err_state; } config.channel_count = audio_data->channels; config.sample_rate = audio_data->freq; if (ioctl(fd, AUDIO_SET_CONFIG, &config)) { perror("could not set pcm config"); goto err_state; } if (ioctl(fd, AUDIO_START, 0) < 0) { perror("could not start pcm playback node"); goto err_state; } } while(1) { // Send free Read buffer aio_buf.buf_addr = aio_op_buf[out_free_indx].buf_addr; aio_buf.buf_len = aio_op_buf[out_free_indx].buf_len; aio_buf.data_len = 0; // Driver will notify actual size aio_buf.private_data = aio_op_buf[out_free_indx].private_data; wait_for_data(); #ifdef DEBUG_LOCAL printf("%s:free_idx %d, data_idx %d\n", __func__, out_free_indx, out_data_indx); #endif out_free_indx = out_data_indx; printf("%s:ASYNC_READ addr %p len %d\n", __func__, aio_buf.buf_addr, aio_buf.buf_len); if (ioctl(afd, AUDIO_ASYNC_READ, &aio_buf) < 0) { printf("error on async read\n"); break; } meta_out_ptr = (struct dec_meta_out *)aio_op_buf[out_free_indx].buf_addr; meta_out_dsp = (struct meta_out_dsp *)(((char *)meta_out_ptr + sizeof(struct dec_meta_out))); printf("nr of frames %d\n", meta_out_ptr->num_of_frames); #ifdef DEBUG_LOCAL printf("%s:msw ts 0x%8x, lsw_ts 0x%8x, nflags 0x%8x\n", __func__, meta_out_dsp->msw_ts, meta_out_dsp->lsw_ts, meta_out_dsp->nflags); #endif first_frame_offset = meta_out_dsp->offset_to_frame + sizeof(struct dec_meta_out); total_frame_size = 0; if(meta_out_ptr->num_of_frames != 0xFFFFFFFF) { // Go over all meta data field to find exact frame size for(idx=0; idx < meta_out_ptr->num_of_frames; idx++) { total_frame_size += meta_out_dsp->frame_size; meta_out_dsp++; } printf("total size %d\n", total_frame_size); } else { //OutPut EOS reached if (meta_out_dsp->nflags == EOS) { printf("%s:Received EOS at output port 0x%8x\n", __func__, meta_out_dsp->nflags); break; } } printf("%s: Read Size %d offset %d\n", __func__, total_frame_size, first_frame_offset); write(fd, ((char *)aio_op_buf[out_free_indx].buf_addr + first_frame_offset), total_frame_size); total_len += total_frame_size; } if(file_write) { append_header.Sample_rate = audio_data->freq; append_header.Number_Channels = audio_data->channels; append_header.Bytes_Sec = append_header.Sample_rate * append_header.Number_Channels * 2; append_header.Block_align = append_header.Number_Channels * 2; create_wav_header(total_len); lseek(fd, 0, SEEK_SET); write(fd, (char *)&append_header, 44); } else { sleep(1); // All buffers drained #if defined(TARGET_USES_QCOM_MM_AUDIO) && defined(AUDIOV2) if (devmgr_unregister_session(dec_id, DIR_RX) < 0) { perror("could not deroute pcm decoder stream\n"); } #endif } err_state: close(fd); printf("%s:exit\n", __func__); pthread_exit(NULL); return NULL; } static void *qcp_write_thread_8660(void *arg) { struct msm_audio_aio_buf aio_buf; struct audio_pvt_data *audio_data = (struct audio_pvt_data *) arg; int afd = audio_data->afd, sz; struct meta_in_q6 *meta_in_ptr; int eos=0; while(1) { if(!eos) { // Copy write buffer aio_buf.buf_addr = aio_ip_buf[in_free_indx].buf_addr; aio_buf.buf_len = aio_ip_buf[in_free_indx].buf_len; aio_buf.private_data = aio_ip_buf[in_free_indx].private_data; sz = fill_buffer_8660(aio_buf.buf_addr, in_size, audio_data); if (sz == sizeof(struct meta_in_q6)) { //NT mode EOS printf("%s:Done reading file\n", __func__); printf("%s:Send EOS on I/N Put\n", __func__); aio_buf.data_len = sz; aio_ip_buf[in_free_indx].data_len = sz; eos = 1; } else if (sz == 0){ // Tunnel mode EOS eos = 1; break; } else { aio_buf.data_len = sz; aio_ip_buf[in_free_indx].data_len = sz; } printf("%s:ASYNC_WRITE addr %p len %d\n", __func__, aio_buf.buf_addr,aio_buf.data_len); ioctl(afd, AUDIO_ASYNC_WRITE, &aio_buf); } wait_for_data_consumed(); #ifdef DEBUG_LOCAL printf("%s:free_idx %d, data_idx %d\n", __func__, in_free_indx, in_data_indx); #endif in_free_indx = in_data_indx; meta_in_ptr = (struct meta_in_q6 *)aio_ip_buf[in_data_indx].buf_addr; //Input EOS reached if (meta_in_ptr->nflags == EOS) { printf("%s:Received EOS buffer back at i/p 0x%8x\n", __func__, meta_in_ptr->nflags); break; } } if(!audio_data->mode && eos) { printf("%s:Wait for data to drain out\n", __func__); fsync(afd); eos_ack = 1; sleep(1); ioctl(afd, AUDIO_ABORT_GET_EVENT, 0); } printf("%s:exit\n", __func__); // Free memory created during file setup free(audio_data->org_next); pthread_exit(NULL); return NULL; } static void *qcp_dec_event_8660(void *arg) { struct audio_pvt_data *audio_data = (struct audio_pvt_data *) arg; int afd = audio_data->afd, rc; struct msm_audio_event event; int eof = 0; struct dec_meta_out *meta_out_ptr; struct meta_out_dsp *meta_out_dsp; struct meta_in_q6 *meta_in_ptr; pthread_t evt_read_thread; pthread_t evt_write_thread; eos_ack = 0; if (audio_data->mode) // Non Tunnel mode pthread_create(&evt_read_thread, NULL, qcp_read_thread_8660, (void *) audio_data); pthread_create(&evt_write_thread, NULL, qcp_write_thread_8660, (void *) audio_data); // Till EOF not reached in NT or till eos not reached in tunnel while((!eof && audio_data->mode) || (!eos_ack && !audio_data->mode)) { // Wait till timeout event.timeout_ms = 0; rc = ioctl(afd, AUDIO_GET_EVENT, &event); if (rc < 0) { printf("%s: errno #%d", __func__, errno); continue; } #ifdef DEBUG_LOCAL printf("%s:AUDIO_GET_EVENT event %d \n", __func__, event.event_type); #endif switch(event.event_type) { case AUDIO_EVENT_READ_DONE: if(event.event_payload.aio_buf.buf_len == 0) printf("Warning buf_len Zero\n"); if (event.event_payload.aio_buf.data_len >= sizeof(struct dec_meta_out)) { printf("%s: READ_DONE: addr %p len %d\n", __func__, event.event_payload.aio_buf.buf_addr, event.event_payload.aio_buf.data_len); meta_out_ptr = (struct dec_meta_out *)event.event_payload.aio_buf.buf_addr; out_data_indx =(int) event.event_payload.aio_buf.private_data; meta_out_dsp = (struct meta_out_dsp *)(((char *)meta_out_ptr + sizeof(struct dec_meta_out))); //OutPut EOS reached if (meta_out_dsp->nflags == EOS) { eof = 1; printf("%s:Received EOS event at output 0x%8x\n", __func__, meta_out_dsp->nflags); } data_available(); } else { printf("%s:AUDIO_EVENT_READ_DONE:unexpected length\n", __func__); } break; case AUDIO_EVENT_WRITE_DONE: if (event.event_payload.aio_buf.data_len >= sizeof(struct meta_in_q6)) { printf("%s:WRITE_DONE: addr %p len %d\n", __func__, event.event_payload.aio_buf.buf_addr, event.event_payload.aio_buf.data_len); meta_in_ptr = (struct meta_in_q6 *)event.event_payload.aio_buf.buf_addr; in_data_indx =(int) event.event_payload.aio_buf.private_data; //Input EOS reached if (meta_in_ptr->nflags == EOS) { printf("%s:Received EOS at input 0x%8x\n", __func__, meta_in_ptr->nflags); } data_consumed(); } else { printf("%s:AUDIO_EVENT_WRITE_DONE:unexpected length\n", __func__); } break; default: printf("%s: -Unknown event- %d\n", __func__, event.event_type); break; } } if(audio_data->mode) pthread_join(evt_read_thread, NULL); else pthread_join(evt_write_thread, NULL); printf("%s:exit\n", __func__); pthread_exit(NULL); return NULL; } /* Expect on raw file, which is only wma data file */ static void *setup_qcp_file(struct audtest_config *clnt_config) { struct audio_pvt_data *audio_data = (struct audio_pvt_data *) clnt_config->private_data; struct stat stat_buf; char *content_buf; int fd, afd; size_t buffer_size; struct qcp_header *qcpheader = NULL; printf("setup qcp file\n"); fd = open(clnt_config->file_name, O_RDONLY); if (fd < 0) { printf("Err while opening qcp file %s\n", clnt_config->file_name); return((void *)-1); } (void) fstat(fd, &stat_buf); buffer_size = stat_buf.st_size; /* memory set for file */ audio_data->next = (char*)malloc(buffer_size); printf("Total file len: %d, buffer start addr=%p\n", buffer_size, audio_data->next); if (!audio_data->next) { fprintf(stderr,"could not allocate %d bytes\n", buffer_size); close(fd); return ((void*)-1); } audio_data->org_next = audio_data->next; content_buf = audio_data->org_next; if ((read(fd, audio_data->next, buffer_size)) != (ssize_t)buffer_size) { fprintf(stderr,"could not read %d bytes\n", buffer_size); goto fail; } qcpheader = (struct qcp_header *)audio_data->next; if (!strcmp(qcpheader->fmt.info.name, "Qcelp 13K")) { /* Qcelp 13K */ printf("qcelp open\n"); if (audio_data->mode) afd = open(QCELP_DEVICE_NODE, O_RDWR|O_NONBLOCK); else afd = open(QCELP_DEVICE_NODE, O_WRONLY|O_NONBLOCK); } else { printf("evrc open\n"); if (audio_data->mode) afd = open(EVRC_DEVICE_NODE, O_RDWR|O_NONBLOCK); else afd = open(EVRC_DEVICE_NODE, O_WRONLY|O_NONBLOCK); } if (afd < 0) { printf("Unable to open driver\n"); goto fail; } audio_data->afd = afd; /* Skip header part, send bitstrema */ audio_data->next = audio_data->next + (sizeof(struct qcp_header) + sizeof(struct vrat_chunk) + sizeof(struct data_chunk)); audio_data->avail = buffer_size - (sizeof(struct qcp_header) + sizeof(struct vrat_chunk) + sizeof(struct data_chunk)); audio_data->org_avail = audio_data->avail; printf("Total available len: %d, buffer start addr=%p\n", audio_data->avail, audio_data->next); return 0; fail: close(fd); free(content_buf); return ((void*)-1); } static int qcp_start_8660(struct audtest_config *clnt_config) { struct audio_pvt_data *audio_data = (struct audio_pvt_data *) clnt_config->private_data; unsigned n = 0; pthread_t evt_thread; int sz; int rc = -1; #ifdef AUDIOV2 int dec_id; #endif int afd, ipmem_fd[QCPTEST_NUM_IBUF], opmem_fd[QCPTEST_NUM_OBUF]; void *ipmem_ptr[QCPTEST_NUM_IBUF], *opmem_ptr[QCPTEST_NUM_OBUF]; struct msm_audio_pmem_info pmem_info; struct msm_audio_aio_buf aio_buf; struct msm_audio_buf_cfg buf_cfg; struct msm_audio_config config; // Voice decoder operates in 8k, mono audio_data->freq = 8000; audio_data->channels = 1; audio_data->bitspersample = 16; memset(ipmem_fd, 0, (sizeof(int) * QCPTEST_NUM_IBUF)); memset(opmem_fd, 0, (sizeof(int) * QCPTEST_NUM_OBUF)); memset(ipmem_ptr, 0, (sizeof(void *) * QCPTEST_NUM_IBUF)); memset(opmem_ptr, 0, (sizeof(void *) * QCPTEST_NUM_OBUF)); if(((in_size + sizeof(struct meta_in_q6)) > QCPTEST_IBUFSZ) || (out_size > QCPTEST_OBUFSZ)) { perror("configured input / output size more"\ "than pmem allocation"); return -1; } if(setup_qcp_file(clnt_config) < (void *)0) return -1; afd = audio_data->afd; if (audio_data->mode) { /* PCM config */ if (ioctl(afd, AUDIO_GET_CONFIG, &config)) { perror("could not get config"); goto err_state1; } config.sample_rate = audio_data->freq; config.channel_count = audio_data->channels; config.bits = audio_data->bitspersample; if (ioctl(afd, AUDIO_SET_CONFIG, &config)) { perror("could not set config"); goto err_state1; } printf("pcm config sample_rate=%d channels=%d bitspersample=%d \n", config.sample_rate, config.channel_count, config.bits); } else { #ifdef AUDIOV2 if (ioctl(afd, AUDIO_GET_SESSION_ID, &dec_id)) { perror("could not get decoder session id\n"); goto err_state1; } #if defined(TARGET_USES_QCOM_MM_AUDIO) if (devmgr_register_session(dec_id, DIR_RX) < 0) { goto err_state1; } #endif #endif } audio_data->frame_count = 0; if(ioctl(afd, AUDIO_GET_BUF_CFG, &buf_cfg)) { printf("Error getting AUDIO_GET_BUF_CONFIG\n"); goto err_state2; } printf("Default meta_info_enable = 0x%8x\n", buf_cfg.meta_info_enable); printf("Default frames_per_buf = 0x%8x\n", buf_cfg.frames_per_buf); if (audio_data->mode) { // NT mode support meta info buf_cfg.meta_info_enable = 1; if(ioctl(afd, AUDIO_SET_BUF_CFG, &buf_cfg)) { printf("Error setting AUDIO_SET_BUF_CONFIG\n"); goto err_state2; } } pthread_cond_init(&avail_cond, 0); pthread_mutex_init(&avail_lock, 0); pthread_cond_init(&consumed_cond, 0); pthread_mutex_init(&consumed_lock, 0); data_is_available = 0; data_is_consumed = 0; in_free_indx=0; out_free_indx=0; if ((ioctl(afd, AUDIO_START, 0))< 0 ) { printf("qcptest: unable to start driver\n"); goto err_state2; } if (audio_data->mode) { /* non - tunnel portion */ printf("selected non-tunnel part\n"); // Register read buffers for (n = 0; n < QCPTEST_NUM_OBUF; n++) { opmem_fd[n] = open("/dev/pmem_audio", O_RDWR); printf("%s: opmem_fd %x\n", __func__, opmem_fd[n]); opmem_ptr[n] = mmap(0, QCPTEST_OBUFSZ, PROT_READ | PROT_WRITE, MAP_SHARED, opmem_fd[n], 0); printf("%s:opmem_ptr[%d] %x\n", __func__, n, (unsigned int) opmem_ptr[n]); pmem_info.fd = opmem_fd[n]; pmem_info.vaddr = opmem_ptr[n]; rc = ioctl(afd, AUDIO_REGISTER_PMEM, &pmem_info); if(rc < 0) { printf( "error on register opmem=%d\n",rc); goto err_state2; } // Read buffers local structure aio_op_buf[n].buf_addr = opmem_ptr[n]; aio_op_buf[n].buf_len = out_size + sizeof(struct dec_meta_out); aio_op_buf[n].data_len = 0; // Driver will notify actual size aio_op_buf[n].private_data = (void *)n; //Index } // Send n-1 Read buffer for (n = 0; n < (QCPTEST_NUM_OBUF-1); n++) { aio_buf.buf_addr = aio_op_buf[n].buf_addr; aio_buf.buf_len = aio_op_buf[n].buf_len; aio_buf.data_len = aio_op_buf[n].data_len; aio_buf.private_data = aio_op_buf[n].private_data; printf("ASYNC_READ addr %p len %d\n", aio_buf.buf_addr, aio_buf.buf_len); if (ioctl(afd, AUDIO_ASYNC_READ, &aio_buf) < 0) { printf("error on async read\n"); goto err_state2; } } //Indicate available free buffer as (n-1) out_free_indx = QCPTEST_NUM_OBUF-1; } //Register Write buffer for (n = 0; n < QCPTEST_NUM_IBUF; n++) { ipmem_fd[n] = open("/dev/pmem_audio", O_RDWR); printf("%s: ipmem_fd %x\n", __func__, ipmem_fd[n]); ipmem_ptr[n] = mmap(0, QCPTEST_IBUFSZ, PROT_READ | PROT_WRITE, MAP_SHARED, ipmem_fd[n], 0); printf("%s:ipmem_ptr[%d] %x\n", __func__, n, (unsigned int )ipmem_ptr[n]); pmem_info.fd = ipmem_fd[n]; pmem_info.vaddr = ipmem_ptr[n]; rc = ioctl(afd, AUDIO_REGISTER_PMEM, &pmem_info); if(rc < 0) { printf( "error on register ipmem=%d\n",rc); goto err_state2; } // Write buffers local structure aio_ip_buf[n].buf_addr = ipmem_ptr[n]; aio_ip_buf[n].buf_len = QCPTEST_IBUFSZ; aio_ip_buf[n].data_len = 0; // Driver will notify actual size aio_ip_buf[n].private_data = (void *)n; //Index } // Send n-1 write buffer for (n = 0; n < (QCPTEST_NUM_IBUF-1); n++) { aio_buf.buf_addr = aio_ip_buf[n].buf_addr; aio_buf.buf_len = aio_ip_buf[n].buf_len; if ((sz = fill_buffer_8660(aio_buf.buf_addr, in_size, audio_data)) < 0) goto err_state2; aio_buf.data_len = sz; aio_ip_buf[n].data_len = sz; aio_buf.private_data = aio_ip_buf[n].private_data; printf("ASYNC_WRITE addr %p len %d\n", aio_buf.buf_addr, aio_buf.data_len); rc = ioctl(afd, AUDIO_ASYNC_WRITE, &aio_buf); if(rc < 0) { printf( "error on async write=%d\n",rc); goto err_state2; } } //Indicate available free buffer as (n-1) in_free_indx = QCPTEST_NUM_IBUF-1; pthread_create(&evt_thread, NULL, qcp_dec_event_8660, (void *) audio_data); pthread_join(evt_thread, NULL); printf("AUDIO_STOP as event thread completed\n"); done: rc = 0; ioctl(afd, AUDIO_STOP, 0); err_state2: if (audio_data->mode) { for (n = 0; n < QCPTEST_NUM_OBUF; n++) { munmap(opmem_ptr[n], QCPTEST_OBUFSZ); close(opmem_fd[n]); } } for (n = 0; n < QCPTEST_NUM_IBUF; n++) { munmap(ipmem_ptr[n], QCPTEST_IBUFSZ); close(ipmem_fd[n]); } if (!audio_data->mode) { #if defined(TARGET_USES_QCOM_MM_AUDIO) && defined(AUDIOV2) if (devmgr_unregister_session(dec_id, DIR_RX) < 0) printf("error closing stream\n"); #endif } err_state1: close(afd); return rc; } static int qcp_start(struct audtest_config *clnt_config) { int fd, afd, ret; int jj, count, pkts_per_buffer, pkts_size, rate; unsigned ii; int inc = 0; pthread_t thread, event_th; struct type_size ts[8]; struct qcp_header *qcpheader = NULL; struct vrat_chunk vrat; struct data_chunk data; struct msm_audio_config config; struct meta_in meta; int qcelp_evrc_opflg = 0; char *transcodebuf = NULL; unsigned int size = 0; int ret_val = 0; struct audio_pvt_data *audio_data = (struct audio_pvt_data *) clnt_config->private_data; #ifdef AUDIOV2 unsigned short dec_id; #endif /* Open the file for operation */ fd = open(clnt_config->file_name, O_RDWR); if (fd < 0) { printf("unable to open qcp file =%s\n", clnt_config->file_name); return -1; } qcpheader = (struct qcp_header *)malloc(sizeof(struct qcp_header)); #ifdef DEBUG_LOCAL printf("=====================================================\n"); for (ii = 0; ii < 294; ii++) { char ch; read(fd, &ch, 1); printf("%hhx ", ch); } printf("=====================================================\n"); #endif /* QCP File Parsing section ---- START */ lseek(fd, 0, SEEK_SET); printf("\n"); /* Read main QCP Header */ ret = read(fd, qcpheader, sizeof(struct qcp_header)); if (ret < 0) { ret_val = -1; goto file_err; } else audio_data->start_ptr += ret; printf("size of riff chunk = %d and size of a file = %d\n", qcpheader->riff.s_riff, qcpheader->riff.s_riff + 8); printf("size of fmt_chunk : %d\n", qcpheader->fmt.s_fmt); printf("major:minor = %d:%d\n", qcpheader->fmt.mjr, qcpheader->fmt.mnr); printf("Name of the codec = %s\n", qcpheader->fmt.info.name); printf("Average bps of the codec = %hd\n", qcpheader->fmt.info.abps); printf("Bytes per packet = %hd\n", qcpheader->fmt.info.bytes_per_pkt); printf("Samples per block = %hd\n", qcpheader->fmt.info.samp_per_block); printf("Samples per sec = %hd\n", qcpheader->fmt.info.samp_per_sec); printf("Bits per sample = %hd\n", qcpheader->fmt.info.bits_per_samp); printf("No of possible rates = %d\n", qcpheader->fmt.info.vr_num_of_rates); for (ii = 0; ii < qcpheader->fmt.info.vr_num_of_rates; ii++) { char *ptr; ptr = (char *)&(qcpheader->fmt.info.vr_bytes_per_pkt[ii]); printf("size = %hhd rate = %hhd\n", ptr[0], ptr[1]); ts[ii].size = ptr[0]; ts[ii].rate = ptr[1]; } /* Read VRAT Chunk */ if (qcpheader->fmt.info.vr_num_of_rates) { ret = read(fd, &vrat, sizeof(vrat)); if (ret < 0) { ret_val = -1; goto file_err; } else audio_data->start_ptr += ret; printf("%c %c %c %c \n", vrat.vrat[0], vrat.vrat[1], vrat.vrat[2], vrat.vrat[3]); printf("size of the vrat_chunk = %hhd\n", vrat.s_vrat); printf("variable rate = %d\n", vrat.v_rate); printf("data chubk size in packets = %d\n", vrat.size_in_pkts); } /* QCP File parsing section - END */ if (!strcmp(qcpheader->fmt.info.name, "Qcelp 13K")) { /* Qcelp 13K */ if (audio_data->mode) afd = open(QCELP_DEVICE_NODE, O_RDWR); else afd = open(QCELP_DEVICE_NODE, O_WRONLY); qcelp_evrc_opflg = 0; } else { if (audio_data->mode) afd = open(EVRC_DEVICE_NODE, O_RDWR); else afd = open(EVRC_DEVICE_NODE, O_WRONLY); qcelp_evrc_opflg = 1; } if (afd < 0) { printf("Unable to open audio device = %s\n", ((qcelp_evrc_opflg) ? EVRC_DEVICE_NODE : QCELP_DEVICE_NODE)); ret_val = -1; goto file_err; } #ifdef AUDIOV2 if (!audio_data->mode) { if (ioctl(afd, AUDIO_GET_SESSION_ID, &dec_id)) { perror("could not get decoder session id\n"); close(afd); return -1; } #if defined(TARGET_USES_QCOM_MM_AUDIO) if (devmgr_register_session(dec_id, DIR_RX) < 0) { ret = -1; goto exit; } #endif } #endif /* Store handle for commands */ /* clnt_config->private_data = (void *) afd; */ audio_data->afd = afd; pthread_create(&event_th, NULL, event_notify, (void *) audio_data); printf("qcp_play thread start\n"); if (ioctl(afd, AUDIO_GET_CONFIG, &config)) { printf("could not get config\n"); ret_val = -1; goto err_state; } if (audio_data->mode) { config.meta_field = 1; if (ioctl(afd, AUDIO_SET_CONFIG, &config)) { printf("could not get config\n"); ret_val = -1; goto err_state; } } if (!qcelp_evrc_opflg) /* 13k selection */ config.buffer_size -= (config.buffer_size % QCELP_PKT_SIZE); else config.buffer_size -= (config.buffer_size % EVRC_PKT_SIZE); if (audio_data->mode) size = (config.buffer_size + sizeof(struct meta_in)); else size = config.buffer_size; transcodebuf = (char *)malloc(size); if (!transcodebuf) { printf("could not allocate memory for store transcoded data\n"); ret_val = -1; goto err_state; } memset(transcodebuf, 0, size); printf("transcodebuf = %d", (int)transcodebuf); if (audio_data->mode) { /* non - tunnel portion */ struct msm_audio_pcm_config config_rec; printf(" selected non-tunnel part\n"); append_header.Sample_rate = clnt_config->sample_rate; append_header.Number_Channels = clnt_config->channel_mode; append_header.Bytes_Sec = append_header.Sample_rate * append_header.Number_Channels * 2; append_header.Block_align = append_header.Number_Channels * 2; if (ioctl(afd, AUDIO_GET_PCM_CONFIG, &config_rec)) { printf("could not get PCM config\n"); ret_val = -1; goto err_state; } printf(" config_rec.pcm_feedback = %d, \ config_rec.buffer_count = %d , \ config_rec.buffer_size=%d \n", \ config_rec.pcm_feedback, \ config_rec.buffer_count, config_rec.buffer_size); config_rec.pcm_feedback = 1; audio_data->recsize = config_rec.buffer_size; audio_data->recbuf = (char *)malloc(config_rec.buffer_size); if (!audio_data->recbuf) { printf("could not allocate memory for decoding\n"); ret_val = -1; goto err_state; } memset(audio_data->recbuf, 0, config_rec.buffer_size); if (ioctl(afd, AUDIO_SET_PCM_CONFIG, &config_rec)) { printf("could not set PCM config\n"); ret_val = -1; goto err_state; } pthread_create(&thread, NULL, qcp_dec, (void *)audio_data); } if (!qcelp_evrc_opflg) { /* 13k selection */ pkts_size = QCELP_PKT_SIZE; pkts_per_buffer = config.buffer_size / QCELP_PKT_SIZE; } else { /* EVRC selection */ pkts_size = EVRC_PKT_SIZE; pkts_per_buffer = config.buffer_size / EVRC_PKT_SIZE; } /* Read Data Chunk */ ret = read(fd, &data, sizeof(data)); if (ret < 0) { ret_val = -1; goto err_state; } else audio_data->start_ptr += ret; printf("Data chunk size = %d\n", data.s_data); if (qcpheader->fmt.info.vr_num_of_rates) { /* Variable rate */ for (ii = 0, jj = 0, count = 0; ; ii = ii + inc + 1) { if ((ii < data.s_data) && (audio_data->quit != 1)) { int bytetrace; if (audio_data->mode) { bytetrace = (jj * pkts_size) + 1 + sizeof(struct meta_in); if (jj == 0) { meta.offset = sizeof(struct meta_in); meta.timestamp = (audio_data->frame_count * 20000); meta.nflags = 0; #ifdef DEBUG_LOCAL printf("Meta In timestamp: %lld\n", meta.timestamp); #endif memcpy(transcodebuf, &meta, sizeof(struct meta_in)); } } else bytetrace = (jj * pkts_size) + 1; read(fd, &transcodebuf[bytetrace], 1); for (rate = 0; rate < qcpheader->fmt.info.vr_num_of_rates; rate++) { if (transcodebuf[bytetrace] == ts[rate].rate) { inc = ts[rate].size; break; } } if (rate == qcpheader->fmt.info.vr_num_of_rates) { printf("Unknown Rate\n"); break; } if (audio_data->mode) bytetrace = (jj * pkts_size) + 2 + sizeof(struct meta_in); else bytetrace = (jj * pkts_size) + 2; read(fd, &transcodebuf[bytetrace], inc); count++; jj++; if (jj == pkts_per_buffer) { #ifdef DEBUG_LOCAL printf("writing %d no of packets\n", jj); printf("Data1 %d = %p, Data2 %d = %p \n", transcodebuf[0], &transcodebuf[0], transcodebuf[1], &transcodebuf[1]); #endif audio_data->frame_count += jj; jj = 0; if (audio_data->suspend == 1) { printf("enter suspend mode\n"); ioctl(afd, AUDIO_STOP, 0); while (audio_data->suspend == 1) sleep(1); ioctl(afd, AUDIO_START, 0); printf("exit suspend mode\n"); } ret = write(afd, &transcodebuf[0], size); if( (ret < 0) && (audio_data->flush_enable == 1) ) { printf("Flush in progress \n"); usleep(5000); printf("start_ptr = %d\n", audio_data->start_ptr); ii = 0; jj = 0; /* Set to start of data portion */ lseek(fd, audio_data->start_ptr, SEEK_SET); } #ifdef DEBUG_LOCAL printf("ret = %d\n", ret); #endif if (count == pkts_per_buffer * 2) ioctl(afd, AUDIO_START, 0); } } else if ((ii >= data.s_data) && (audio_data->repeat != 0) && (audio_data->quit != 1)) { printf("\nRepeat playback\n"); ii = 0; jj = 0; /* Set to start of data portion */ lseek(fd, audio_data->start_ptr, SEEK_SET); if(audio_data->repeat > 0) audio_data->repeat--; sleep(1); continue; } else if (((ii >= data.s_data) && (audio_data->repeat == 0)) || (audio_data->quit == 1)) break; } } else { /* Fixed rate */ printf("I am reading Fixed rate encoded data\n"); for (ii = 0, jj = 0, count = 0; ; ii = ii + qcpheader->fmt.info.bytes_per_pkt) { if ((ii < data.s_data) && (audio_data->quit != 1)) { int bytetrace; if (audio_data->mode) { bytetrace = (jj * pkts_size) + 1 + sizeof(struct meta_in); if (jj == 0) { meta.offset = sizeof(struct meta_in); meta.timestamp = (audio_data->frame_count * 20000); meta.nflags = 0; #ifdef DEBUG_LOCAL printf("Meta In timestamp: %lld\n", meta.timestamp); #endif memcpy(transcodebuf, &meta, sizeof(struct meta_in)); } } else bytetrace = (jj * pkts_size) + 1; read(fd, &transcodebuf[bytetrace], qcpheader->fmt.info.bytes_per_pkt); count++; jj++; if (jj == pkts_per_buffer) { #ifdef DEBUG_LOCAL printf("writing %d no of packets\n", jj); #endif audio_data->frame_count += jj; jj = 0; if (audio_data->suspend == 1) { printf("enter suspend mode\n"); ioctl(afd, AUDIO_STOP, 0); while (audio_data->suspend == 1) sleep(1); ioctl(afd, AUDIO_START, 0); printf("exit suspend mode\n"); } ret = write(afd, &transcodebuf[0], size); if( (ret < 0) && (audio_data->flush_enable == 1) ) { printf("Flush in progress \n"); usleep(5000); printf("start_ptr = %d\n", audio_data->start_ptr); ii = 0; jj = 0; /* Set to start of data portion */ lseek(fd, audio_data->start_ptr, SEEK_SET); } #ifdef DEBUG_LOCAL printf("ret = %d\n", ret); #endif if (count == pkts_per_buffer * 2) ioctl(afd, AUDIO_START, 0); } } else if ((ii >= data.s_data) && (audio_data->repeat != 0) && (audio_data->quit != 1)) { printf("\nRepeat playback\n"); ii = 0; jj = 0; /* Set to start of data portion */ lseek(fd, audio_data->start_ptr, SEEK_SET); if(audio_data->repeat > 0) audio_data->repeat--; sleep(1); continue; } else if (((ii >= data.s_data) && (audio_data->repeat == 0)) || (audio_data->quit == 1)) break; } } if (jj) { audio_data->frame_count += jj; printf("write last buffer with remaining frames: %d\n", jj); ret = write(afd, &transcodebuf[0], ((jj * pkts_size) + sizeof(struct meta_in))); printf("ret = %d\n", ret); } /* Currently EVRC EOS works only if delay * is introduced inbetween last buffer and * EOS buffer. Below loop does that. */ if (qcelp_evrc_opflg) /* EVRC selection */ usleep(50000); if (audio_data->mode) { printf("EOS: write with nflags 1\n"); memset(transcodebuf, 0, size); meta.offset = sizeof(struct meta_in); meta.timestamp = (audio_data->frame_count * 20000); meta.nflags = 1; #ifdef DEBUG_LOCAL printf("Meta In timestamp: %lld\n", meta.timestamp); #endif memcpy(transcodebuf, &meta, sizeof(struct meta_in)); if (write(afd, &transcodebuf[0], sizeof(struct meta_in)) < 0) printf("Error writing buffer with EOS\n"); } else { printf("FSYNC: Reached end of file, calling fsync\n"); if (fsync(afd) < 0) printf(" fsync failed\n"); } sleep(2); ioctl(afd, AUDIO_STOP, 0); printf("count = %d\n", count); ioctl(afd, AUDIO_ABORT_GET_EVENT, 0); err_state: #if defined(TARGET_USES_QCOM_MM_AUDIO) && defined(AUDIOV2) if (!audio_data->mode) { if (devmgr_unregister_session(dec_id, DIR_RX) < 0) ret = -1; } exit: #endif close(afd); if (transcodebuf) free(transcodebuf); if (audio_data->recbuf) free(audio_data->recbuf); file_err: free(qcpheader); close(fd); return ret_val; } void *qcp_thread(void *arg) { struct audiotest_thread_context *context = (struct audiotest_thread_context *)arg; int ret_val; if (context->config.tgt == 0x07) ret_val = qcp_start(&context->config); else ret_val = qcp_start_8660(&context->config); printf("Free audio instance 0x%8x \n", (unsigned int) context->config.private_data); free(context->config.private_data); free_context(context); pthread_exit((void *)ret_val); return NULL; } int qcpplay_read_params(void) { struct audiotest_thread_context *context; char *token; int ret_val = 0; if ((context = get_free_context()) == NULL) { ret_val = -1; } else { struct audio_pvt_data *audio_data; audio_data = (struct audio_pvt_data *) malloc(sizeof(struct audio_pvt_data)); if(!audio_data) { printf("error allocating audio instance structure \n"); free_context(context); ret_val = -1; } else { printf("Created audio instance 0x%8x \n",(unsigned int) audio_data); memset(audio_data, 0, sizeof(struct audio_pvt_data)); /* Set complete zero */ context->config.file_name = "/data/sample.qcp"; context->type = AUDIOTEST_TEST_MOD_QCP_DEC; context->config.tgt = 0x7; #ifdef _ANDROID_ audio_data->outfile = "/data/pcm.wav"; #else audio_data->outfile = "/tmp/pcm.wav"; #endif audio_data->repeat = 0; audio_data->quit = 0; out_size = 8192 + sizeof(struct dec_meta_out); in_size = 320; token = strtok(NULL, " "); while (token != NULL) { if (!memcmp(token, "-id=", (sizeof("-id=") - 1))) { context->cxt_id = atoi(&token[sizeof("-id=") - 1]); } else if (!memcmp(token, "-mode=", (sizeof("-mode=") - 1))) { audio_data->mode = atoi(&token[sizeof("-mode=") - 1]); } else if (!memcmp(token, "-out=", (sizeof("-out=") - 1))) { audio_data->outfile = token + (sizeof("-out=")-1); } else if (!memcmp(token, "-repeat=", (sizeof("-repeat=") - 1))) { audio_data->repeat = atoi(&token[sizeof("-repeat=") - 1]); if (audio_data->repeat == 0) audio_data->repeat = -1; else audio_data->repeat--; } else if (!memcmp(token,"-tgt=", (sizeof("-tgt=") - 1))) { context->config.tgt = atoi(&token[sizeof("-tgt=") - 1]); } else if (!memcmp(token, "-wr=",(sizeof("-wr=") - 1))) { file_write = atoi(&token[sizeof("-wr=") - 1]); } else if (!memcmp(token, "-outsize=", (sizeof("-outsize=") - 1))) { out_size = atoi(&token[sizeof("-outsize=") - 1]) + sizeof(struct dec_meta_out); } else if (!memcmp(token, "-insize=", (sizeof("-insize=") - 1))) { in_size = atoi(&token[sizeof("-insize=") - 1]); } else { context->config.file_name = token; } token = strtok(NULL, " "); } if (audio_data->mode == 1) { /* non-tunnel */ context->config.sample_rate = 8000; context->config.channel_mode = 1; } context->config.private_data = (struct audio_pvt_data *) audio_data; pthread_create(&context->thread, NULL, qcp_thread, (void *)context); } } return ret_val; } int qcp_play_control_handler(void *private_data) { int drvfd , ret_val = 0; int volume; struct audio_pvt_data *audio_data = (struct audio_pvt_data *) private_data; char *token; token = strtok(NULL, " "); if ((private_data != NULL) && (token != NULL)) { drvfd = audio_data->afd; if (!memcmp(token, "-cmd=", (sizeof("-cmd=") - 1))) { token = &token[sizeof("-cmd=") - 1]; printf("%s: cmd %s\n", __FUNCTION__, token); if (!strcmp(token, "pause")) { ioctl(drvfd, AUDIO_PAUSE, 1); } else if (!strcmp(token, "resume")) { ioctl(drvfd, AUDIO_PAUSE, 0); #if defined(TARGET_USES_QCOM_MM_AUDIO) && defined(AUDIOV2) } else if (!strcmp(token, "volume")) { int rc; unsigned short dec_id; token = strtok(NULL, " "); if (!memcmp(token, "-value=", (sizeof("-value=") - 1))) { volume = atoi(&token[sizeof("-value=") - 1]); if (ioctl(drvfd, AUDIO_GET_SESSION_ID, &dec_id)) { perror("could not get decoder session id\n"); } else { printf("session %d - volume %d \n", dec_id, volume); rc = msm_set_volume(dec_id, volume); printf("session volume result %d\n", rc); } } #else } else if (!strcmp(token, "volume")) { token = strtok(NULL, " "); if (!memcmp(token, "-value=", (sizeof("-value=") - 1))) { volume = atoi(&token[sizeof("-value=") - 1]); ioctl(drvfd, AUDIO_SET_VOLUME, volume); printf("volume:%d\n", volume); } #endif } else if (!strcmp(token, "flush")) { audio_data->flush_enable = 1; ioctl(drvfd, AUDIO_FLUSH, 0); printf("flush\n"); } else if (!strcmp(token, "quit")) { audio_data->quit = 1; printf("quit session\n"); } } } else { ret_val = -1; } return ret_val; } const char *qcpplay_help_txt = "Play QCP file: type \n\ echo \"playqcp path_of_file -id=xxx -mode=x \ -out=path_of_outfile -repeat=x\" > %s \n\ Codec type of QCP file: Qcelp 13K or EVRC \n\ mode= 0(tunnel mode) or 1 (non-tunnel mode) \n\ Repeat 'x' no. of times, repeat infinitely if repeat = 0\n\ Supported control command: pause, resume, volume, flush, quit \n\ examples: \n\ echo \"playqcp path_of_file -id=xxx -mode=<0 or 1>\" > %s \n\ echo \"control_cmd -id=xxx -cmd=pause\" > %s \n\ echo \"control_cmd -id=xxx -cmd=resume\" > %s \n\ echo \"control_cmd -id=xxx -cmd=flush\" > %s \n\ echo \"control_cmd -id=xxx -cmd=volume -value=yyyy\" > %s \n"; void qcpplay_help_menu(void) { printf(qcpplay_help_txt, cmdfile, cmdfile, cmdfile, cmdfile, cmdfile, cmdfile); }