Browse Source

Move APRS-IS code from dml_trx to dml_fprs_db

Add messaging to FPRS
Add speach synthesis.
Add synthesis of FPRS messages to dml_trx
Fix FPRS destination routing to prevent loops.
master
Jeroen Vreeken 3 years ago
parent
commit
b7894cf4f3
13 changed files with 523 additions and 75 deletions
  1. +41
    -2
      configure.ac
  2. +40
    -0
      dml_fprs_db.c
  3. +4
    -0
      dml_fprs_db.conf
  4. +182
    -59
      dml_trx.c
  5. +0
    -3
      dml_trx.conf
  6. +40
    -6
      fprs_aprsis.c
  7. +1
    -1
      fprs_aprsis.h
  8. +3
    -2
      fprs_db.c
  9. +45
    -2
      fprs_parse.c
  10. +5
    -0
      fprs_parse.h
  11. +16
    -0
      htdocs/fprs.js
  12. +144
    -0
      soundlib.c
  13. +2
    -0
      soundlib.h

+ 41
- 2
configure.ac View File

@ -8,7 +8,7 @@ AC_PROG_CC
AC_CONFIG_MACRO_DIR([m4])
# Check codec2 library
# Check for codec2 library
dnl Search for libcodec2
AC_SEARCH_LIBS([freedv_set_data_header], [codec2], [], [
@ -22,7 +22,7 @@ AS_IF([test "x$codec2_found_headers" != "xyes"],
[AC_MSG_ERROR([Unable to find the codec2 headers])])
# Check ethar library
# Check for eth_ar library
dnl Search for libeth_ar
AC_SEARCH_LIBS([eth_ar_call2mac], [eth_ar], [], [
@ -36,4 +36,43 @@ AS_IF([test "x$eth_ar_found_headers" != "xyes"],
[AC_MSG_ERROR([Unable to find the eth_ar headers])])
# Check for alsa library (flite depends on it bus does not link it itself...)
dnl Search for alsa lib
AC_SEARCH_LIBS([snd_pcm_open], [asound], [], [])
# Check for flite library
dnl Search for flite
AC_SEARCH_LIBS([flite_text_to_wave], [flite],
[libflite_found=1], [libflite_found=0])
AC_SEARCH_LIBS([cmu_lex_init], [flite_cmulex],
[libflite_cmulex_found=1], [libflite_cmulex_found=0])
AC_SEARCH_LIBS([usenglish_init], [flite_usenglish],
[libflite_usenglish_found=1], [libflite_usenglish_found=0])
AC_SEARCH_LIBS([register_cmu_us_slt], [flite_cmu_us_slt],
[libflite_cmu_us_slt_found=1], [libflite_cmu_us_slt_found=0])
AC_CHECK_HEADERS([flite/flite.h],
[flite_headers_found=yes])
dnl Search for libsamplerate
AC_SEARCH_LIBS([src_simple], [samplerate], [libsamplerate_found=1], [libsamplerate_found=0])
AC_CHECK_HEADERS([samplerate.h], [samplerate_headers_found=yes])
AS_IF([ test "x$flite_headers_found" == "xyes" && \
test "$libflite_found" == "1" && \
test "$libflite_cmulex_found" == "1" && \
test "$libflite_usenglish_found" == "1" && \
test "$libflite_cmu_us_slt_found" == "1" && \
test "$libsamplerate_found" == "1" && \
test "x$samplerate_headers_found" == "xyes" ],
[CFLAGS+=" -DHAVE_FLITE"])
AC_OUTPUT([Makefile])

+ 40
- 0
dml_fprs_db.c View File

@ -26,6 +26,7 @@
#include "dml_stream.h"
#include "fprs_db.h"
#include "fprs_parse.h"
#include "fprs_aprsis.h"
#include <eth_ar/eth_ar.h>
@ -52,6 +53,12 @@ struct dml_connection *dml_con;
struct dml_crypto_key *dk;
static bool aprsis = false;
static char *aprsis_host;
static int aprsis_port;
static char *aprsis_call;
void recv_data(void *data, size_t size, uint64_t timestamp, struct dml_stream *from);
static uint16_t alloc_data_id(void)
@ -459,6 +466,13 @@ printf("send to uplink\n");
packet_id = dml_stream_data_id_get(stream_fprs);
if (packet_id)
dml_packet_send_data(dml_con, packet_id, data, size, timestamp, dk);
struct fprs_frame *fprs_frame = fprs_frame_create();
if (fprs_frame) {
fprs_frame_data_set(fprs_frame, data, size);
fprs_aprsis_frame(fprs_frame, NULL);
fprs_frame_destroy(fprs_frame);
}
}
if (link & FPRS_PARSE_DOWNLINK) {
printf("send to downlink\n");
@ -485,6 +499,22 @@ void recv_data(void *data, size_t size, uint64_t timestamp, struct dml_stream *f
);
}
void message_cb(struct fprs_frame *frame)
{
uint8_t data[fprs_frame_data_size(frame)];
size_t size;
struct timespec ts;
clock_gettime(CLOCK_REALTIME, &ts);
fprs_frame_data_get(frame, data, &size);
fprs_parse_data(data, size, &ts,
FPRS_PARSE_UPLINK,
TIME_VALID_UPLINK,
send_data,
NULL
);
}
static int fprs_timer(void *arg)
{
@ -562,6 +592,16 @@ int main(int argc, char **argv)
ca = dml_config_value("ca", NULL, ".");
aprsis_port = atoi(dml_config_value("aprsis_port", NULL, "14580"));
aprsis_host = dml_config_value("aprsis_host", NULL, NULL);
aprsis_call = dml_config_value("aprsis_call", NULL, NULL);
if (aprsis_host && aprsis_call) {
aprsis = true;
fprs_aprsis_init(aprsis_host, aprsis_port, aprsis_call,
true, message_cb);
}
if (dml_crypto_init(NULL, ca)) {
fprintf(stderr, "Failed to init crypto\n");
return -1;


+ 4
- 0
dml_fprs_db.conf View File

@ -11,3 +11,7 @@ key = k.pem
# Who do we trust?
ca = ./ca/
# Use APRS-IS
#aprsis_port = 14580
#aprsis_host = euro.aprs2.net
#aprsis_call = PIRATE-FP

+ 182
- 59
dml_trx.c View File

@ -28,7 +28,6 @@
#include "dml_stream.h"
#include "fprs_db.h"
#include "fprs_parse.h"
#include "fprs_aprsis.h"
#include "trx_dv.h"
#include "soundlib.h"
@ -83,14 +82,10 @@ static uint8_t mac_dev[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
static double my_fprs_longitude = 0.0;
static double my_fprs_latitude = 0.0;
static char *my_fprs_text = "";
static bool aprsis = false;
static char *aprsis_host;
static int aprsis_port;
static char my_call[ETH_AR_CALL_SIZE];
enum sound_msg {
SOUND_MSG_IDLE,
SOUND_MSG_SILENCE,
SOUND_MSG_CONNECT,
SOUND_MSG_DISCONNECT,
@ -99,13 +94,111 @@ enum sound_msg {
SOUND_MSG_NOTALLOWED,
};
static enum sound_msg do_msg = SOUND_MSG_IDLE;
struct sound_msg_e {
struct sound_msg_e *next;
uint8_t *data;
size_t size;
bool free_data;
};
static struct sound_msg_e *sound_msg_q = NULL;
static char *command_prefix = "";
static void queue_sound_msg(enum sound_msg msg)
{
printf("queue msg: %d\n", msg);
do_msg = msg;
struct sound_msg_e *ent = calloc(sizeof(struct sound_msg_e), 1);
struct sound_msg_e **q = &sound_msg_q;
/* Add some silence before a spoken message */
if (msg != SOUND_MSG_SILENCE) {
queue_sound_msg(SOUND_MSG_SILENCE);
queue_sound_msg(SOUND_MSG_SILENCE);
queue_sound_msg(SOUND_MSG_SILENCE);
queue_sound_msg(SOUND_MSG_SILENCE);
}
if (!ent)
goto err_ent;
uint8_t *data;
size_t size;
data = soundlib_get(msg, &size);
if (!data)
goto err_data;
ent->free_data = false;
ent->size = size;
ent->data = data;
while (*q)
q = &(*q)->next;
*q = ent;
return;
err_data:
free(ent);
err_ent:
return;
}
static void queue_sound_spell(char *text)
{
size_t size;
uint8_t *data = soundlib_spell(text, &size);
struct sound_msg_e **q = &sound_msg_q;
if (!data)
return;
struct sound_msg_e *ent = calloc(sizeof(struct sound_msg_e), 1);
if (!ent)
goto err_ent;
ent->data = data;
ent->size = size;
ent->free_data = true;
printf("Queue: %p %zd\n", data, size);
while (*q)
q = &(*q)->next;
*q = ent;
return;
err_ent:
free(data);
return;
}
static void queue_sound_synthesize(char *text)
{
size_t size;
uint8_t *data = soundlib_synthesize(text, &size);
struct sound_msg_e **q = &sound_msg_q;
if (!data)
return;
struct sound_msg_e *ent = calloc(sizeof(struct sound_msg_e), 1);
if (!ent)
goto err_ent;
ent->data = data;
ent->size = size;
ent->free_data = true;
printf("Queue: %p %zd\n", data, size);
while (*q)
q = &(*q)->next;
*q = ent;
return;
err_ent:
free(data);
return;
}
static uint16_t alloc_data_id(void)
@ -174,10 +267,6 @@ static int fprs_update_status(char *stream, char *assoc)
/* Send FPRS frame with callsign in FreeDV header */
trx_dv_send_fprs(mac_dev, mac_bcast, dml_data, dml_size);
if (aprsis) {
fprs_aprsis_frame(fprs_frame, mac_dev);
}
/* Add callsign to packet for others */
fprs_frame_add_callsign(fprs_frame, mac_dev);
@ -206,6 +295,10 @@ static int fprs_update_mac(uint8_t mac[6])
/* Only if it is from someone else */
if (!memcmp(mac, mac_dev, 6))
return 0;
/* Only if we know who send something */
if (!memcmp(mac, mac_bcast, 6))
return 0;
fprs_frame = fprs_frame_create();
if (!fprs_frame)
@ -260,10 +353,6 @@ static int fprs_timer(void *arg)
fprs_frame_data_get(fprs_frame, dml_data, &dml_size);
trx_dv_send_fprs(mac_dev, mac_bcast, dml_data, dml_size);
if (aprsis) {
fprs_aprsis_frame(fprs_frame, mac_dev);
}
fprs_frame_add_callsign(fprs_frame, mac_dev);
dml_size = sizeof(dml_data);
@ -778,24 +867,6 @@ static void recv_data(void *data, size_t size)
}
}
static void send_msg(int nr)
{
uint8_t *data;
size_t size;
printf("Send message %d\n", nr);
data = soundlib_get(nr, &size);
while (size) {
size_t sendsize = 160;
if (size < sendsize)
sendsize = size;
trx_dv_send(mac_dev, mac_bcast, 'A', data, sendsize);
data += sendsize;
size -= sendsize;
}
}
static int rx_watchdog(void *arg)
{
printf("No activity, sending state off packet\n");
@ -810,14 +881,29 @@ static int rx_watchdog(void *arg)
rx_state = false;
if (do_msg) {
send_msg(SOUND_MSG_SILENCE);
send_msg(SOUND_MSG_SILENCE);
send_msg(SOUND_MSG_SILENCE);
send_msg(SOUND_MSG_SILENCE);
send_msg(SOUND_MSG_SILENCE);
send_msg(do_msg);
do_msg = SOUND_MSG_IDLE;
while (sound_msg_q) {
uint8_t *data;
size_t size;
struct sound_msg_e *e = sound_msg_q;
data = e->data;
size = e->size;
while (size) {
size_t sendsize = 160;
if (size < sendsize)
sendsize = size;
trx_dv_send(mac_dev, mac_bcast, 'A', data, sendsize);
data += sendsize;
size -= sendsize;
}
if (e->free_data)
free(e->data);
sound_msg_q = e->next;
free(e);
}
dml_poll_timeout(&rx_state,
@ -927,6 +1013,7 @@ static void command_cb_handle(char *command)
dml_stream_id_get(stream_dv),
DML_PACKET_REQ_REVERSE_CONNECT);
queue_sound_msg(SOUND_MSG_CONNECT);
queue_sound_spell(command);
char *constr;
if (asprintf(&constr, "Connecting %s", command) >= 0) {
@ -934,9 +1021,10 @@ static void command_cb_handle(char *command)
free(constr);
}
} else {
if (notfound)
if (notfound) {
queue_sound_msg(SOUND_MSG_NOTFOUND);
else if (nokey)
queue_sound_spell(command);
} else if (nokey)
queue_sound_msg(SOUND_MSG_NOTALLOWED);
else
queue_sound_msg(SOUND_MSG_DISCONNECT);
@ -1001,9 +1089,6 @@ static int fprs_cb(void *arg, uint8_t from[6], uint8_t *fprsdata, size_t size)
trx_dv_send_fprs(mac_dev, mac_bcast, f_data, f_size);
}
if (aprsis) {
fprs_aprsis_frame(fprs_frame, from);
}
fprs_parse_data(f_data, f_size, &ts,
FPRS_PARSE_DOWNLINK,
TIME_VALID_DOWNLINK,
@ -1029,10 +1114,51 @@ void mac_dev_cb(uint8_t mac[6])
printf("Interface address %02x:%02x:%02x:%02x:%02x:%02x %s-%d\n",
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5],
multicast ? "MULTICAST" : my_call, ssid);
}
static char prev_msg[256] = {0};
static char prev_id[256] = {0};
static uint8_t prev_from[6] = {0};
if (aprsis_host) {
fprs_aprsis_init(aprsis_host, aprsis_port, my_call);
int message_cb(uint8_t to[6], uint8_t from[6],
void *data, size_t dsize, void *id, size_t isize, void *arg)
{
int ssid;
bool multicast;
char from_call[ETH_AR_CALL_SIZE];
char msg_asc[dsize + 1];
char id_asc[isize + 1];
if (memcmp(to, mac_dev, 6))
return -1;
memcpy(msg_asc, data, dsize);
msg_asc[dsize] = 0;
if (id) {
memcpy(id_asc, id, isize);
id_asc[isize] = 0;
} else
id_asc[0] = 0;
if (!memcmp(prev_from, from, 6)) {
if (!strcmp(prev_msg, msg_asc))
return 0;
if (id_asc[0] && !strcmp(id_asc, prev_id))
return 0;
}
strncpy(prev_msg, msg_asc, 255);
strncpy(prev_id, id_asc, 255);
memcpy(prev_from, from, 6);
eth_ar_mac2call(from_call, &ssid, &multicast, from);
printf("Message from %s: %s, ID: %s\n", from_call, msg_asc, id_asc);
queue_sound_synthesize("Message from:");
queue_sound_spell(from_call);
queue_sound_spell(msg_asc);
return 0;
}
int main(int argc, char **argv)
@ -1074,9 +1200,6 @@ int main(int argc, char **argv)
my_fprs_latitude = atof(dml_config_value("latitude", NULL, "0.0"));
my_fprs_text = dml_config_value("fprs_text", NULL, "");
aprsis_port = atoi(dml_config_value("aprsis_port", NULL, "14580"));
aprsis_host = dml_config_value("aprsis_host", NULL, NULL);
command_prefix = dml_config_value("command_prefix", NULL, "");
dv_dev = dml_config_value("dv_device", NULL, NULL);
@ -1109,12 +1232,6 @@ int main(int argc, char **argv)
return -1;
}
if (aprsis_host) {
aprsis = true;
}
if (dml_id_gen(id, DML_PACKET_DESCRIPTION_VERSION_0, bps,
DML_MIME_DV_C2, name, alias, description))
return -1;
@ -1156,7 +1273,13 @@ int main(int argc, char **argv)
dml_poll_add(&fprs_timer, NULL, NULL, fprs_timer);
dml_poll_add(&cur_db, NULL, NULL, fprs_db_check);
soundlib_init(8000);
fprs_parse_hook_message(message_cb, NULL);
if (soundlib_init(8000)) {
printf("Could not init soundlib\n");
return -1;
}
soundlib_add_silence(SOUND_MSG_SILENCE, 0.16);
soundlib_add_beep(SOUND_MSG_CONNECT, 800, 0.08);


+ 0
- 3
dml_trx.conf View File

@ -22,9 +22,6 @@ ca = ./ca/
## Optional prefix used for searching aliasses
#command_prefix = 204
#aprsis_host = euro.aprs2.net
#aprsis_port = 14580
# Use alaw audio files instead of beeps
soundlib_connect = audio/connect.alaw
soundlib_disconnect = audio/disconnect.alaw


+ 40
- 6
fprs_aprsis.c View File

@ -51,6 +51,9 @@ static int is_port;
static int aprs_is_cb(void *arg);
static bool filter_type_message = false;
static void (*message_cb)(struct fprs_frame *) = NULL;
static int tcp_connect(char *host, int port)
{
struct addrinfo *result;
@ -158,6 +161,12 @@ static int aprsis_open(void)
fprs2aprs_login(loginline, &loginline_len, call);
if (write(fd_is, loginline, strlen(loginline)) <= 0)
goto err_write;
if (filter_type_message) {
char *filter = "#filter t/m\r\n";
if (write(fd_is, filter, strlen(filter)) <= 0)
goto err_write;
}
if (dml_poll_add(fprs_aprsis_init, aprs_is_cb, NULL, NULL))
goto err_poll;
@ -186,13 +195,36 @@ static void aprs_is_error(void)
static int aprs_is_cb(void *arg)
{
char buffer[256];
static char buffer[1000];
static size_t pos;
ssize_t r;
r = read(fd_is, buffer, 256);
r = read(fd_is, buffer + pos, 1000 - pos);
printf("------------------------------------- %zd ----- %zd\n", pos, r);
if (r > 0) {
if (write(2, buffer, r) != r)
return -1;
int i;
char *line = buffer;
for (i = 0; i < r + pos; i++) {
if (buffer[i] == '\r' ||
buffer[i] == '\n') {
buffer[i] = 0;
if (strlen(line)) {
printf("%s\n", line);
struct fprs_frame *frame = aprs2fprs(buffer);
if (frame) {
if (message_cb) {
message_cb(frame);
}
fprs_frame_destroy(frame);
}
}
line = buffer + i + 1;
}
}
if (line != buffer + i) {
pos = i - (line - buffer);
memmove(buffer, line, i - pos);
}
} else {
if (r == 0) {
aprs_is_error();
@ -215,7 +247,7 @@ int fprs_aprsis_frame(struct fprs_frame *frame, uint8_t *from)
size_t aprs_size = 255;
if (fprs2aprs(aprs, &aprs_size, frame, from, call)) {
printf("Could not convert to APRIS frame\n");
printf("Could not convert to APRS-IS frame\n");
return -1;
}
@ -228,7 +260,7 @@ int fprs_aprsis_frame(struct fprs_frame *frame, uint8_t *from)
return 0;
}
int fprs_aprsis_init(char *host, int port, char *mycall)
int fprs_aprsis_init(char *host, int port, char *mycall, bool req_msg, void (*msg_cb)(struct fprs_frame *))
{
if (fd_is >= 0) {
close(fd_is);
@ -242,6 +274,8 @@ int fprs_aprsis_init(char *host, int port, char *mycall)
is_host = strdup(host);
is_port = port;
call = strdup(mycall);
filter_type_message = req_msg;
message_cb = msg_cb;
int i;


+ 1
- 1
fprs_aprsis.h View File

@ -20,7 +20,7 @@
#define _INCLUDE_FPRS_APRSIS_H_
int fprs_aprsis_init(char *host, int port, char *call);
int fprs_aprsis_init(char *host, int port, char *mycall, bool req_msg, void (*msg_cb)(struct fprs_frame *));
int fprs_aprsis_frame(struct fprs_frame *frame, uint8_t *from);


+ 3
- 2
fprs_db.c View File

@ -47,7 +47,7 @@ static struct fprs_db_entry *db;
static struct fprs_db_entry *fprs_db_find(struct fprs_db_id *id)
{
struct fprs_db_entry *entry;
for (entry = db; entry; entry = entry->next) {
if (id->type != entry->id.type)
continue;
@ -165,6 +165,7 @@ int fprs_db_element_set(struct fprs_db_id *id,
(*dentry)->t = t;
(*dentry)->t_valid = t + t_valid;
(*dentry)->type = type;
(*dentry)->link = link;
}
return 0;
@ -240,5 +241,5 @@ unsigned int fprs_db_link_get(struct fprs_db_id *id)
for (dentry = entry->elements; dentry; dentry = dentry->next)
link |= dentry->link;
return link;
return link;
}

+ 45
- 2
fprs_parse.c View File

@ -41,6 +41,10 @@ struct fprs_request {
static struct fprs_request *requests = NULL;
static int (*cb_message)(uint8_t to[6], uint8_t from[6], void *data, size_t dsize, void *id, size_t isize, void *arg);
static void *arg_message;
static int fprs_request_add(struct fprs_db_id *id, enum fprs_type type, time_t t_req, unsigned int link)
{
struct fprs_request *req, **entryp;
@ -256,11 +260,41 @@ int fprs_parse_data(void *data, size_t size, struct timespec *recv_time, unsigne
dest_id.type = FPRS_DB_ID_CALLSIGN;
memcpy(dest_id.id.callsign, fprs_element_data(fprs_destination), 6);
if (cb_message) {
struct fprs_element *fprs_message =
fprs_frame_element_by_type(fprs_frame, FPRS_MESSAGE);
struct fprs_element *fprs_messageid =
fprs_frame_element_by_type(fprs_frame, FPRS_MESSAGEID);
if (fprs_message && fprs_callsign) {
void *mdata = fprs_element_data(fprs_message);
size_t msize = fprs_element_size(fprs_message);
void *idata = NULL;
size_t isize = 0;
if (fprs_messageid) {
idata = fprs_element_data(fprs_messageid);
isize = fprs_element_size(fprs_messageid);
}
/* Skip if claimed */
if (!cb_message(
dest_id.id.callsign,
fprs_element_data(fprs_callsign),
mdata, msize, idata, isize, arg_message))
goto skip;
}
}
unsigned int dest_link = fprs_db_link_get(&dest_id);
if (!dest_link) {
dest_link = FPRS_PARSE_UPLINK;
/* Don't repeat if it already came from uplink */
if (link != FPRS_PARSE_UPLINK)
dest_link = FPRS_PARSE_UPLINK;
else
goto skip;
}
cb(data, size, dest_link, arg);
@ -371,3 +405,12 @@ err_frame:
return r;
}
int fprs_parse_hook_message(
int (*cb)(uint8_t to[6], uint8_t from[6], void *data, size_t dsize, void *id, size_t isize, void *arg),
void *arg)
{
cb_message = cb;
arg_message = NULL;
return 0;
}

+ 5
- 0
fprs_parse.h View File

@ -34,4 +34,9 @@ int fprs_parse_request_flush(
int (*cb)(void *data, size_t size, unsigned int link, void *arg),
void *arg);
/* cb must return zero in order to claim a message as handled. */
int fprs_parse_hook_message(
int (*cb)(uint8_t to[6], uint8_t from[6], void *data, size_t dsize, void *id, size_t isize, void *arg),
void *arg);
#endif /* _INCLUDE_FPRS_PARSE_H_ */

+ 16
- 0
htdocs/fprs.js View File

@ -38,6 +38,9 @@ var FPRS = {
TIMESTAMP: 20,
DMLSTREAM: 21,
DMLASSOC: 22,
MESSAGE: 32,
MESSAGEID: 33,
MESSAGEACK: 34,
}
};
@ -153,6 +156,7 @@ function fprs_element(eltype, elsize, eldataview, eloff)
break;
case FPRS.ELEMENT.DESTINATION:
str+= "DESTINATION";
str+= eth_ar.call(dataview.buffer, dataview.byteOffset);
break;
case FPRS.ELEMENT.TIMESTAMP:
str+= "TIMESTAMP: ";
@ -174,6 +178,18 @@ function fprs_element(eltype, elsize, eldataview, eloff)
str+= "DMLASSOC: ";
str+= data2str(dataview);
break;
case FPRS.ELEMENT.MESSAGE:
str+= "MESSAGE: ";
str+= data2str(dataview);
break;
case FPRS.ELEMENT.MESSAGEID:
str+= "MESSAGEID: ";
str+= data2str(dataview);
break;
case FPRS.ELEMENT.MESSAGEACK:
str+= "MESSAGEACK: ";
str+= data2str(dataview);
break;
}
return str;


+ 144
- 0
soundlib.c View File

@ -21,6 +21,61 @@
#include <stdlib.h>
#include <stdio.h>
#ifdef HAVE_FLITE
#include <flite/flite.h>
#include <samplerate.h>
cst_voice *register_cmu_us_slt(void);
static cst_voice *flite_voice;
static char *spell(char c)
{
switch (c) {
case 'a': return "Alfa";
case 'b': return "Bravo";
case 'c': return "Charlie";
case 'd': return "Delta";
case 'e': return "Echo";
case 'f': return "Foxtrot";
case 'g': return "Golf";
case 'h': return "Hotel";
case 'i': return "India";
case 'j': return "Juliett";
case 'k': return "Kilo";
case 'l': return "Lima";
case 'm': return "Mike";
case 'n': return "November";
case 'o': return "Oscar";
case 'p': return "Papa";
case 'q': return "Quebec";
case 'r': return "Romeo";
case 's': return "Sierra";
case 't': return "Tango";
case 'u': return "Uniform";
case 'v': return "Victor";
case 'w': return "Whiskey";
case 'x': return "X-ray";
case 'y': return "Yankee";
case 'z': return "Zulu";
case '0': return "Zero";
case '1': return "One";
case '2': return "Two";
case '3': return "Three";
case '4': return "Four";
case '5': return "Five";
case '6': return "Six";
case '7': return "Seven";
case '8': return "Eight";
case '9': return "Nine";
case '-': return "Dash";
case '.': return "Point";
case '/': return "Slash";
}
return " ";
}
#endif
static int rate = 8000;
struct libentry {
@ -153,9 +208,98 @@ uint8_t *soundlib_get(int nr, size_t *size)
return entry->data;
}
#ifdef HAVE_FLITE
static uint8_t *soundlib_add_buffer(uint8_t *old, size_t *size, short *in_samples, size_t in_nr, int in_rate)
{
SRC_DATA src;
float data_in[in_nr];
long out_nr = (in_nr * rate)/in_rate;
float data_out[out_nr];
short short_out[out_nr];
src.data_in = data_in;
src.data_out = data_out;
src.input_frames = in_nr;
src.output_frames = out_nr;
src.src_ratio = rate / (double)in_rate;
src_short_to_float_array(in_samples, data_in, in_nr);
src_simple(&src, SRC_LINEAR, 1);
src_float_to_short_array(data_out, short_out, out_nr);
old = realloc(old, *size + out_nr);
if (!old)
return NULL;
alaw_encode(old + *size, short_out, out_nr);
*size += out_nr;
return old;
}
uint8_t *soundlib_synthesize(char *text, size_t *size)
{
if (size)
*size = 0;
uint8_t *sound = NULL;
cst_wave *wave = flite_text_to_wave(text, flite_voice);
if (wave) {
size_t addsize = 0;
sound = soundlib_add_buffer(sound, &addsize,
wave->samples, wave->num_samples, wave->sample_rate);
delete_wave(wave);
if (size)
*size = addsize;
}
return sound;
}
uint8_t *soundlib_spell(char *text, size_t *size)
{
int i;
uint8_t *sound = NULL;
size_t pos = 0;
for (i = 0; i < strlen(text); i++) {
char *letter = spell(text[i]);
cst_wave *wave = flite_text_to_wave(letter, flite_voice);
sound = soundlib_add_buffer(sound, &pos,
wave->samples, wave->num_samples, wave->sample_rate);
delete_wave(wave);
}
if (size)
*size = pos;
return sound;
}
#else
uint8_t *soundlib_spell(char *text, size_t *size)
{
if (size)
*size = 0;
return NULL;
}
#endif
int soundlib_init(int init_rate)
{
rate = init_rate;
#ifdef HAVE_FLITE
flite_voice = register_cmu_us_slt();
if (!flite_voice) {
printf("Could not select voice\n");
return -1;
}
#endif
return 0;
}

+ 2
- 0
soundlib.h View File

@ -26,6 +26,8 @@ int soundlib_add_silence(int nr, double length);
int soundlib_add_file(int nr, char *name);
uint8_t *soundlib_get(int nr, size_t *size);
uint8_t *soundlib_synthesize(char *text, size_t *size);
uint8_t *soundlib_spell(char *text, size_t *size);
int soundlib_init(int init_rate);


Loading…
Cancel
Save