diff options
Diffstat (limited to 'modules/portaudio')
-rw-r--r-- | modules/portaudio/module.mk | 11 | ||||
-rw-r--r-- | modules/portaudio/portaudio.c | 331 |
2 files changed, 342 insertions, 0 deletions
diff --git a/modules/portaudio/module.mk b/modules/portaudio/module.mk new file mode 100644 index 0000000..81e5b80 --- /dev/null +++ b/modules/portaudio/module.mk @@ -0,0 +1,11 @@ +# +# module.mk +# +# Copyright (C) 2010 Creytiv.com +# + +MOD := portaudio +$(MOD)_SRCS += portaudio.c +$(MOD)_LFLAGS += -lportaudio + +include mk/mod.mk diff --git a/modules/portaudio/portaudio.c b/modules/portaudio/portaudio.c new file mode 100644 index 0000000..36ca3f1 --- /dev/null +++ b/modules/portaudio/portaudio.c @@ -0,0 +1,331 @@ +/** + * @file portaudio.c Portaudio sound driver + * + * Copyright (C) 2010 Creytiv.com + */ +#include <stdlib.h> +#include <string.h> +#include <portaudio.h> +#include <re.h> +#include <rem.h> +#include <baresip.h> + + +/* + * portaudio v19 is required + */ + +struct ausrc_st { + struct ausrc *as; /* inheritance */ + PaStream *stream_rd; + ausrc_read_h *rh; + void *arg; + volatile bool ready; + unsigned ch; +}; + +struct auplay_st { + struct auplay *ap; /* inheritance */ + PaStream *stream_wr; + auplay_write_h *wh; + void *arg; + volatile bool ready; + unsigned ch; +}; + + +static struct ausrc *ausrc; +static struct auplay *auplay; + + +/* + * 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 read_callback(const void *inputBuffer, void *outputBuffer, + unsigned long frameCount, + const PaStreamCallbackTimeInfo *timeInfo, + PaStreamCallbackFlags statusFlags, void *userData) +{ + struct ausrc_st *st = userData; + unsigned sampc; + + (void)outputBuffer; + (void)timeInfo; + (void)statusFlags; + + if (!st->ready) + return paAbort; + + sampc = frameCount * st->ch; + + st->rh(inputBuffer, 2*sampc, st->arg); + + return paContinue; +} + + +static int write_callback(const void *inputBuffer, void *outputBuffer, + unsigned long frameCount, + const PaStreamCallbackTimeInfo *timeInfo, + PaStreamCallbackFlags statusFlags, void *userData) +{ + struct auplay_st *st = userData; + unsigned sampc; + + (void)inputBuffer; + (void)timeInfo; + (void)statusFlags; + + if (!st->ready) + return paAbort; + + sampc = frameCount * st->ch; + + st->wh(outputBuffer, 2*sampc, st->arg); + + return paContinue; +} + + +static int read_stream_open(struct ausrc_st *st, const struct ausrc_prm *prm, + uint32_t dev) +{ + PaStreamParameters prm_in; + PaError err; + unsigned long frames_per_buffer = prm->srate * prm->ptime / 1000; + + memset(&prm_in, 0, sizeof(prm_in)); + prm_in.device = dev; + prm_in.channelCount = prm->ch; + prm_in.sampleFormat = paInt16; + prm_in.suggestedLatency = 0.100; + + st->stream_rd = NULL; + err = Pa_OpenStream(&st->stream_rd, &prm_in, NULL, prm->srate, + frames_per_buffer, paNoFlag, read_callback, st); + if (paNoError != err) { + warning("portaudio: read: Pa_OpenStream: %s\n", + Pa_GetErrorText(err)); + return EINVAL; + } + + err = Pa_StartStream(st->stream_rd); + if (paNoError != err) { + warning("portaudio: read: Pa_StartStream: %s\n", + Pa_GetErrorText(err)); + return EINVAL; + } + + return 0; +} + + +static int write_stream_open(struct auplay_st *st, + const struct auplay_prm *prm, uint32_t dev) +{ + PaStreamParameters prm_out; + PaError err; + unsigned long frames_per_buffer = prm->srate * prm->ptime / 1000; + + memset(&prm_out, 0, sizeof(prm_out)); + prm_out.device = dev; + prm_out.channelCount = prm->ch; + prm_out.sampleFormat = paInt16; + prm_out.suggestedLatency = 0.100; + + st->stream_wr = NULL; + err = Pa_OpenStream(&st->stream_wr, NULL, &prm_out, prm->srate, + frames_per_buffer, paNoFlag, write_callback, st); + if (paNoError != err) { + warning("portaudio: write: Pa_OpenStream: %s\n", + Pa_GetErrorText(err)); + return EINVAL; + } + + err = Pa_StartStream(st->stream_wr); + if (paNoError != err) { + warning("portaudio: write: Pa_StartStream: %s\n", + Pa_GetErrorText(err)); + return EINVAL; + } + + return 0; +} + + +static void ausrc_destructor(void *arg) +{ + struct ausrc_st *st = arg; + + st->ready = false; + + if (st->stream_rd) { + Pa_AbortStream(st->stream_rd); + Pa_CloseStream(st->stream_rd); + } + + mem_deref(st->as); +} + + +static void auplay_destructor(void *arg) +{ + struct auplay_st *st = arg; + + st->ready = false; + + if (st->stream_wr) { + Pa_AbortStream(st->stream_wr); + Pa_CloseStream(st->stream_wr); + } + + mem_deref(st->ap); +} + + +static int src_alloc(struct ausrc_st **stp, struct ausrc *as, + struct media_ctx **ctx, + struct ausrc_prm *prm, const char *device, + ausrc_read_h *rh, ausrc_error_h *errh, void *arg) +{ + struct ausrc_st *st; + PaDeviceIndex dev_index; + int err; + + (void)ctx; + (void)device; + (void)errh; + + if (!stp || !as || !prm) + return EINVAL; + + if (str_isset(device)) + dev_index = atoi(device); + else + dev_index = Pa_GetDefaultInputDevice(); + + prm->fmt = AUFMT_S16LE; + + st = mem_zalloc(sizeof(*st), ausrc_destructor); + if (!st) + return ENOMEM; + + st->as = mem_ref(as); + st->rh = rh; + st->arg = arg; + st->ch = prm->ch; + + st->ready = true; + + err = read_stream_open(st, prm, dev_index); + if (err) + goto out; + + out: + if (err) + mem_deref(st); + else + *stp = st; + + return err; +} + + +static int play_alloc(struct auplay_st **stp, struct auplay *ap, + struct auplay_prm *prm, const char *device, + auplay_write_h *wh, void *arg) +{ + struct auplay_st *st; + PaDeviceIndex dev_index; + int err; + + (void)device; + + if (!stp || !ap || !prm) + return EINVAL; + + if (str_isset(device)) + dev_index = atoi(device); + else + dev_index = Pa_GetDefaultOutputDevice(); + + prm->fmt = AUFMT_S16LE; + + st = mem_zalloc(sizeof(*st), auplay_destructor); + if (!st) + return ENOMEM; + + st->ap = mem_ref(ap); + st->wh = wh; + st->arg = arg; + st->ch = prm->ch; + + st->ready = true; + + err = write_stream_open(st, prm, dev_index); + if (err) + goto out; + + out: + if (err) + mem_deref(st); + else + *stp = st; + + return err; +} + + +static int pa_init(void) +{ + PaError paerr; + int i, n, err = 0; + + paerr = Pa_Initialize(); + if (paNoError != paerr) { + warning("portaudio: init: %s\n", Pa_GetErrorText(paerr)); + return ENODEV; + } + + n = Pa_GetDeviceCount(); + + info("portaudio: device count is %d\n", n); + + for (i=0; i<n; i++) { + const PaDeviceInfo *devinfo; + + devinfo = Pa_GetDeviceInfo(i); + + debug("portaudio: device %d: %s\n", i, devinfo->name); + (void)devinfo; + } + + if (paNoDevice != Pa_GetDefaultInputDevice()) + err |= ausrc_register(&ausrc, "portaudio", src_alloc); + + if (paNoDevice != Pa_GetDefaultOutputDevice()) + err |= auplay_register(&auplay, "portaudio", play_alloc); + + return err; +} + + +static int pa_close(void) +{ + ausrc = mem_deref(ausrc); + auplay = mem_deref(auplay); + + Pa_Terminate(); + + return 0; +} + + +EXPORT_SYM const struct mod_export DECL_EXPORTS(portaudio) = { + "portaudio", + "sound", + pa_init, + pa_close +}; |