386 lines
10 KiB
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;
|
|
}
|