freeswitch/libs/libcodec2/unittest/dvdongle2.c

386 lines
10 KiB
C

/*---------------------------------------------------------------------------*\
FILE........: dvdongle2.c
AUTHOR......: David Rowe
DATE CREATED: 28 Oct 2010
Program to encode and decode raw speech samples using the AMBE codec
implemented on a DV Dongle.
The DV Dongle connects to a USB port and provides encoding and
decoding of compressed audio using the DVSI AMBE2000 full duplex
vocoder DSP chip.
Refs:
[1] http://www.dvdongle.com/
[2] http://www.moetronix.com/files/dvdongletechref100.pdf
[3] http://www.dvsinc.com/manuals/AMBE-2000_manual.pdf
[4] http://www.moetronix.com/files/ambetest103.zip
Serial code based on ser.c sample from http://www.captain.at
Compile with:
gcc dvdongle2.c -o dvdongle2 -Wall -g -O2
Note: This program is not very stable, it sometimes stops part way
through processing an utterance. I made it just good enough to work
most of the time, as my purpose was just to process a few sample
files.
\*---------------------------------------------------------------------------*/
/*
Copyright (C) 1990-2010 David Rowe
All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License version 2.1, as
published by the Free Software Foundation. This program is
distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <termios.h>
#define MAX_STR 1024
#define LEN_TARGET_NAME_RESPONSE 14
#define N 160
/* message parsing state machine states */
#define MSGSTATE_HDR1 0
#define MSGSTATE_HDR2 1
#define MSGSTATE_DATA 2
#define LENGTH_MASK 0x1FFF /* mask for message length */
#define TYPE_MASK 0xE0 /* mask for upper byte of header */
#define TYPE_C 0x20 /* compressed speech from target */
#define TYPE_UC 0x40 /* uncompressed speech from target */
#define MAX_MSG_LEN 8192
/* Control items sent to DV Dongle */
char target_name[] = {0x04, 0x20, 0x01, 0x00};
/* note [2] appears to be in error, specifies run as 0x02, stop as 0x01 */
char run_state_stop[] = {0x05, 0x00, 0x18, 0x00, 0x00};
char run_state_run[] = {0x05, 0x00, 0x18, 0x00, 0x01};
/* Control item codes from DV Dongle */
char data_item_0[] = {0x42, 0x81};
char data_item_1[] = {0x32, 0xa0};
char run_state[] = {0x05, 0x00};
char idle[] = {0x00, 0x00};
typedef struct {
short header;
char power;
char control1;
short rate[5];
short unused[3];
short dtmf;
short control2;
short channel_data[12];
} COMPRESSED;
COMPRESSED c_in;
COMPRESSED c_out;
FILE *fin, *fout, *f;
int fd, c_msg, uc_msg;
int initport(int fd) {
struct termios options;
// Set the options for the port...
cfmakeraw(&options);
cfsetispeed(&options, B230400);
cfsetospeed(&options, B230400);
options.c_cflag |= (CLOCAL | CREAD);
tcsetattr(fd, TCSANOW, &options);
return 1;
}
int getbaud(int fd) {
struct termios termAttr;
int inputSpeed = -1;
speed_t baudRate;
tcgetattr(fd, &termAttr);
/* Get the input speed */
baudRate = cfgetispeed(&termAttr);
switch (baudRate) {
case B0: inputSpeed = 0; break;
case B50: inputSpeed = 50; break;
case B110: inputSpeed = 110; break;
case B134: inputSpeed = 134; break;
case B150: inputSpeed = 150; break;
case B200: inputSpeed = 200; break;
case B300: inputSpeed = 300; break;
case B600: inputSpeed = 600; break;
case B1200: inputSpeed = 1200; break;
case B1800: inputSpeed = 1800; break;
case B2400: inputSpeed = 2400; break;
case B4800: inputSpeed = 4800; break;
case B9600: inputSpeed = 9600; break;
case B19200: inputSpeed = 19200; break;
case B38400: inputSpeed = 38400; break;
case B57600: inputSpeed = 38400; break;
case B115200: inputSpeed = 38400; break;
case B230400: inputSpeed = 230400; break;
}
return inputSpeed;
}
void write_dongle(int fd, char *data, int len) {
int n;
//printf(" writing %d bytes\n", len);
n = write(fd, data, len);
if (n < 0) {
perror("write failed");
exit(1);
}
}
void read_dongle(int fd, char *data, int len) {
int n;
//printf(" reading %d bytes \n", len);
n = read(fd, data, len);
if (n < 0) {
perror("read failed");
exit(1);
}
//printf(" read %d bytes\n", len);
}
void parse_message(int msg_type, int msg_len, char msg_data[]) {
short buf[N];
COMPRESSED *c_out;
//printf("msg_type: 0x%02x msg_len: %d\n", msg_type, msg_len);
/* echo compressed speech frames back to target */
if (msg_type == TYPE_C) {
c_out = (COMPRESSED*)msg_data;
#ifdef TMP
printf("control1 0x%04x\n", c_out->control1 & 0xff);
printf("rate[0] 0x%04x\n", c_out->rate[0]);
printf("rate[1] 0x%04x\n", c_out->rate[1]);
printf("rate[2] 0x%04x\n", c_out->rate[2]);
printf("rate[3] 0x%04x\n", c_out->rate[3]);
printf("rate[4] 0x%04x\n", c_out->rate[4]);
printf("control2 0x%04x\n", c_out->control2 & 0xffff);
printf("cd[0] 0x%04x\n", c_out->channel_data[0] & 0xffff);
printf("cd[1] 0x%04x\n", c_out->channel_data[1] & 0xffff);
printf("cd[2] 0x%04x\n", c_out->channel_data[2] & 0xffff);
printf("cd[3] 0x%04x\n", c_out->channel_data[3] & 0xffff);
printf("cd[4] 0x%04x\n", c_out->channel_data[4] & 0xffff);
printf("cd[5] 0x%04x\n", c_out->channel_data[5] & 0xffff);
printf("cd[6] 0x%04x\n", c_out->channel_data[6] & 0xffff);
printf("uc_msg %d\n", uc_msg);
#endif
printf("bit errors %d\n", c_out->unused[2]);
memcpy(&c_in.channel_data,
&c_out->channel_data,
sizeof(c_in.channel_data));
write_dongle(fd, data_item_1, sizeof(data_item_1));
write_dongle(fd, (char*)&c_in, sizeof(c_in));
c_msg++;
}
/* write speech buffers to disk */
if (msg_type == TYPE_UC) {
if (fout != NULL) {
fwrite(msg_data, sizeof(char), msg_len-2, fout);
printf("msg_len %d\n", msg_len);
}
if (fin != NULL)
fread(buf, sizeof(short), N, fin);
else
memset(buf, 0, sizeof(buf));
write_dongle(fd, data_item_0, sizeof(data_item_0));
write_dongle(fd, (char*)buf, sizeof(buf));
uc_msg++;
}
}
int main(int argc, char **argv) {
char response[MAX_STR];
int i;
int state, next_state;
short header;
int msg_type, msg_length;
char msg_data[MAX_MSG_LEN];
int n, length;
int r;
char data;
f = fopen("/tmp/log.txt", "wt");
assert(f != NULL);
/* open and configure serial port */
fd = open("/dev/ttyUSB0", O_RDWR | O_NOCTTY | O_NDELAY);
if (fd == -1) {
perror("open_port: Unable to open /dev/ttyS0 - ");
exit(1);
} else {
fcntl(fd, F_SETFL, 0);
}
initport(fd);
fin = NULL;
if (argc >= 2) {
fin = fopen(argv[1],"rb");
assert(fin != NULL);
}
fout = NULL;
if (argc == 3) {
fout = fopen(argv[2],"wb");
assert(fout != NULL);
}
/* check DV Dongle is alive */
write_dongle(fd, target_name, sizeof(target_name));
read_dongle(fd, response, LEN_TARGET_NAME_RESPONSE);
if (strcmp(&response[4],"DV Dongle") != 0) {
printf("DV Dongle not responding\n");
exit(1);
}
printf("Found DV Dongle....\n");
c_in.header = 0x13ec;
c_in.power = 0x0;
c_in.control1 = 0x0;
#define RATE2000
#ifdef RATE2000
c_in.rate[0] = 0x0028; /* 2000 bit/s, no FEC */
c_in.rate[1] = 0x0000;
c_in.rate[2] = 0x0000;
c_in.rate[3] = 0x0000;
c_in.rate[4] = 0x6248;
#endif
#ifdef RATE3600_1200
c_in.rate[0] = 0x5048; /* 3600 bit/s, 1200 bit/s FEC */
c_in.rate[1] = 0x0001;
c_in.rate[2] = 0x0000;
c_in.rate[3] = 0x2412;
c_in.rate[4] = 0x6860;
#endif
c_in.unused[0] = 0x0;
c_in.unused[1] = 0x0;
c_in.unused[2] = 0x0;
c_in.dtmf = 0x00ff;
c_in.control2 = 0x8000;
/* put codec in run mode */
write_dongle(fd, run_state_run, sizeof(run_state_run));
//write_dongle(fd, data_item_1, sizeof(data_item_1));
//write_dongle(fd, (char*)&c_in, sizeof(c_in));
state = MSGSTATE_HDR1;
header = msg_type = msg_length = n = length = 0;
c_msg = uc_msg = 0;
for(i=0; i<100000; i++) {
/*
We can only reliably read one byte at a time. Until I
realised this there was "much wailing and gnashing of
teeth". Trying to read() n bytes read() returns n but may
actually reads some number between 1 and n. So it may only
read 1 byte int data[] but return n.
*/
r = read(fd, &data, 1);
assert(r == 1);
/* used state machine design from ambetest103.zip, SerialPort.cpp */
next_state = state;
switch(state) {
case MSGSTATE_HDR1:
header = data;
next_state = MSGSTATE_HDR2;
break;
case MSGSTATE_HDR2:
header |= data<<8;
msg_length = header & LENGTH_MASK;
msg_type = header & TYPE_MASK;
//printf("%0x %d\n", msg_type, msg_length);
if (length == 2) {
parse_message(msg_type, msg_length, msg_data);
next_state = MSGSTATE_HDR1;
}
else {
if (msg_length == 0x0)
length = 8192;
else
length = msg_length - 2;
n = 0;
next_state = MSGSTATE_DATA;
}
break;
case MSGSTATE_DATA:
msg_data[n++] = data;
length--;
if (length == 0) {
parse_message(msg_type, msg_length, msg_data);
next_state = MSGSTATE_HDR1;
}
break;
}
state = next_state;
}
printf("finished, c_msg = %d uc_msg = %d\n", c_msg, uc_msg);
write_dongle(fd, run_state_stop, sizeof(run_state_stop));
close(fd);
if (fin != NULL)
fclose(fin);
if (fout != NULL)
fclose(fout);
fclose(f);
return 0;
}