/* wmaprotest.c - native WMAPRO10 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 #include "audiotest_def.h" #define WMAPRO_DEVICE_NODE "/dev/msm_wmapro" #define EOS 1 /* 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, 2, 48000, 8001, 2, 16, {'d', 'a', 't', 'a'}, 0 }; typedef struct TIMESTAMP{ unsigned long LowPart; unsigned long HighPart; } __attribute__ ((packed)) TIMESTAMP; struct meta_in{ unsigned short offset; TIMESTAMP ntimestamp; unsigned int nflags; } __attribute__ ((packed)); struct meta_out{ unsigned short offset; TIMESTAMP ntimestamp; unsigned int nflags; unsigned short errflag; unsigned short sample_frequency; unsigned short channel; unsigned int tick_count; } __attribute__ ((packed)); #ifdef _ANDROID_ static const char *cmdfile = "/data/audio_test"; #else static const char *cmdfile = "/tmp/audio_test"; #endif static void create_wav_header(int Datasize) { append_header.Chunk_size = Datasize + 8 + 16 + 12; append_header.Chunk_data_size = Datasize; return; } static void *wmapro_dec(void *arg) { int fd, ret_val = 0; struct meta_out meta; struct audio_pvt_data *audio_data = (struct audio_pvt_data *) arg; int afd = audio_data->afd; unsigned long long *time; int len, total_len = 0; 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 Bytes of data \n", len); #endif if (len < 0) { if (audio_data->flush_enable == 1 && errno == EBUSY) { printf("Flush in progress\n"); usleep(5000); continue; } else { printf(" error reading the PCM samples \n"); goto fail; } } else if (len != 0) { memcpy(&meta, audio_data->recbuf, sizeof(struct meta_out)); time = (unsigned long long *)(audio_data->recbuf + 2); meta.ntimestamp.LowPart = (*time & 0xFFFFFFFF); meta.ntimestamp.HighPart = ((*time >> 32) & 0xFFFFFFFF); #ifdef DEBUG_LOCAL printf("Meta_out High part is %lu\n", meta.ntimestamp.HighPart); printf("Meta_out Low part is %lu\n", meta.ntimestamp.LowPart); printf("Meta Out Timestamp: %llu\n", (((unsigned long long)meta.ntimestamp.HighPart << 32) + meta.ntimestamp.LowPart)); #endif if (meta.nflags == EOS) { 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: printf("error:Reached fail\n"); 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; } static int wmapro_start(struct audtest_config *clnt_config) { int fd, afd, ii, count, ret, pkts_size, size; int retval = 0; pthread_t thread, event_th; static struct msm_audio_config config; static struct msm_audio_wmapro_config wmapro_config; int wmapro_opflg = 0; char *transcodebuf = NULL; 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_RDONLY); if (fd < 0) { printf("unable to open wmapro file =%s\n", clnt_config->file_name); return -1; } if (audio_data->mode) { printf("non-tunnel mode\n"); afd = open(WMAPRO_DEVICE_NODE, O_RDWR); } else { printf("tunnel mode\n"); afd = open(WMAPRO_DEVICE_NODE, O_WRONLY); } wmapro_opflg = 0; if (afd < 0) { printf("Unable to open audio device = %s\n", WMAPRO_DEVICE_NODE); goto file_err; } /* Store handle for commands */ audio_data->afd = afd; #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 pthread_create(&event_th, NULL, event_notify, (void *) audio_data); if (ioctl(afd, AUDIO_GET_CONFIG, &config)) { perror("could not get config"); retval = -1; goto err_state; } if (audio_data->mode) config.meta_field = 1; config.sample_rate = audio_data->freq; config.channel_count = audio_data->channels; if (ioctl(afd, AUDIO_SET_CONFIG, &config)) { perror("could not set config"); retval = -1; goto err_state; } wmapro_config.armdatareqthr = audio_data->datareqthr; wmapro_config.numchannels = audio_data->channels; wmapro_config.validbitspersample = audio_data->bitspersample; wmapro_config.formattag = audio_data->formattag; wmapro_config.samplingrate = audio_data->freq; wmapro_config.avgbytespersecond = audio_data->bps; wmapro_config.asfpacketlength = audio_data->asfpacketlength; wmapro_config.channelmask = audio_data->channelmask; wmapro_config.encodeopt = audio_data->encopt; wmapro_config.advancedencodeopt = audio_data->advancedencodeopt; wmapro_config.advancedencodeopt2 = audio_data->advancedencodeopt2; printf("wmapro_config.armdatareqthr=%d\n", wmapro_config.armdatareqthr); printf("wmapro_config.numchannels=%d\n", wmapro_config.numchannels); printf("wmapro_config.samplingrate=%d\n", wmapro_config.samplingrate); printf("wmapro_config.avgbytespersec=%d\n", wmapro_config.avgbytespersecond); printf("wmapro_config.validbitspersample=%d\n", wmapro_config.validbitspersample); printf("wmapro_config.formattag=%d\n", wmapro_config.formattag); printf("wmapro_config.channelmask=%d\n", wmapro_config.channelmask); printf("wmapro_config.asfpacketlength=%d\n", wmapro_config.asfpacketlength); printf("wmapro_config.encodeopts=%d\n", wmapro_config.encodeopt); printf("wmapro_config.advancedencodeopt=%d\n", wmapro_config.advancedencodeopt); printf("wmapro_config.advancedencodeopt2=%d\n", wmapro_config.advancedencodeopt2); if (ioctl(afd, AUDIO_SET_WMAPRO_CONFIG, &wmapro_config)) { perror("could not set WMAPRO config"); retval = -1; goto err_state; } transcodebuf = (char *)malloc(config.buffer_size); if (!transcodebuf) { printf("could not allocate memory for store transcoded data\n"); retval = -1; goto err_state; } memset(transcodebuf, 0, config.buffer_size); printf("transcodebuf = %d\n", (int) transcodebuf); if (!audio_data->mode) config.buffer_size = (config.buffer_size - sizeof(struct meta_in)); if (audio_data->mode) { /* non - tunnel portion */ struct msm_audio_pcm_config config_rec; printf(" selected non-tunnel part\n"); append_header.Sample_rate = wmapro_config.samplingrate; append_header.Number_Channels = wmapro_config.numchannels; 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"); retval = -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"); retval = -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"); retval = -1; goto err_state; } pthread_create(&thread, NULL, wmapro_dec, (void *)audio_data); } audio_data->start_ptr = 0x0; size = lseek(fd, 0, SEEK_END); lseek(fd, audio_data->start_ptr, SEEK_SET); if (audio_data->mode) pkts_size = (config.buffer_size - sizeof(struct meta_in)); else pkts_size = config.buffer_size; for (ii = 0, count = 0; ; ii = ii + pkts_size) { if ((ii < size) && (audio_data->quit != 1)) { if (audio_data->mode) { struct meta_in meta; meta.ntimestamp.LowPart = ((audio_data->frame_count * 20000) & 0xFFFFFFFF); meta.ntimestamp.HighPart = (((unsigned long long)(audio_data->frame_count * 20000) >> 32) & 0xFFFFFFFF); meta.offset = sizeof(struct meta_in); meta.nflags = 0; 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)); #endif memcpy(transcodebuf, &meta, sizeof(struct meta_in)); read(fd, (transcodebuf + sizeof(struct meta_in)), pkts_size); } else read(fd, transcodebuf, pkts_size); count++; printf("writing %d no of packets\n", count); 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, config.buffer_size); printf("ret = %d\n", ret); if ((ret < 0) && (audio_data->flush_enable == 1)) { printf("Flush in progress \n"); usleep(5000); ii = 0; /* Set to start of data portion */ lseek(fd, audio_data->start_ptr, SEEK_SET); } if (count == 2) ioctl(afd, AUDIO_START, 0); } else if ((ii >= size) && (audio_data->repeat != 0) && (audio_data->quit != 1)) { printf("\nRepeat playback\n"); ii = 0; count = 2; /* 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 >= size) && (audio_data->repeat == 0)) || (audio_data->quit == 1)) break; } printf(" File reached end or quit cmd issued, exit loop \n"); if (audio_data->mode) { struct meta_in meta; meta.offset = sizeof(struct meta_in); meta.ntimestamp.LowPart = ((audio_data->frame_count * 20000) & 0xFFFFFFFF); meta.ntimestamp.HighPart = (((unsigned long long)(audio_data->frame_count * 20000) >> 32) & 0xFFFFFFFF); meta.nflags = EOS; #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)); #endif memset(transcodebuf, 0, sizeof(config.buffer_size)); memcpy(transcodebuf, &meta, sizeof(struct meta_in)); if (write(afd, transcodebuf, sizeof(struct meta_in)) < 0) printf(" writing buffer\ for EOS failed\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 if (transcodebuf) free(transcodebuf); if (audio_data->recbuf) free(audio_data->recbuf); close(afd); return retval; file_err: close(fd); return -1; } void *wmapro_thread(void *arg) { struct audiotest_thread_context *context = (struct audiotest_thread_context *)arg; int ret_val; ret_val = wmapro_start(&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 wmaproplay_read_params(void) { struct audiotest_thread_context *context; char *token = NULL; int ret_val = 0; context = get_free_context(); if (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); /* Set complete zero */ memset(audio_data, 0, sizeof(struct audio_pvt_data)); context->config.file_name = "/data/sample.strm"; context->type = AUDIOTEST_TEST_MOD_WMAPRO_DEC; #ifdef _ANDROID_ audio_data->outfile = "/data/pcm.wav"; #else audio_data->outfile = "/tmp/pcm.wav"; #endif audio_data->repeat = 0; audio_data->quit = 0; audio_data->mode = 0; token = strtok(NULL, " "); while (token != NULL) { if (!memcmp(token, "-id=", (sizeof("-id=") - 1))) { context->cxt_id = atoi(&token[sizeof("-id=") - 1]); printf("Context id =%d\n", context->cxt_id); } 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, "-datareqthr=", (sizeof("-datareqthr=") - 1))) { audio_data->datareqthr = atoi(&token[sizeof("-datareqthr=") - 1]); } else if (!memcmp(token, "-channels=", (sizeof("-channels=") - 1))) { audio_data->channels = atoi(&token[sizeof("-channels=") - 1]); } else if (!memcmp(token, "-bps=", (sizeof("-bps=") - 1))) { audio_data->bps = atoi(&token[sizeof("-bps=") - 1]); } else if (!memcmp(token, "-freq=", (sizeof("-freq=") - 1))) { audio_data->freq = atoi(&token[sizeof("-freq=") - 1]); } else if (!memcmp(token, "-encopt=", (sizeof("-encopt=") - 1))) { audio_data->encopt = atoi(&token[sizeof("-encopt=") - 1]); } else if (!memcmp(token, "-formattag=", (sizeof("-formattag=") - 1))) { audio_data->formattag = atoi(&token[sizeof("-formattag=") - 1]); } else if (!memcmp(token, "-asfpacketlength=", (sizeof("-asfpacketlength=") - 1))) { audio_data->asfpacketlength = atoi(&token[sizeof("-asfpacketlength=") - 1]); } else if (!memcmp(token, "-advancedencodeopt=", (sizeof("-advancedencodeopt=") - 1))) { audio_data->advancedencodeopt = atoi(&token[sizeof("-advancedencodeopt=") - 1]); } else if (!memcmp(token, "-advancedencodeopt2=", (sizeof("-advancedencodeopt2=") - 1))) { audio_data->advancedencodeopt2 = atoi(&token[sizeof("-advancedencodeopt2=") - 1]); } else if (!memcmp(token, "-channelmask=", (sizeof("-channelmask=") - 1))) { audio_data->channelmask = atoi(&token[sizeof("-channelmask=") - 1]); } else if (!memcmp(token, "-bitspersample=", (sizeof("-bitspersample=") - 1))) { audio_data->bitspersample = atoi(&token[sizeof("-bitspersample=") - 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 { 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; } printf("reading parameters\n\n"); context->config.private_data = (struct audio_pvt_data *) audio_data; pthread_create(&context->thread, NULL, wmapro_thread, (void *)context); } } return ret_val; } int wmapro_play_control_handler(void *private_data) { int drvfd, ret_val = 0; int volume; char *token; struct audio_pvt_data *audio_data = (struct audio_pvt_data *) private_data; printf("reached here\n"); 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")) { printf("reached here\n"); 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 *wmaproplay_help_txt = "Play WMAPRO stream file: \n\ echo \"playwmapro path_of_file -id=xxx -mode=x -datareqthr=x -channels=x\ -bps=x -freq=x -encopt=x -bitspersample=x -formattag=x -asfpacketlength=x\ -channelmask=x -advancedencodeopt=x -advancedencodeopt2=x\ -out=path_of_outfile -repeat=x\" > %s \n Codec type of WMAPRO file\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 wmaproplay_help_menu(void) { printf(wmaproplay_help_txt, cmdfile, cmdfile, cmdfile, cmdfile, cmdfile, cmdfile); }