mirror of
https://github.com/servalproject/serval-dna.git
synced 2024-12-23 23:12:31 +00:00
71ed78e058
Change a test case: configuration options are now case sensitive. Fix config file load and parse logic in conf.c, always copy 'debug' flags from config.debug. The config schema 'interfaces' option is no longer MANDATORY. Introduce new CLIFLAG_PERMISSIVE_CONFIG to supress bad-config ERROR messages from the 'config set' and 'config get' commands. Refactor cli_execute() into cli_parse() and cli_invoke(). Use *const* struct command_line_option everywhere.
260 lines
5.9 KiB
C
260 lines
5.9 KiB
C
#include <codec2.h>
|
|
#include <spandsp.h>
|
|
#include "fifo.h"
|
|
#include <portaudio.h>
|
|
#include <pthread.h>
|
|
#include <samplerate.h>
|
|
#include "serval.h"
|
|
|
|
/* Defines */
|
|
#define IN_FRAMES 128
|
|
#define NUM_BUFS (8)
|
|
#define ECHO_LEN (128)
|
|
#define ADAPT_MODE (ECHO_CAN_USE_ADAPTION | ECHO_CAN_USE_NLP | ECHO_CAN_USE_CNG)
|
|
|
|
#define CODEC2_BYTES_PER_FRAME ((CODEC2_BITS_PER_FRAME + 7) / 8)
|
|
|
|
/* Prototypes */
|
|
typedef struct {
|
|
PaStream *stream;
|
|
|
|
SRC_STATE *src;
|
|
|
|
pthread_mutex_t mtx; /* Mutex for frobbing queues */
|
|
|
|
/* Incoming samples after decompression
|
|
* Written with result of recvfrom + codec2_decode
|
|
* Read by sample rate converter
|
|
*/
|
|
struct fifo *incoming;
|
|
int incoverflow;
|
|
|
|
/* Samples after rate conversion
|
|
* Written by sample rate converter
|
|
* Read by PA callback
|
|
*/
|
|
struct fifo *incrate;
|
|
int underrun;
|
|
|
|
/* Outgoing samples
|
|
* Written by PA callback
|
|
* Read by codec2_encode + sendto
|
|
*/
|
|
struct fifo *outgoing;
|
|
|
|
int overrun;
|
|
|
|
echo_can_state_t *echocan; /* Echo canceller state */
|
|
void *codec2; /* Codec2 state */
|
|
|
|
} PaCtx;
|
|
|
|
static void freectx(PaCtx *ctx);
|
|
static int patestCallback(const void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer,
|
|
const PaStreamCallbackTimeInfo* timeInfo, PaStreamCallbackFlags statusFlags,
|
|
void *userData);
|
|
static PaCtx *pa_phone_setup(void);
|
|
|
|
/* Declarations */
|
|
|
|
int
|
|
app_pa_phone(int argc, const char *const *argv, const struct command_line_option *o)
|
|
{
|
|
PaCtx *ctx;
|
|
|
|
if ((ctx = pa_phone_setup()) == NULL)
|
|
return -1;
|
|
|
|
freectx(ctx);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* This routine will be called by the PortAudio engine when audio is needed.
|
|
** It may called at interrupt level on some machines so don't do anything
|
|
** that could mess up the system like calling malloc() or free().
|
|
*/
|
|
static int
|
|
patestCallback(const void *inputBuffer, void *outputBuffer,
|
|
unsigned long framesPerBuffer,
|
|
const PaStreamCallbackTimeInfo* timeInfo,
|
|
PaStreamCallbackFlags statusFlags,
|
|
void *userData) {
|
|
PaCtx *ctx;
|
|
int16_t *in, *out;
|
|
int avail, amt;
|
|
|
|
ctx = (PaCtx *)userData;
|
|
out = (int16_t *)outputBuffer;
|
|
in = (int16_t *)inputBuffer;
|
|
|
|
pthread_mutex_lock(&ctx->mtx);
|
|
|
|
amt = framesPerBuffer * sizeof(out[0]);
|
|
|
|
/* Copy out samples to be played */
|
|
if ((avail = fifo_get(ctx->incrate, (uint8_t *)out, amt)) < amt) {
|
|
/* Zero out samples there are no data for */
|
|
bzero(out + (avail / sizeof(out[0])), amt - avail);
|
|
ctx->underrun += (amt - avail) / sizeof(out[0]);
|
|
}
|
|
|
|
/* Copy in samples to be recorded */
|
|
if ((avail = fifo_put(ctx->outgoing, (uint8_t *)in, amt)) < amt) {
|
|
/* Zero out samples there are no data for */
|
|
bzero(in + (avail / sizeof(out[0])), amt - avail);
|
|
ctx->overrun += (amt - avail) / sizeof(out[0]);
|
|
}
|
|
|
|
#if 1
|
|
/* Run the echo canceller */
|
|
for (int ofs = 0; ofs < framesPerBuffer; ofs++)
|
|
out[ofs] = echo_can_update(ctx->echocan, in[ofs], out[ofs]);
|
|
#endif
|
|
pthread_mutex_unlock(&ctx->mtx);
|
|
|
|
return paContinue;
|
|
}
|
|
|
|
static PaCtx *
|
|
pa_phone_setup(void) {
|
|
PaCtx *ctx;
|
|
int err, i, srcerr;
|
|
PaError err2;
|
|
|
|
err = paNoError;
|
|
err2 = 0;
|
|
|
|
if ((ctx = calloc(1, sizeof(PaCtx))) == NULL) {
|
|
WHY("Unable to allocate PA context");
|
|
err2 = 1;
|
|
goto error;
|
|
}
|
|
|
|
/* Init mutex */
|
|
if (pthread_mutex_init(&ctx->mtx, NULL) != 0) {
|
|
WHYF("Unable to init mutex: %s\n", strerror(errno));
|
|
err2 = 1;
|
|
goto error;
|
|
}
|
|
|
|
/* Allocate FIFOs */
|
|
i = IN_FRAMES * 10 * sizeof(int16_t);
|
|
printf("Allocating %d byte FIFOs\n", i);
|
|
|
|
if ((ctx->incoming = fifo_alloc(i)) == NULL) {
|
|
WHY("Unable to allocate incoming FIFO\n");
|
|
err2 = 1;
|
|
goto error;
|
|
}
|
|
|
|
if ((ctx->incrate = fifo_alloc(i)) == NULL) {
|
|
WHY("Unable to allocate incoming SRC FIFO\n");
|
|
err2 = 1;
|
|
goto error;
|
|
}
|
|
|
|
if ((ctx->outgoing = fifo_alloc(i)) == NULL) {
|
|
WHY("Unable to allocate outgoing FIFO\n");
|
|
err2 = 1;
|
|
goto error;
|
|
}
|
|
|
|
|
|
/* Init sample rate converter */
|
|
if ((ctx->src = src_new(SRC_SINC_BEST_QUALITY, 1, &srcerr)) == NULL) {
|
|
WHYF("Unable to init sample rate converter: %d\n", srcerr);
|
|
err2 = 1;
|
|
goto error;
|
|
}
|
|
|
|
/* Init echo canceller */
|
|
if ((ctx->echocan = echo_can_init(ECHO_LEN, ADAPT_MODE)) == NULL) {
|
|
WHY("Unable to init echo canceller\n");
|
|
err2 = 1;
|
|
goto error;
|
|
}
|
|
|
|
/* Init codec2 */
|
|
if ((ctx->codec2 = codec2_create()) == NULL) {
|
|
WHY("Unable to init codec2\n");
|
|
err2 = 1;
|
|
goto error;
|
|
}
|
|
|
|
/* Initialize Port Audio library */
|
|
if ((err = Pa_Initialize()) != paNoError)
|
|
goto error;
|
|
|
|
/* Open an audio I/O stream. */
|
|
if ((err = Pa_OpenDefaultStream(&ctx->stream,
|
|
1, /* input channels */
|
|
1, /* output channels */
|
|
paInt16,
|
|
SAMPLE_RATE,
|
|
IN_FRAMES, /* frames per buffer */
|
|
patestCallback,
|
|
&ctx)) != paNoError)
|
|
goto error;
|
|
|
|
/* Start stream */
|
|
if ((err = Pa_StartStream(ctx->stream)) != paNoError)
|
|
goto error;
|
|
|
|
/* Close down stream, PA, etc */
|
|
/* XXX: hangs in pthread_join on Ubuntu 10.04 */
|
|
#ifndef linux
|
|
if ((err = Pa_StopStream(ctx->stream)) != paNoError)
|
|
goto error;
|
|
#endif
|
|
|
|
/* Do stuff */
|
|
|
|
if ((err = Pa_CloseStream(ctx->stream)) != paNoError)
|
|
goto error;
|
|
|
|
error:
|
|
Pa_Terminate();
|
|
|
|
/* Free things */
|
|
freectx(ctx);
|
|
|
|
if (err != paNoError)
|
|
WHYF("Port audio error: %s\n", Pa_GetErrorText(err));
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static void
|
|
freectx(PaCtx *ctx) {
|
|
/* Destroy mutex */
|
|
pthread_mutex_destroy(&ctx->mtx);
|
|
|
|
/* Free SRC resources */
|
|
if (ctx->src != NULL)
|
|
src_delete(ctx->src);
|
|
|
|
/* Free echo caneller */
|
|
if (ctx->echocan != NULL)
|
|
echo_can_free(ctx->echocan);
|
|
|
|
/* Free codec2 */
|
|
if (ctx->codec2 != NULL)
|
|
codec2_destroy(ctx->codec2);
|
|
|
|
/* Free FIFOs */
|
|
if (ctx->incoming != NULL)
|
|
fifo_free(ctx->incoming);
|
|
if (ctx->incrate != NULL)
|
|
fifo_free(ctx->incrate);
|
|
if (ctx->outgoing != NULL)
|
|
fifo_free(ctx->outgoing);
|
|
}
|
|
|
|
/*
|
|
* Local variables:
|
|
* c-basic-offset: 2
|
|
* End:
|
|
*/
|
|
|