/* pcmtest.c - native PCM 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-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 "audiotest_def.h" #include "equalizer.h" #define DUAL_MIC_SOURCE 4 #define FRAME_NUM 128 const char *dev_file_name; static char *next, *org_next; static unsigned avail, org_avail; static int quit, repeat; static int rec_source; int rec_stop = 1; struct audiot_buf_config { char *buf; uint32_t head; /* next buffer app will write */ uint32_t tail; /* next buffer read() will read */ int fd; int frame_available; int buffer_size; /* pcm min buffer size */ uint32_t frame_count; /* total number of frames read */ pthread_mutex_t cond_lock; pthread_mutex_t cmpl_lock; pthread_mutex_t lock; pthread_cond_t cond; pthread_t thread; }; void wait_for_frame(struct audiot_buf_config *buf_cfg) { pthread_mutex_lock(&buf_cfg->cond_lock); while (!buf_cfg->frame_available && !rec_stop ) { pthread_cond_wait(&buf_cfg->cond, &buf_cfg->cond_lock); } buf_cfg->frame_count--; if(!buf_cfg->frame_count) buf_cfg->frame_available = 0; pthread_mutex_unlock(&buf_cfg->cond_lock); } void frame_available(struct audiot_buf_config *buf_cfg) { pthread_mutex_lock(&buf_cfg->cond_lock); if(buf_cfg->frame_available == 0) { buf_cfg->frame_available = 1; } buf_cfg->frame_count++; pthread_cond_signal(&buf_cfg->cond); pthread_mutex_unlock(&buf_cfg->cond_lock); } static int pcm_play(struct audtest_config *cfg, unsigned rate, unsigned channels, int (*fill)(void *buf, unsigned sz, void *cookie), void *cookie) { struct msm_audio_config config; // struct msm_audio_stats stats; unsigned n; int sz; char *buf; int afd; int cntW=0; int ret = 0; #ifdef AUDIOV2 unsigned short dec_id; #endif afd = open(dev_file_name, O_WRONLY); if (afd < 0) { perror("pcm_play: cannot open audio device"); return -1; } cfg->private_data = (void*) afd; #ifdef AUDIOV2 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 if (ioctl(afd, AUDIO_GET_CONFIG, &config)) { perror("could not get config"); ret = -1; goto err_state; } config.channel_count = channels; config.sample_rate = rate; if (ioctl(afd, AUDIO_SET_CONFIG, &config)) { perror("could not set config"); ret = -1; goto err_state; } buf = (char*) malloc(sizeof(char) * config.buffer_size); if (buf == NULL) { perror("fail to allocate buffer\n"); ret = -1; goto err_state; } printf("initiate_play: buffer_size=%d, buffer_count=%d\n", config.buffer_size, config.buffer_count); fprintf(stderr,"prefill\n"); for (n = 0; n < config.buffer_count; n++) { if ((sz = fill(buf, config.buffer_size, cookie)) < 0) break; if (write(afd, buf, sz) != sz) break; } cntW=cntW+config.buffer_count; fprintf(stderr,"start playback\n"); if (ioctl(afd, AUDIO_START, 0) >= 0) { for (;;) { #if 0 if (ioctl(afd, AUDIO_GET_STATS, &stats) == 0) fprintf(stderr,"%10d\n", stats.out_bytes); #endif if (((sz = fill(buf, config.buffer_size, cookie)) < 0) || (quit == 1)) { if ((repeat == 0) || (quit == 1)) { printf(" fill return NON NULL, exit loop \n"); break; } else { printf("\nRepeat playback\n"); avail = org_avail; next = org_next; cntW = 0; if(repeat > 0) repeat--; sleep(1); continue; } } if (write(afd, buf, sz) != sz) { printf(" write return not equal to sz, exit loop\n"); break; } else { cntW++; printf(" pcm_play: repeat_count=%d cntW=%d\n", repeat, cntW); } } printf("end of pcm play\n"); sleep(5); } else { printf("pcm_play: Unable to start driver\n"); } free(buf); err_state: #if defined(TARGET_USES_QCOM_MM_AUDIO) && defined(AUDIOV2) if (devmgr_unregister_session(dec_id, DIR_RX) < 0) ret = -1; exit: #endif close(afd); return ret; } /* http://ccrma.stanford.edu/courses/422/projects/WaveFormat/ */ #define ID_RIFF 0x46464952 #define ID_WAVE 0x45564157 #define ID_FMT 0x20746d66 #define ID_DATA 0x61746164 #define FORMAT_PCM 1 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; /* sample_rate * num_channels * bps / 8 */ uint16_t block_align; /* num_channels * bps / 8 */ uint16_t bits_per_sample; uint32_t data_id; uint32_t data_sz; }; static int fill_buffer(void *buf, unsigned sz, void *cookie) { unsigned cpy_size = (sz < avail?sz:avail); /* Below statement to remove warning for unused variable cookie, but to keep function prototype intact */ (void) cookie; if (avail == 0) return -1; memcpy(buf, next, cpy_size); next += cpy_size; avail -= cpy_size; return cpy_size; } static int play_file(struct audtest_config *config, unsigned rate, unsigned channels, int fd, size_t count) { int ret = 0; next = (char*)malloc(count); org_next = next; printf(" play_file: count=%d,next=%p\n", count,next); if (!next) { fprintf(stderr,"could not allocate %d bytes\n", count); return -1; } if (read(fd, next, count) != (ssize_t)count) { fprintf(stderr,"could not read %d bytes\n", count); return -1; } avail = count; org_avail = avail; ret = pcm_play(config, rate, channels, fill_buffer, 0); free(org_next); next = NULL; org_next = NULL; return ret; } int wav_play(struct audtest_config *config) { struct wav_header hdr; // unsigned rate, channels; int fd; if (config == NULL) { return -1; } fd = open(config->file_name, O_RDONLY); if (fd < 0) { fprintf(stderr, "playwav: cannot open '%s'\n", config->file_name); return -1; } if (read(fd, &hdr, sizeof(hdr)) != sizeof(hdr)) { fprintf(stderr, "playwav: cannot read header\n"); return -1; } fprintf(stderr,"playwav: %d ch, %d hz, %d bit, %s\n", hdr.num_channels, hdr.sample_rate, hdr.bits_per_sample, hdr.audio_format == FORMAT_PCM ? "PCM" : "unknown"); if ((hdr.riff_id != ID_RIFF) || (hdr.riff_fmt != ID_WAVE) || (hdr.fmt_id != ID_FMT)) { fprintf(stderr, "playwav: '%s' is not a riff/wave file\n", config->file_name); return -1; } if ((hdr.audio_format != FORMAT_PCM) || (hdr.fmt_sz != 16)) { fprintf(stderr, "playwav: '%s' is not pcm format\n", config->file_name); return -1; } if (hdr.bits_per_sample != 16) { fprintf(stderr, "playwav: '%s' is not 16bit per sample\n", config->file_name); return -1; } return play_file(config, hdr.sample_rate, hdr.num_channels, fd, hdr.data_sz); } void* pcmwrite_thread(void *arg) { struct audiot_buf_config *buf_cfg = (struct audiot_buf_config *)arg; while(!rec_stop) { wait_for_frame(buf_cfg); if (write(buf_cfg->fd, &buf_cfg->buf[buf_cfg->tail * buf_cfg->buffer_size], buf_cfg->buffer_size) != (ssize_t)buf_cfg->buffer_size) { perror("cannot write buffer"); pthread_mutex_unlock(&buf_cfg->cmpl_lock); return NULL; } pthread_mutex_lock(&buf_cfg->lock); buf_cfg->tail = (buf_cfg->tail + 1) & (FRAME_NUM - 1); pthread_mutex_unlock(&buf_cfg->lock); } pthread_mutex_unlock(&buf_cfg->cmpl_lock); return NULL; } int wav_rec(struct audtest_config *config) { struct wav_header hdr; char *buf; struct msm_audio_config cfg; struct audiot_buf_config *buf_cfg; unsigned sz; //n; int fd, afd; unsigned total = 0; struct audio_pvt_data *audio_data = (struct audio_pvt_data *) config->private_data; #ifdef AUDIOV2 unsigned short enc_id; int device_id=-1; const char *device = "a2dp_tx"; int a2dp_set = 0; #endif if ((config->channel_mode != 2) && (config->channel_mode != 1)) { perror("invalid channel mode \n"); return -1; } else { switch (config->sample_rate) { case 48000: case 44100: case 32000: case 24000: case 22050: case 16000: case 12000: case 11025: case 8000: break; default: perror("invalid sample rate \n"); return -1; break; } } hdr.riff_id = ID_RIFF; hdr.riff_sz = 0; hdr.riff_fmt = ID_WAVE; hdr.fmt_id = ID_FMT; hdr.fmt_sz = 16; hdr.audio_format = FORMAT_PCM; hdr.num_channels = config->channel_mode; hdr.sample_rate = config->sample_rate; hdr.byte_rate = hdr.sample_rate * hdr.num_channels * 2; hdr.block_align = hdr.num_channels * 2; hdr.bits_per_sample = 16; hdr.data_id = ID_DATA; hdr.data_sz = 0; fd = open(config->file_name, O_CREAT | O_RDWR, 0666); if (fd < 0) { perror("cannot open output file"); return -1; } write(fd, &hdr, sizeof(hdr)); #if defined(AUDIOV2) || defined(AUDIO7X27A) afd = open(dev_file_name, O_RDONLY); if (afd < 0) { perror("cannot open msm_pcm_in"); close(fd); return -1; } #else afd = open("/dev/msm_pcm_in", O_RDONLY); if (afd < 0) { perror("cannot open msm_pcm_in"); close(fd); return -1; } #endif #ifdef AUDIOV2 if (ioctl(afd, AUDIO_GET_SESSION_ID, &enc_id)) { perror("could not get encoder session id\n"); close(fd); close(afd); return -1; } if(!strcmp(dev_file_name, "/dev/msm_a2dp_in")) a2dp_set = 1; if(!a2dp_set) { printf("Routing to non A2DP\n"); if (devmgr_register_session(enc_id, DIR_TX) < 0) { close(fd); close(afd); return -1; } } else { device_id = msm_get_device(device); printf("Routing to A2DP\n"); if (devmgr_enable_device(device_id, DIR_TX) < 0){ perror("could not enable TX device\n"); return -1; } if (msm_route_stream(DIR_TX, enc_id, device_id, 1) < 0) { perror("could not route stream to Device\n"); if (devmgr_disable_device(device_id, DIR_RX) < 0) perror("could not disable device\n"); return -1; } } if (rec_source == DUAL_MIC_SOURCE) { if (msm_set_dual_mic_config(enc_id, 1) < 0) { perror("could not set dual mic config\n"); if (devmgr_disable_device(device_id, DIR_RX) < 0) perror("could not disable device\n"); return -1; } } #endif if (ioctl(afd, AUDIO_GET_CONFIG, &cfg)) { perror("cannot read audio config"); goto fail; } fprintf(stderr,"rec_buf_size = %d\n", audio_data->recsize); cfg.buffer_size = audio_data->recsize; cfg.channel_count = hdr.num_channels; cfg.sample_rate = hdr.sample_rate; sz = cfg.buffer_size; if (sz > (cfg.buffer_size * FRAME_NUM)) { fprintf(stderr, "buffer size %d too large\n", sz); goto fail; } if (sz < 160) { fprintf(stderr, "buffer size %d is small\n", sz); goto fail; } if (ioctl(afd, AUDIO_SET_CONFIG, &cfg)) { perror("cannot write audio config"); goto fail; } buf_cfg = (struct audiot_buf_config *)malloc(sizeof(struct audiot_buf_config)); if(buf_cfg == NULL) { fprintf(stderr,"\n buffer allocation failed \n"); goto fail; } memset(buf_cfg,0,sizeof(struct audiot_buf_config)); buf_cfg->buf =(char *)malloc(cfg.buffer_size * FRAME_NUM); if(buf_cfg->buf == NULL) { fprintf(stderr,"\n buffer allocation failed \n"); goto fail; } rec_stop = 0; buf_cfg->buffer_size = sz; buf_cfg->fd = fd; buf = buf_cfg->buf; pthread_cond_init(&buf_cfg->cond, 0); pthread_mutex_init(&buf_cfg->cond_lock, 0); pthread_mutex_init(&buf_cfg->cmpl_lock, 0); pthread_mutex_init(&buf_cfg->lock, 0); pthread_create( &buf_cfg->thread, NULL, pcmwrite_thread, (void*) buf_cfg); #ifdef AUDIOV2 /* Record form voice link */ if (rec_source <= VOC_REC_BOTH ) { if (ioctl(afd, AUDIO_SET_INCALL, &rec_source)) { perror("Error: AUDIO_SET_INCALL failed"); goto fail; } printf("rec source = 0x%8x\n", rec_source); } #endif if (ioctl(afd, AUDIO_START, 0) < 0) { perror("cannot start audio"); goto fail; } fprintf(stderr,"\n*** RECORDING IN PROGRESS ***\n"); while(!rec_stop) { if (read(afd, &buf[buf_cfg->head * sz], sz) != (ssize_t)sz) { perror("cannot read buffer"); rec_stop = 1; frame_available(buf_cfg); goto done; } pthread_mutex_lock(&buf_cfg->lock); buf_cfg->head = (buf_cfg->head + 1) & (FRAME_NUM - 1); /* If overflow, move the tail index foward. */ if (buf_cfg->head == buf_cfg->tail) { buf_cfg->tail = (buf_cfg->tail + 1) & (FRAME_NUM - 1); fprintf(stderr,"\n***BUFFER OVERFLOW***"); rec_stop = 1; pthread_mutex_unlock(&buf_cfg->lock); frame_available(buf_cfg); goto done; } pthread_mutex_unlock(&buf_cfg->lock); frame_available(buf_cfg); total += sz; } done: close(afd); /* wait for pcm writer thread to exit */ pthread_mutex_lock(&buf_cfg->cmpl_lock); /* update lengths in header */ hdr.data_sz = total; hdr.riff_sz = total + 8 + 16 + 8; lseek(fd, 0, SEEK_SET); write(fd, &hdr, sizeof(hdr)); close(fd); pthread_cond_destroy(&buf_cfg->cond); pthread_mutex_destroy(&buf_cfg->cond_lock); pthread_mutex_destroy(&buf_cfg->lock); pthread_mutex_destroy(&buf_cfg->cmpl_lock); #ifdef AUDIOV2 if(!a2dp_set) { printf("Derouting from non A2DP\n"); if (devmgr_unregister_session(enc_id, DIR_TX) < 0){ perror("could not unregister encode session\n"); } } else { printf("Derouting from A2DP\n"); if (devmgr_disable_device(device_id, DIR_TX) < 0){ perror("could not enable TX device\n"); return -1; } if (msm_route_stream(DIR_TX, enc_id, device_id, 0) < 0) { perror("could not route stream to Device\n"); if (devmgr_disable_device(device_id, DIR_RX) < 0) perror("could not disable device\n"); return -1; } } #endif return 0; fail: close(afd); close(fd); #ifdef AUDIOV2 if(!a2dp_set) { printf("Derouting from non A2DP\n"); if (devmgr_unregister_session(enc_id, DIR_TX) < 0){ perror("could not unregister encode session\n"); } } else { device_id = msm_get_device(device); printf("De Routing to A2DP\n"); if (devmgr_enable_device(device_id, DIR_TX) < 0){ perror("could not enable TX device\n"); return -1; } if (msm_route_stream(DIR_TX, enc_id, device_id, 1) < 0) { perror("could not route stream to Device\n"); if (devmgr_disable_device(device_id, DIR_RX) < 0) perror("could not disable device\n"); return -1; } } #endif unlink(config->file_name); return -1; } void* playpcm_thread(void* arg) { struct audiotest_thread_context *context = (struct audiotest_thread_context*) arg; int ret_val; ret_val = wav_play(&context->config); free_context(context); pthread_exit((void*) ret_val); return NULL; } int pcmplay_read_params(void) { struct audiotest_thread_context *context; char *token; int ret_val = 0; if ((context = get_free_context()) == NULL) { ret_val = -1; } else { context->config.file_name = "/data/data.wav"; dev_file_name = "/dev/msm_pcm_out"; repeat = 0; quit = 0; 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, "-dev=", (sizeof("-dev=") - 1))) { dev_file_name = token + (sizeof("-dev=")-1); } else if (!memcmp(token, "-repeat=", (sizeof("-repeat=") - 1))) { repeat = atoi(&token[sizeof("-repeat=") - 1]); if (repeat == 0) repeat = -1; else repeat--; } else { context->config.file_name = token; } token = strtok(NULL, " "); } context->type = AUDIOTEST_TEST_MOD_PCM_DEC; pthread_create( &context->thread, NULL, playpcm_thread, (void*) context); } return ret_val; } void* recpcm_thread(void* arg) { struct audiotest_thread_context *context = (struct audiotest_thread_context*) arg; int ret_val; ret_val = wav_rec(&context->config); free_context(context); pthread_exit((void*) ret_val); return NULL; } int pcmrec_read_params(void) { struct audiotest_thread_context *context; char *token; int ret_val = 0; if ((context = get_free_context()) == NULL) { ret_val = -1; } else { context->config.sample_rate = 8000; context->config.file_name = "/data/record.wav"; dev_file_name = "/dev/msm_pcm_in"; context->config.channel_mode = 1; context->type = AUDIOTEST_TEST_MOD_PCM_ENC; rec_source = 3; 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 { audio_data->recsize = 2048; token = strtok(NULL, " "); while (token != NULL) { printf("%s \n", token); if (!memcmp(token,"-rate=", (sizeof("-rate=") - 1))) { context->config.sample_rate = atoi(&token[sizeof("-rate=") - 1]); } else if (!memcmp(token,"-cmode=", (sizeof("-cmode=") - 1))) { context->config.channel_mode = atoi(&token[sizeof("-cmode=") - 1]); } else if (!memcmp(token,"-recbufsize=", (sizeof("-recbufsize=") - 1))) { audio_data->recsize = atoi(&token[sizeof("-recbufsize=") - 1]); } else if (!memcmp(token,"-id=", (sizeof("-id=") - 1))) { context->cxt_id = atoi(&token[sizeof("-id=") - 1]); } else if (!memcmp(token, "-dev=", (sizeof("-dev=") - 1))) { dev_file_name = token + (sizeof("-dev=")-1); } else if (!memcmp(token, "-src=", (sizeof("-src=") - 1))) { rec_source = atoi(&token[sizeof("-src=") - 1]); } else { context->config.file_name = token; } token = strtok(NULL, " "); } context->config.private_data = (struct audio_pvt_data *) audio_data; printf("%s:sample_rate=%d channel_mode=%d, recbufsize = %d\n", __FUNCTION__, context->config.sample_rate, context->config.channel_mode, audio_data->recsize); pthread_create( &context->thread, NULL, recpcm_thread, (void*) context); } } return ret_val; } int pcm_play_control_handler(void* private_data) { int drvfd , ret_val = 0; int volume; char *token; #if defined(TARGET_USES_QCOM_MM_AUDIO) && defined(AUDIOV2) int eq_preset; #endif token = strtok(NULL, " "); if ((private_data != NULL) && (token != NULL)) { drvfd = (int) private_data; if(!memcmp(token,"-cmd=", (sizeof("-cmd=") -1))) { token = &token[sizeof("-cmd=") - 1]; printf("%s: cmd %s\n", __FUNCTION__, token); #if defined(TARGET_USES_QCOM_MM_AUDIO) && defined(AUDIOV2) 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); if ((volume >= 0) && (volume <= 100)) { rc = msm_set_volume(dec_id, volume); printf("session volume result %d\n", rc); } else printf("session volume out of range\n"); } } } else if (!strcmp(token, "eq")) { token = strtok(NULL, " "); if (!memcmp(token, "-preset=", (sizeof("-preset=") - 1))) { eq_preset = atoi(&token[sizeof("-preset=") - 1]); if ((eq_preset >= 0) && (eq_preset < MAX_PRESETS)) #ifdef QDSP6V2 set_pcm_default_eq_values(drvfd, eq_preset); #else printf("not supported\n"); #endif else { printf("Wrong preset:%d Check command: \ for supported preset values\n", eq_preset); printf("mm-audio-native-test -format playpcm\n"); } } } else if (!strcmp(token, "stop")) { quit = 1; printf("quit session\n"); } #else if (!strcmp(token, "pause")) { ioctl(drvfd, AUDIO_PAUSE, 1); } else if (!strcmp(token, "resume")) { ioctl(drvfd, AUDIO_PAUSE, 0); } 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); } } else if (!strcmp(token, "stop")) { quit = 1; printf("quit session\n"); } #endif } } else { ret_val = -1; } return ret_val; } int pcm_rec_control_handler(void* private_data) { int /* drvfd ,*/ ret_val = 0; char *token; token = strtok(NULL, " "); if ((private_data != NULL) && (token != NULL)) { /* drvfd = (int) private_data */ if(!memcmp(token,"-cmd=", (sizeof("-cmd=") -1))) { token = &token[sizeof("-cmd=") - 1]; printf("%s: cmd %s\n", __FUNCTION__, token); if (!strcmp(token, "stop")) { rec_stop = 1; } } } else { ret_val = -1; } return ret_val; } const char *pcmplay_help_txt = "Play PCM file: type \n\ echo \"playpcm path_of_file -id=xxx -repeat=x -dev=/dev/msm_pcm_dec or msm_pcm_out\" > tmp/audio_test \n\ Repeat 'x' no. of times, repeat infinitely if repeat = 0\n\ Sample rate of PCM file <= 48000 \n\ Bits per sample = 16 bits \n\ Supported control command: pause, flush, volume, stop, equalizer\n\ examples: \n\ echo \"control_cmd -id=xxx -cmd=eq -preset=yyyy\" > %s \n\ 0 - BLANK \n\ 1 - CLUB \n\ 2 - DANCE \n\ 3 - FULLBASS \n\ 4 - FULLBASSTREBLE \n\ 5 - FULLTREBLE \n\ 6 - LAPTOP \n\ 7 - LARGEHALL \n\ 8 - LIVE \n\ 9 - PARTY \n\ 10 - POP \n\ 11 - REGGAE \n\ 12 - ROCK \n\ 13 - SKA \n\ 14 - SOFT \n\ 15 - SOFTROCK \n\ 16 - TECHNO \n"; void pcmplay_help_menu(void) { printf("%s\n", pcmplay_help_txt); } const char *pcmrec_help_txt = "Record pcm file: type \n\ echo \"recpcm path_of_file -rate=xxx -cmode=x -recbufsize=x -id=xxx -src=x -dev=/dev/msm_pcm_in or msm_a2dp_in\" > tmp/audio_test \n\ sample rate: 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000 \n\ src: 0 - Uplink 1 - Downlink, 2 - UL/DL, 3 - Mic 4 - Dual MIC\n\ channel mode: 1 or 2 \n\ recbufsize(recording buffer size): value greater than 160 and a multiple of 4 for 8k, default is 2048 \n\ (512,1024 or 2048) * channel mode for 7x30, default is 2048 \n\ Supported control command: stop\n "; void pcmrec_help_menu(void) { printf("%s\n", pcmrec_help_txt); }