M7350/qcom-opensource/mm-audio/audio-native/qdsp6/qcptest.c

394 lines
10 KiB
C
Raw Normal View History

2024-09-09 08:52:07 +00:00
/* qcptest.c - native QCP test application
*
* Based on native pcm test application platform/system/extras/sound/playwav.c
*
* Copyright (C) 2008 The Android Open Source Project
* Copyright (c) 2010, 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 <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <stdint.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <string.h>
#include <linux/ioctl.h>
#include <linux/msm_audio_qcp.h>
#include "audiotest_def.h"
struct qcp_header {
/* RIFF Section */
char riff[4];
unsigned int s_riff;
char qlcm[4];
/* Format chunk */
char fmt[4];
unsigned int s_fmt;
char mjr;
char mnr;
unsigned int data1; /* UNIQUE ID of the codec */
unsigned short data2;
unsigned short data3;
char data4[8];
unsigned short ver; /* Codec Info */
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;
unsigned char vr_num_of_rates; /* Rate Header fmt info */
unsigned char rvd1[3];
unsigned short vr_bytes_per_pkt[8];
unsigned int rvd2[5];
/* Vrat chunk */
unsigned char vrat[4];
unsigned int s_vrat;
unsigned int v_rate;
unsigned int size_in_pkts;
/* Data chunk */
unsigned char data[4];
unsigned int s_data;
} __attribute__ ((packed));
/* Common part */
static struct qcp_header append_header = {
{'R', 'I', 'F', 'F'}, 0, {'Q', 'L', 'C', 'M'}, /* Riff */
{'f', 'm', 't', ' '}, 150, 1, 0, 0, 0, 0,{0}, 0, {0},0,0,160,8000,16,0,{0},{0},{0}, /* Fmt */
{'v','r','a','t'}, 0, 0, 0, /* Vrat */
{'d','a','t','a'},0 /* Data */
};
#define QCP_HEADER_SIZE sizeof(struct qcp_header)
static int rec_stop;
static void create_qcp_header(int Datasize, int Frames, int Format)
{
append_header.s_riff = Datasize + QCP_HEADER_SIZE - 8;
if(!Format) {
append_header.data1 = 0xe689d48d;
append_header.data2 = 0x9076;
append_header.data3 = 0x46b5;
append_header.data4[0] = 0x91;
append_header.data4[1] = 0xef;
append_header.data4[2] = 0x73;
append_header.data4[3] = 0x6a;
append_header.data4[4] = 0x51;
append_header.data4[5] = 0x00;
append_header.data4[6] = 0xce;
append_header.data4[7] = 0xb4;
append_header.ver = 0x0001;
memcpy(append_header.name, "TIA IS-127 Enhanced Variable Rate Codec, Speech Service Option 3", 64);
append_header.abps = 9600;
append_header.bytes_per_pkt = 23;
append_header.vr_num_of_rates = 4;
append_header.vr_bytes_per_pkt[0] = 0x0416;
append_header.vr_bytes_per_pkt[1] = 0x030a;
append_header.vr_bytes_per_pkt[2] = 0x0200;
append_header.vr_bytes_per_pkt[3] = 0x0102;
append_header.s_vrat = 0x00000008;
append_header.v_rate = 0x00000001;
append_header.size_in_pkts = Frames;
append_header.s_data = Datasize;
}
else {
printf("QCELP 13K header\n");
append_header.data1 = 0x5E7F6D41;
append_header.data2 = 0xB115;
append_header.data3 = 0x11D0;
append_header.data4[0] = 0xBA;
append_header.data4[1] = 0x91;
append_header.data4[2] = 0x00;
append_header.data4[3] = 0x80;
append_header.data4[4] = 0x5F;
append_header.data4[5] = 0xB4;
append_header.data4[6] = 0xB9;
append_header.data4[7] = 0x7E;
append_header.ver = 0x0002;
memcpy(append_header.name, "Qcelp 13K", 9);
append_header.abps = 13000;
append_header.bytes_per_pkt = 35;
append_header.vr_num_of_rates = 5;
append_header.vr_bytes_per_pkt[0] = 0x0422;
append_header.vr_bytes_per_pkt[1] = 0x0310;
append_header.vr_bytes_per_pkt[2] = 0x0207;
append_header.vr_bytes_per_pkt[3] = 0x0103;
append_header.s_vrat = 0x00000008;
append_header.v_rate = 0x00000001;
append_header.size_in_pkts = Frames;
}
append_header.s_data = Datasize;
return;
}
int qcp_rec(struct audtest_config *config)
{
struct qcp_header hdr;
unsigned char buf[1024];
struct msm_audio_evrc_enc_config evrc_cfg;
struct msm_audio_qcelp_enc_config qcelp_cfg;
struct msm_audio_stream_config str_cfg;
struct msm_voicerec_mode voicerec_mode;
struct audio_pvt_data *audio_data = (struct audio_pvt_data *) config->private_data;
unsigned sz;
int fd, afd;
int total = 0;
int read_size = 0;
//voicerec_mode.rec_mode = config->rec_mode;
fd = open(config->file_name, O_CREAT | O_RDWR, 0666);
if (fd < 0) {
perror("cannot open output file");
return -1;
}
if(!audio_data->format) {
afd = open("/dev/msm_evrc_in", O_RDWR);
if (afd < 0) {
perror("cannot open msm_evrc_in");
close(fd);
return -1;
}
if (ioctl(afd, AUDIO_GET_STREAM_CONFIG, &str_cfg)) {
perror("cannot read audio stream config");
goto fail;
}
sz = 23;
fprintf(stderr,"buffer size %d\n", sz);
if (sz > sizeof(buf)) {
fprintf(stderr,"buffer size %d too large\n", sz);
goto fail;
}
str_cfg.buffer_size = sz;
str_cfg.buffer_count = 2;
if (ioctl(afd, AUDIO_SET_STREAM_CONFIG, &str_cfg)) {
perror("cannot write audio stream config");
goto fail;
}
if (ioctl(afd, AUDIO_GET_EVRC_ENC_CONFIG, &evrc_cfg)) {
perror("cannot read audio config");
goto fail;
}
evrc_cfg.cdma_rate = CDMA_RATE_FULL;
evrc_cfg.min_bit_rate = 4;
evrc_cfg.max_bit_rate = 4;
if (ioctl(afd, AUDIO_SET_EVRC_ENC_CONFIG, &evrc_cfg)) {
perror("cannot write audio config");
goto fail;
}
}
else {
afd = open("/dev/msm_qcelp_in", O_RDWR);
if (afd < 0) {
perror("cannot open msm_qcp_in");
close(fd);
return -1;
}
if (ioctl(afd, AUDIO_GET_STREAM_CONFIG, &str_cfg)) {
perror("cannot read audio stream config");
goto fail;
}
sz = 35;
fprintf(stderr,"buffer size %d\n", sz);
if (sz > sizeof(buf)) {
fprintf(stderr,"buffer size %d too large\n", sz);
goto fail;
}
str_cfg.buffer_size = sz;
str_cfg.buffer_count = 2;
if (ioctl(afd, AUDIO_SET_STREAM_CONFIG, &str_cfg)) {
perror("cannot write audio stream config");
goto fail;
}
if (ioctl(afd, AUDIO_GET_QCELP_ENC_CONFIG, &qcelp_cfg)) {
perror("cannot read audio config");
goto fail;
}
qcelp_cfg.cdma_rate = CDMA_RATE_FULL;
qcelp_cfg.min_bit_rate = 4;
qcelp_cfg.max_bit_rate = 4;
if (ioctl(afd, AUDIO_SET_QCELP_ENC_CONFIG, &qcelp_cfg)) {
perror("cannot write audio config");
goto fail;
}
}
voicerec_mode.rec_mode=2;
if (ioctl(afd, AUDIO_SET_INCALL, &voicerec_mode)) {
perror("cannot set incall mode");
goto fail;
}
if (ioctl(afd, AUDIO_START, 0)) {
perror("cannot start audio");
goto fail;
}
fcntl(0, F_SETFL, O_NONBLOCK);
fprintf(stderr,"\n*** RECORDING * USE 'STOP' CONTROL COMMAND TO STOP***\n");
lseek(fd, QCP_HEADER_SIZE, SEEK_SET);
rec_stop = 0;
while (rec_stop != 1) {
read_size = read(afd, buf, sz);
if (read_size <= 0) {
printf("cannot read buffer error code =0x%8x", read_size);
goto fail;
}
if (write(fd, buf, read_size) != read_size) {
perror("cannot write buffer");
goto fail;
}
total += read_size;
}
close(afd);
create_qcp_header(total, 1, audio_data->format);
lseek(fd, 0, SEEK_SET);
write(fd, (char *)&append_header, QCP_HEADER_SIZE);
close(fd);
return 0;
fail:
close(afd);
close(fd);
unlink(config->file_name);
return -1;
}
static void audiotest_alarm_handler(int sig)
{
g_terminate_early = 1;
sleep(1);
}
void* recqcp_thread(void* arg) {
struct audiotest_thread_context *context =
(struct audiotest_thread_context*) arg;
int ret_val;
ret_val = qcp_rec(&context->config);
free_context(context);
pthread_exit((void*) ret_val);
return NULL;
}
int qcprec_read_params(void) {
struct audiotest_thread_context *context;
struct itimerspec ts;
char *token;
int ret_val = 0;
if ((context = get_free_context()) == NULL) {
ret_val = -1;
} else {
context->config.file_name = "/data/rec.qcp";
context->config.rec_mode = 2;
context->type = AUDIOTEST_TEST_MOD_QCP_ENC;
g_terminate_early = 0;
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 {
token = strtok(NULL, " ");
while (token != NULL) {
printf("%s \n", token);
if (!memcmp(token,"-rec_mode=", (sizeof("-rec_mode=") - 1))) {
context->config.rec_mode =
atoi(&token[sizeof("-rec_mode=") - 1]);
} else if (!memcmp(token,"-id=", (sizeof("-id=") - 1))) {
context->cxt_id = atoi(&token[sizeof("-id=") - 1]);
} else if (!memcmp(token,"-format=", (sizeof("-format=") - 1))) {
audio_data->format = atoi(&token[sizeof("-format=") - 1]);
} else if (!memcmp(token,"-timeout=", (sizeof("-timeout=") - 1))) {
audio_data->g_rec_timeout = atoi(&token[sizeof("-timeout=") - 1]);
memset(&ts, 0, sizeof(struct itimerspec));
printf("setting rec timeout to %d secs\n", audio_data->g_rec_timeout);
ts.it_value.tv_sec = audio_data->g_rec_timeout;
signal(SIGALRM, audiotest_alarm_handler);
setitimer(ITIMER_REAL, &ts, NULL);
} else {
context->config.file_name = token;
}
token = strtok(NULL, " ");
}
printf("%s : rec_mode=%d\n", __FUNCTION__, context->config.rec_mode);
context->config.private_data = (struct audio_pvt_data *) audio_data;
pthread_create(&context->thread, NULL, recqcp_thread, (void *) context);
}
}
return ret_val;
}
int qcp_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"))
printf("Rec stop command\n");
rec_stop = 1;
}
} else {
ret_val = -1;
}
return ret_val;
}
const char *qcprec_help_txt =
"Record qcp file: type \n\
echo \"recqcp path_of_file -format=x -rec_mode=x -id=xxx -timeout=x\" > /data/audio_test \n\
timeout = x (value in seconds) \n\
format = 0 (evrc) or 1 (qcelp) \n\
record mode: 0=txonly 1=rxonly 2=mixed)\n ";
void qcprec_help_menu(void)
{
printf("%s\n", qcprec_help_txt);
}