summaryrefslogtreecommitdiff
path: root/modules/winwave/src.c
diff options
context:
space:
mode:
Diffstat (limited to 'modules/winwave/src.c')
-rw-r--r--modules/winwave/src.c206
1 files changed, 206 insertions, 0 deletions
diff --git a/modules/winwave/src.c b/modules/winwave/src.c
new file mode 100644
index 0000000..1793bf3
--- /dev/null
+++ b/modules/winwave/src.c
@@ -0,0 +1,206 @@
+/**
+ * @file winwave/src.c Windows sound driver -- source
+ *
+ * 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 READ_BUFFERS 4
+#define INC_RPOS(a) ((a) = (((a) + 1) % READ_BUFFERS))
+
+
+struct ausrc_st {
+ struct ausrc *as; /* inheritance */
+ struct dspbuf bufs[READ_BUFFERS];
+ int pos;
+ HWAVEIN wavein;
+ bool rdy;
+ size_t inuse;
+ ausrc_read_h *rh;
+ void *arg;
+};
+
+
+static void ausrc_destructor(void *arg)
+{
+ struct ausrc_st *st = arg;
+ int i;
+
+ st->rh = NULL;
+
+ waveInStop(st->wavein);
+ waveInReset(st->wavein);
+ waveInClose(st->wavein);
+
+ for (i = 0; i < READ_BUFFERS; i++) {
+ waveInUnprepareHeader(st->wavein, &st->bufs[i].wh,
+ sizeof(WAVEHDR));
+ mem_deref(st->bufs[i].mb);
+ }
+
+ mem_deref(st->as);
+}
+
+
+static int add_wave_in(struct ausrc_st *st)
+{
+ struct dspbuf *db = &st->bufs[st->pos];
+ WAVEHDR *wh = &db->wh;
+ MMRESULT res;
+
+ wh->lpData = (LPSTR)db->mb->buf;
+ wh->dwBufferLength = db->mb->size;
+ wh->dwBytesRecorded = 0;
+ wh->dwFlags = 0;
+ wh->dwUser = (DWORD_PTR)db->mb;
+
+ waveInPrepareHeader(st->wavein, wh, sizeof(*wh));
+ res = waveInAddBuffer(st->wavein, wh, sizeof(*wh));
+ if (res != MMSYSERR_NOERROR) {
+ DEBUG_WARNING("add_wave_in: waveInAddBuffer fail: %08x\n",
+ res);
+ return ENOMEM;
+ }
+
+ INC_RPOS(st->pos);
+
+ st->inuse++;
+
+ return 0;
+}
+
+
+static void CALLBACK waveInCallback(HWAVEOUT hwo,
+ UINT uMsg,
+ DWORD_PTR dwInstance,
+ DWORD_PTR dwParam1,
+ DWORD_PTR dwParam2)
+{
+ struct ausrc_st *st = (struct ausrc_st *)dwInstance;
+ WAVEHDR *wh = (WAVEHDR *)dwParam1;
+
+ (void)hwo;
+ (void)dwParam2;
+
+ if (!st->rh)
+ return;
+
+ switch (uMsg) {
+
+ case WIM_CLOSE:
+ st->rdy = false;
+ break;
+
+ case WIM_OPEN:
+ st->rdy = true;
+ break;
+
+ case WIM_DATA:
+ if (st->inuse < 3)
+ add_wave_in(st);
+
+ st->rh((uint8_t *)wh->lpData, wh->dwBytesRecorded, st->arg);
+
+ waveInUnprepareHeader(st->wavein, wh, sizeof(*wh));
+ st->inuse--;
+ break;
+
+ default:
+ break;
+ }
+}
+
+
+static int read_stream_open(struct ausrc_st *st, const struct ausrc_prm *prm)
+{
+ WAVEFORMATEX wfmt;
+ MMRESULT res;
+ uint32_t sampc;
+ int i, err = 0;
+
+ /* Open an audio INPUT stream. */
+ st->wavein = NULL;
+ st->pos = 0;
+ st->rdy = false;
+
+ sampc = prm->srate * prm->ch * prm->ptime / 1000;
+
+ for (i = 0; i < READ_BUFFERS; i++) {
+ memset(&st->bufs[i].wh, 0, sizeof(WAVEHDR));
+ st->bufs[i].mb = mbuf_alloc(2 * sampc);
+ if (!st->bufs[i].mb)
+ return ENOMEM;
+ }
+
+ 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 = waveInOpen(&st->wavein, WAVE_MAPPER, &wfmt,
+ (DWORD_PTR) waveInCallback,
+ (DWORD_PTR) st,
+ CALLBACK_FUNCTION | WAVE_FORMAT_DIRECT);
+ if (res != MMSYSERR_NOERROR) {
+ DEBUG_WARNING("waveInOpen: failed %d\n", err);
+ return EINVAL;
+ }
+
+ /* Prepare enough IN buffers to suite at least 50ms of data */
+ for (i = 0; i < READ_BUFFERS; i++)
+ err |= add_wave_in(st);
+
+ waveInStart(st->wavein);
+
+ return err;
+}
+
+
+int winwave_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;
+ int err;
+
+ (void)ctx;
+ (void)device;
+ (void)errh;
+
+ if (!stp || !as || !prm)
+ return EINVAL;
+
+ st = mem_zalloc(sizeof(*st), ausrc_destructor);
+ if (!st)
+ return ENOMEM;
+
+ st->as = mem_ref(as);
+ st->rh = rh;
+ st->arg = arg;
+
+ prm->fmt = AUFMT_S16LE;
+
+ err = read_stream_open(st, prm);
+
+ if (err)
+ mem_deref(st);
+ else
+ *stp = st;
+
+ return err;
+}