2012-04-23 11:10:34 +00:00
|
|
|
#include <codec2.h>
|
|
|
|
#include <spandsp.h>
|
2012-04-23 07:02:47 +00:00
|
|
|
#include "fifo.h"
|
2012-04-22 22:54:39 +00:00
|
|
|
#include <portaudio.h>
|
2012-04-23 11:10:34 +00:00
|
|
|
#include <pthread.h>
|
2012-04-23 07:02:47 +00:00
|
|
|
#include <samplerate.h>
|
|
|
|
#include "serval.h"
|
|
|
|
|
|
|
|
/* Defines */
|
2012-04-23 11:10:34 +00:00
|
|
|
#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)
|
2012-04-23 07:02:47 +00:00
|
|
|
#define MIN(x, y) ((x) > (y) ? y : x)
|
|
|
|
#define MAX(x, y) ((x) < (y) ? y : x)
|
|
|
|
|
|
|
|
#define CODEC2_BYTES_PER_FRAME ((CODEC2_BITS_PER_FRAME + 7) / 8)
|
|
|
|
|
|
|
|
/* Prototypes */
|
|
|
|
typedef struct {
|
2012-04-23 11:10:34 +00:00
|
|
|
PaStream *stream;
|
|
|
|
|
2012-04-23 07:02:47 +00:00
|
|
|
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;
|
2012-04-22 22:54:39 +00:00
|
|
|
|
2012-04-23 07:02:47 +00:00
|
|
|
int overrun;
|
2012-04-22 22:54:39 +00:00
|
|
|
|
2012-04-23 07:02:47 +00:00
|
|
|
echo_can_state_t *echocan; /* Echo canceller state */
|
|
|
|
void *codec2; /* Codec2 state */
|
|
|
|
|
|
|
|
} PaCtx;
|
|
|
|
|
2012-04-23 11:28:28 +00:00
|
|
|
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);
|
|
|
|
|
2012-04-23 07:02:47 +00:00
|
|
|
/* Declarations */
|
|
|
|
|
2012-04-23 11:28:28 +00:00
|
|
|
int
|
|
|
|
app_pa_phone(int argc, const char *const *argv, struct command_line_option *o) {
|
|
|
|
PaCtx *ctx;
|
|
|
|
|
|
|
|
if ((ctx = pa_phone_setup()) == NULL)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
freectx(ctx);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2012-04-22 22:54:39 +00:00
|
|
|
|
|
|
|
/* This routine will be called by the PortAudio engine when audio is needed.
|
2012-04-23 07:02:47 +00:00
|
|
|
** 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;
|
2012-04-22 22:54:39 +00:00
|
|
|
}
|
|
|
|
|
2012-04-23 11:28:28 +00:00
|
|
|
static PaCtx *
|
2012-04-23 07:02:47 +00:00
|
|
|
pa_phone_setup(void) {
|
2012-04-23 11:10:34 +00:00
|
|
|
PaCtx *ctx;
|
|
|
|
int err, i, srcerr;
|
|
|
|
PaError err2;
|
2012-04-23 07:02:47 +00:00
|
|
|
|
|
|
|
err = paNoError;
|
|
|
|
err2 = 0;
|
|
|
|
|
|
|
|
if ((ctx = calloc(1, sizeof(PaCtx))) == NULL) {
|
|
|
|
WHY("Unable to allocate PA context");
|
|
|
|
err2 = 1;
|
|
|
|
goto error;
|
|
|
|
}
|
2012-04-22 22:54:39 +00:00
|
|
|
|
2012-04-23 07:02:47 +00:00
|
|
|
/* Init mutex */
|
|
|
|
if (pthread_mutex_init(&ctx->mtx, NULL) != 0) {
|
2012-04-23 11:10:34 +00:00
|
|
|
WHYF("Unable to init mutex: %s\n", strerror(errno));
|
2012-04-23 07:02:47 +00:00
|
|
|
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) {
|
2012-04-23 11:10:34 +00:00
|
|
|
WHYF("Unable to init sample rate converter: %d\n", srcerr);
|
2012-04-23 07:02:47 +00:00
|
|
|
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. */
|
2012-04-23 11:10:34 +00:00
|
|
|
if ((err = Pa_OpenDefaultStream(&ctx->stream,
|
2012-04-23 07:02:47 +00:00
|
|
|
1, /* input channels */
|
|
|
|
1, /* output channels */
|
|
|
|
paInt16,
|
|
|
|
SAMPLE_RATE,
|
|
|
|
IN_FRAMES, /* frames per buffer */
|
|
|
|
patestCallback,
|
|
|
|
&ctx)) != paNoError)
|
|
|
|
goto error;
|
2012-04-22 22:54:39 +00:00
|
|
|
|
2012-04-23 07:02:47 +00:00
|
|
|
/* Start stream */
|
2012-04-23 11:10:34 +00:00
|
|
|
if ((err = Pa_StartStream(ctx->stream)) != paNoError)
|
2012-04-23 07:02:47 +00:00
|
|
|
goto error;
|
2012-04-22 22:54:39 +00:00
|
|
|
|
2012-04-23 07:02:47 +00:00
|
|
|
/* Close down stream, PA, etc */
|
|
|
|
/* XXX: hangs in pthread_join on Ubuntu 10.04 */
|
|
|
|
#ifndef linux
|
2012-04-23 11:10:34 +00:00
|
|
|
if ((err = Pa_StopStream(ctx->stream)) != paNoError)
|
2012-04-23 07:02:47 +00:00
|
|
|
goto error;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* Do stuff */
|
|
|
|
|
2012-04-23 11:10:34 +00:00
|
|
|
if ((err = Pa_CloseStream(ctx->stream)) != paNoError)
|
2012-04-23 07:02:47 +00:00
|
|
|
goto error;
|
|
|
|
|
|
|
|
error:
|
|
|
|
Pa_Terminate();
|
|
|
|
|
|
|
|
/* Free things */
|
2012-04-23 11:10:34 +00:00
|
|
|
freectx(ctx);
|
2012-04-23 07:02:47 +00:00
|
|
|
|
|
|
|
if (err != paNoError)
|
2012-04-23 11:10:34 +00:00
|
|
|
WHYF("Port audio error: %s\n", Pa_GetErrorText(err));
|
2012-04-23 07:02:47 +00:00
|
|
|
|
|
|
|
return NULL;
|
2012-04-22 22:54:39 +00:00
|
|
|
}
|
|
|
|
|
2012-04-23 11:28:28 +00:00
|
|
|
static void
|
2012-04-23 11:10:34 +00:00
|
|
|
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);
|
|
|
|
}
|
2012-04-23 07:02:47 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Local variables:
|
|
|
|
* c-basic-offset: 2
|
|
|
|
* End:
|
|
|
|
*/
|
|
|
|
|