diff options
Diffstat (limited to 'modules/winwave/play.c')
-rw-r--r-- | modules/winwave/play.c | 223 |
1 files changed, 223 insertions, 0 deletions
diff --git a/modules/winwave/play.c b/modules/winwave/play.c new file mode 100644 index 0000000..1de2e1f --- /dev/null +++ b/modules/winwave/play.c @@ -0,0 +1,223 @@ +/** + * @file winwave/play.c Windows sound driver -- playback + * + * Copyright (C) 2010 Creytiv.com + */ +#include <re.h> +#include <rem.h> +#include <windows.h> +#include <mmsystem.h> +#include <baresip.h> +#include "winwave.h" + + +#define DEBUG_MODULE "winwave" +#define DEBUG_LEVEL 5 +#include <re_dbg.h> + + +#define WRITE_BUFFERS 4 +#define INC_WPOS(a) ((a) = (((a) + 1) % WRITE_BUFFERS)) + + +struct auplay_st { + struct auplay *ap; /* inheritance */ + struct dspbuf bufs[WRITE_BUFFERS]; + int pos; + HWAVEOUT waveout; + bool rdy; + size_t inuse; + auplay_write_h *wh; + void *arg; +}; + + +static void auplay_destructor(void *arg) +{ + struct auplay_st *st = arg; + int i; + + st->wh = NULL; + + /* Mark the device for closing, and wait for all the + * buffers to be returned by the driver + */ + st->rdy = false; + while (st->inuse > 0) + Sleep(50); + + waveOutClose(st->waveout); + + for (i = 0; i < WRITE_BUFFERS; i++) { + waveOutUnprepareHeader(st->waveout, &st->bufs[i].wh, + sizeof(WAVEHDR)); + mem_deref(st->bufs[i].mb); + } + + mem_deref(st->ap); +} + + +static int dsp_write(struct auplay_st *st) +{ + MMRESULT res; + WAVEHDR *wh; + struct mbuf *mb; + + if (!st->rdy) + return EINVAL; + + wh = &st->bufs[st->pos].wh; + if (wh->dwFlags & WHDR_PREPARED) { + return EINVAL; + } + mb = st->bufs[st->pos].mb; + wh->lpData = (LPSTR)mb->buf; + + if (st->wh) { + st->wh(mb->buf, mb->size, st->arg); + } + + wh->dwBufferLength = mb->size; + wh->dwFlags = 0; + wh->dwUser = (DWORD_PTR) mb; + + waveOutPrepareHeader(st->waveout, wh, sizeof(*wh)); + + INC_WPOS(st->pos); + + res = waveOutWrite(st->waveout, wh, sizeof(*wh)); + if (res != MMSYSERR_NOERROR) + DEBUG_WARNING("dsp_write: waveOutWrite: failed: %08x\n", res); + else + st->inuse++; + + return 0; +} + + +static void CALLBACK waveOutCallback(HWAVEOUT hwo, + UINT uMsg, + DWORD_PTR dwInstance, + DWORD_PTR dwParam1, + DWORD_PTR dwParam2) +{ + struct auplay_st *st = (struct auplay_st *)dwInstance; + WAVEHDR *wh = (WAVEHDR *)dwParam1; + + (void)hwo; + (void)dwParam2; + + switch (uMsg) { + + case WOM_OPEN: + st->rdy = true; + break; + + case WOM_DONE: + /*LOCK();*/ + waveOutUnprepareHeader(st->waveout, wh, sizeof(*wh)); + /*UNLOCK();*/ + st->inuse--; + dsp_write(st); + break; + + case WOM_CLOSE: + st->rdy = false; + break; + + default: + break; + } +} + + +static int write_stream_open(struct auplay_st *st, + const struct auplay_prm *prm) +{ + WAVEFORMATEX wfmt; + MMRESULT res; + uint32_t sampc; + int i; + + /* Open an audio I/O stream. */ + st->waveout = NULL; + st->pos = 0; + st->rdy = false; + + sampc = prm->srate * prm->ch * prm->ptime / 1000; + + for (i = 0; i < WRITE_BUFFERS; i++) { + memset(&st->bufs[i].wh, 0, sizeof(WAVEHDR)); + st->bufs[i].mb = mbuf_alloc(2 * sampc); + } + + wfmt.wFormatTag = WAVE_FORMAT_PCM; + wfmt.nChannels = prm->ch; + wfmt.nSamplesPerSec = prm->srate; + wfmt.wBitsPerSample = 16; + wfmt.nBlockAlign = (prm->ch * wfmt.wBitsPerSample) / 8; + wfmt.nAvgBytesPerSec = wfmt.nSamplesPerSec * wfmt.nBlockAlign; + wfmt.cbSize = 0; + + res = waveOutOpen(&st->waveout, WAVE_MAPPER, &wfmt, + (DWORD_PTR) waveOutCallback, + (DWORD_PTR) st, + CALLBACK_FUNCTION | WAVE_FORMAT_DIRECT); + if (res != MMSYSERR_NOERROR) { + DEBUG_WARNING("waveOutOpen: failed %d\n", res); + return EINVAL; + } + waveOutClose(st->waveout); + res = waveOutOpen(&st->waveout, WAVE_MAPPER, &wfmt, + (DWORD_PTR) waveOutCallback, + (DWORD_PTR) st, + CALLBACK_FUNCTION | WAVE_FORMAT_DIRECT); + if (res != MMSYSERR_NOERROR) { + DEBUG_WARNING("waveOutOpen: failed %d\n", res); + return EINVAL; + } + + return 0; +} + + +int winwave_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; + int i, err; + (void)device; + + if (!stp || !ap || !prm) + return EINVAL; + + st = mem_zalloc(sizeof(*st), auplay_destructor); + if (!st) + return ENOMEM; + + st->ap = mem_ref(ap); + st->wh = wh; + st->arg = arg; + + prm->fmt = AUFMT_S16LE; + + err = write_stream_open(st, prm); + if (err) + goto out; + + /* The write runs at 100ms intervals + * prepare enough buffers to suite its needs + */ + for (i = 0; i < 5; i++) + dsp_write(st); + + out: + if (err) + mem_deref(st); + else + *stp = st; + + return err; +} |