/*****************************************************************************/

/*
 *      audiowin32.c  --  Audio processing for "virtual transceiver", win32 IO.
 *
 *      Copyright (C) 1999, 2001  Thomas Sailer (t.sailer@alumni.ethz.ch)
 *
 *      This program is free software; you can redistribute it and/or modify
 *      it under the terms of the GNU General Public License as published by
 *      the Free Software Foundation; either version 2 of the License, or
 *      (at your option) any later version.
 *
 *      This program is distributed in the hope that it will be useful,
 *      but WITHOUT ANY WARRANTY; without even the implied warranty of
 *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *      GNU General Public License for more details.
 *
 *      You should have received a copy of the GNU General Public License
 *      along with this program; if not, write to the Free Software
 *      Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 *  Please note that the GPL allows you to use the driver, NOT the radio.
 *  In order to use the radio, you need a license from the communications
 *  authority of your country.
 *
 */

/*****************************************************************************/

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

/* AIX requires this to be the first thing in the file.  */
#ifndef __GNUC__
# if HAVE_ALLOCA_H
#  include <alloca.h>
# else
#  ifdef _AIX
#pragma alloca
#  else
#   ifndef alloca /* predefined by HP cc +Olibcalls */
char *alloca ();
#   endif
#  endif
# endif
#endif

#include <windows.h>
#include <mmsystem.h>
#include <malloc.h>
#include <cguid.h>

#include "trx.h"
#include "trxapi.h"

/* ---------------------------------------------------------------------- */

#define BUFSIZE    8192   /* must be significantly bigger than SNDLATENCY! */

#define WAVE_FORMAT_1M08 1
#define WAVE_FORMAT_1S08 2
#define WAVE_FORMAT_1M16 4
#define WAVE_FORMAT_1S16 8
#define WAVE_FORMAT_2M08 16
#define WAVE_FORMAT_2S08 32
#define WAVE_FORMAT_2M16 64
#define WAVE_FORMAT_2S16 128
#define WAVE_FORMAT_4M08 256
#define WAVE_FORMAT_4S08 512
#define WAVE_FORMAT_4M16 1024
#define WAVE_FORMAT_4S16 2048
#define WAVE_FORMAT_PCM 1

/* ---------------------------------------------------------------------- */

#if defined(HAVE_DIRECTX)

#include <directx.h>

struct dsdrivers {
	struct list_head list;
        GUID guid;
        char name[64];
	char desc[64];
};

static LIST_HEAD(dsplaylist);
static LIST_HEAD(dscapturelist);

/* ---------------------------------------------------------------------- */

static void stop(struct trx_thread_state *state)
{
	if (state->du.a.io.dsplay) {
		IDirectSoundBuffer_Stop(state->du.a.io.playbuf);
		IDirectSoundBuffer_Release(state->du.a.io.playbuf);
	}
	if (state->du.a.io.dsrec) {
		IDirectSoundCaptureBuffer_Stop(state->du.a.io.recbuf);
		IDirectSoundCaptureBuffer_Release(state->du.a.io.recbuf);
		IDirectSoundCapture_Release(state->du.a.io.dsrec);
	}
	if (state->du.a.io.dsplay)
		IDirectSound_Release(state->du.a.io.dsplay);
	state->du.a.io.recbuf = NULL;
	state->du.a.io.dsrec = NULL;
	state->du.a.io.playbuf = NULL;
	state->du.a.io.dsplay = NULL;
}

static void start(struct trx_thread_state *state)
{
	struct list_head *list;
	struct dsdrivers *drv;
        LPGUID lpguid;
        HRESULT res;
        WAVEFORMATEX waveformat;
        DSBUFFERDESC bdesc;
        DSCBUFFERDESC cbdesc;
        DSCAPS caps;
        DSCCAPS ccaps;
        DSBCAPS bcaps;
        DSCBCAPS cbcaps;
        unsigned int isprimary = 0, weight = ~0;
        void *lptr1;
        DWORD lbytes1;
        HWND hwnd = GetDesktopWindow();

	lpguid = NULL;
	for (list = dsplaylist.next; list != &dsplaylist; list = list->next) {
		drv = list_entry(list, struct dsdrivers, list);
		if (strcmp(drv->desc, state->cfg.adapt.audiodevout))
			continue;
		if (memcmp(&drv->guid, &GUID_NULL, sizeof(drv->guid)))
			lpguid = &drv->guid;
		break;
	}
	if (FAILED(res = DirectSoundCreate(lpguid, &state->du.a.io.dsplay, NULL))) {
		lprintf(1, "DirectSoundCreate error 0x%lx\n", res);
		goto errdscreate;
	}
	if (FAILED(res = IDirectSound_SetCooperativeLevel(state->du.a.io.dsplay, hwnd, DSSCL_WRITEPRIMARY))) {
		lprintf(3, "SetCooperativeLevel DSSCL_WRITEPRIMARY error 0x%lx\n", res);
		if (FAILED(res = IDirectSound_SetCooperativeLevel(state->du.a.io.dsplay, hwnd, DSSCL_EXCLUSIVE))) {
			lprintf(1, "SetCooperativeLevel DSSCL_EXCLUSIVE error 0x%lx\n", res);
			goto errdsb;
		}
	} else
		isprimary = 1;
	lpguid = NULL;
	for (list = dscapturelist.next; list != &dscapturelist; list = list->next) {
		drv = list_entry(list, struct dsdrivers, list);
		if (strcmp(drv->desc, state->cfg.adapt.audiodevin))
			continue;
		if (memcmp(&drv->guid, &GUID_NULL, sizeof(drv->guid)))
			lpguid = &drv->guid;
		break;
	}
	if (FAILED(res = DirectSoundCaptureCreate(lpguid, &state->du.a.io.dsrec, NULL))) {
		lprintf(1, "DirectSoundCaptureCreate error 0x%lx\n", res);
		goto errdsb;
	}
	/* DirectSound capabilities */
	caps.dwSize = sizeof(caps);
	if (FAILED(res = IDirectSound_GetCaps(state->du.a.io.dsplay, &caps))) {
		lprintf(1, "DirectSoundGetCaps error 0x%lx\n", res);
		goto errdscb;
	}
	lprintf(5, "DirectSound capabilities:\n"
		"  Flags 0x%04lx\n"
		"  SampleRate min %lu max %lu\n"
		"  # Primary Buffers %lu\n",
		caps.dwFlags, caps.dwMinSecondarySampleRate, caps.dwMaxSecondarySampleRate, caps.dwPrimaryBuffers);
        /* DirectSoundCapture capabilities */
	ccaps.dwSize = sizeof(ccaps);
	if (FAILED(res = IDirectSoundCapture_GetCaps(state->du.a.io.dsrec, &ccaps))) {
		lprintf(1, "DirectSoundCaptureGetCaps error 0x%lx\n", res);
		goto errdscb;
	}
	lprintf(5, "DirectSoundCapture capabilities:\n"
		"  Flags 0x%04lx\n"
		"  Formats 0x%04lx\n"
		"  Channels %lu\n",
		ccaps.dwFlags, ccaps.dwFormats, ccaps.dwChannels);
        /* adjust sampling rate */
	if (!(caps.dwFlags & DSCAPS_PRIMARY16BIT) || !(caps.dwFlags & DSCAPS_PRIMARYMONO)) {
		lprintf(1, "Unsupported playback format 16bit mono\n");
		goto errdscb;
	}
	if (state->du.a.p.sratedspout < caps.dwMinSecondarySampleRate)
		state->du.a.p.sratedspout = caps.dwMinSecondarySampleRate;
	if (state->du.a.p.sratedspout > caps.dwMaxSecondarySampleRate)
		state->du.a.p.sratedspout = caps.dwMaxSecondarySampleRate;
	if ((ccaps.dwFormats & WAVE_FORMAT_1M16) && abs(state->du.a.p.srateusb - 11025) < weight) {
		weight = abs(state->du.a.p.srateusb - 11025);
		state->du.a.p.sratedspin = 11025;
	}
	if ((ccaps.dwFormats & WAVE_FORMAT_1M16) && abs(state->du.a.p.srateusb - 22050) < weight) {
		weight = abs(state->du.a.p.srateusb - 22050);
		state->du.a.p.sratedspin = 22050;
	}
	if ((ccaps.dwFormats & WAVE_FORMAT_1M16) && abs(state->du.a.p.srateusb - 44100) < weight) {
		weight = abs(state->du.a.p.srateusb - 44100);
		state->du.a.p.sratedspin = 44100;
	}
	if (weight == ~0) {
		lprintf(1, "Unsupported capture sampling rate\n");
		goto errdscb;
	}
        /* create capture buffer */
	memset(&waveformat, 0, sizeof(waveformat));
	waveformat.wFormatTag = WAVE_FORMAT_PCM;
	waveformat.wBitsPerSample = 16;
	waveformat.nChannels = 1;
	waveformat.nSamplesPerSec = state->du.a.p.sratedspin;
	waveformat.nBlockAlign = waveformat.nChannels * waveformat.wBitsPerSample / 8;
	waveformat.nAvgBytesPerSec = waveformat.nSamplesPerSec * waveformat.nBlockAlign;
	memset(&cbdesc, 0, sizeof(cbdesc));
	cbdesc.dwSize = sizeof(cbdesc);
	cbdesc.dwFlags = /* DSCBCAPS_WAVEMAPPED */ 0;
	cbdesc.dwBufferBytes = 65536;
	cbdesc.lpwfxFormat = &waveformat;
	if (FAILED(res = IDirectSoundCapture_CreateCaptureBuffer(state->du.a.io.dsrec, &cbdesc, &state->du.a.io.recbuf, NULL))) {
		lprintf(1, "CreateSoundCaptureBuffer error 0x%lx\n", res);
		goto errdscb;
	}
        /* create playback buffer */
	if (isprimary) {
		memset(&bdesc, 0, sizeof(bdesc));
		bdesc.dwSize = sizeof(bdesc);
		bdesc.dwFlags = DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_PRIMARYBUFFER;
		bdesc.dwBufferBytes = 0;
		bdesc.lpwfxFormat = NULL;
		if (FAILED(res = IDirectSound_CreateSoundBuffer(state->du.a.io.dsplay, &bdesc, &state->du.a.io.playbuf, NULL))) {
			lprintf(1, "DirectSoundCreateSoundBuffer error 0x%lx\n", res);
			goto errdspb;
		}
		memset(&waveformat, 0, sizeof(waveformat));
		waveformat.wFormatTag = WAVE_FORMAT_PCM;
		waveformat.wBitsPerSample = 16;
		waveformat.nChannels = 1;
		waveformat.nSamplesPerSec = state->du.a.p.sratedspout;
		waveformat.nBlockAlign = waveformat.nChannels * waveformat.wBitsPerSample / 8;
		waveformat.nAvgBytesPerSec = waveformat.nSamplesPerSec * waveformat.nBlockAlign;
		if (FAILED(res = IDirectSoundBuffer_SetFormat(state->du.a.io.playbuf, &waveformat))) {
			lprintf(1, "DirectSoundBufferSetFormat error 0x%lx\n", res);
			goto errsnd;
		}
		if (FAILED(res = IDirectSoundBuffer_GetFormat(state->du.a.io.playbuf, &waveformat, sizeof(waveformat), NULL))) {
			lprintf(1, "DirectSoundBufferGetFormat error 0x%lx\n", res);
			goto errsnd;
		}
		lprintf(5, "Sampling rates: Recording %u Playback %u\n", state->du.a.p.sratedspin, state->du.a.p.sratedspout);
	} else {
		/* first try to set the format of the primary buffer */
		memset(&bdesc, 0, sizeof(bdesc));
		bdesc.dwSize = sizeof(bdesc);
		bdesc.dwFlags = DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_PRIMARYBUFFER;
		bdesc.dwBufferBytes = 0;
		bdesc.lpwfxFormat = NULL;
		if (FAILED(res = IDirectSound_CreateSoundBuffer(state->du.a.io.dsplay, &bdesc, &state->du.a.io.playbuf, NULL))) {
			lprintf(1, "DirectSoundCreateSoundBuffer (primary) error 0x%lx\n", res);
		} else {
			memset(&waveformat, 0, sizeof(waveformat));
			waveformat.wFormatTag = WAVE_FORMAT_PCM;
			waveformat.wBitsPerSample = 16;
			waveformat.nChannels = 1;
			waveformat.nSamplesPerSec = state->du.a.p.sratedspout;
			waveformat.nBlockAlign = waveformat.nChannels * waveformat.wBitsPerSample / 8;
			waveformat.nAvgBytesPerSec = waveformat.nSamplesPerSec * waveformat.nBlockAlign;
			if (FAILED(res = IDirectSoundBuffer_SetFormat(state->du.a.io.playbuf, &waveformat))) {
				lprintf(1, "DirectSoundBufferSetFormat (primary) error 0x%lx\n", res);
			}
			IDirectSoundBuffer_Release(state->du.a.io.playbuf);        
		}
		/* create secondary */
		memset(&waveformat, 0, sizeof(waveformat));
		waveformat.wFormatTag = WAVE_FORMAT_PCM;
		waveformat.wBitsPerSample = 16;
		waveformat.nChannels = 1;
		waveformat.nSamplesPerSec = state->du.a.p.sratedspout;
		waveformat.nBlockAlign = waveformat.nChannels * waveformat.wBitsPerSample / 8;
		waveformat.nAvgBytesPerSec = waveformat.nSamplesPerSec * waveformat.nBlockAlign;
		memset(&bdesc, 0, sizeof(bdesc));
		bdesc.dwSize = sizeof(bdesc);
		bdesc.dwFlags = DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_GLOBALFOCUS;
		bdesc.dwBufferBytes = 65536;
		bdesc.lpwfxFormat = &waveformat;
		if (FAILED(res = IDirectSound_CreateSoundBuffer(state->du.a.io.dsplay, &bdesc, &state->du.a.io.playbuf, NULL))) {
			lprintf(1, "DirectSoundCreateSoundBuffer error 0x%lx\n", res);
			goto errdspb;
		}
	}
         /* find out buffer size */
	bcaps.dwSize = sizeof(bcaps);
	if (FAILED(res = IDirectSoundBuffer_GetCaps(state->du.a.io.playbuf, &bcaps))) {
		lprintf(1, "DirectSoundBufferGetCaps error 0x%lx\n", res);
		goto errsnd;
	}
	lprintf(1, "Playback buffer characteristics:\n"
		"  Flags 0x%04lx\n"
		"  Buffer Bytes %lu\n"
		"  Unlock Transfer rate %lu\n"
		"  CPU overhead %lu\n",
		bcaps.dwFlags, bcaps.dwBufferBytes, bcaps.dwUnlockTransferRate, bcaps.dwPlayCpuOverhead);
	state->du.a.io.playbufsz = bcaps.dwBufferBytes;
	cbcaps.dwSize = sizeof(cbcaps);
	if (FAILED(res = IDirectSoundCaptureBuffer_GetCaps(state->du.a.io.recbuf, &cbcaps))) {
		lprintf(1, "DirectSoundCaptureBufferGetCaps error 0x%lx\n", res);
		goto errsnd;
	}
	lprintf(5, "Recording buffer characteristics:\n"
		"  Flags 0x%04lx\n"
		"  Buffer Bytes %lu\n",
		cbcaps.dwFlags, cbcaps.dwBufferBytes);
	state->du.a.io.recbufsz = cbcaps.dwBufferBytes;
        /* start recording */
	if (FAILED(res = IDirectSoundCaptureBuffer_Start(state->du.a.io.recbuf, DSCBSTART_LOOPING))) {
		lprintf(1, "DirectSoundCaptureBufferStart error 0x%lx\n", res);
		goto errsnd;
	}
        /* zero playback buffer and start it */
	if (FAILED(res = IDirectSoundBuffer_Lock(state->du.a.io.playbuf, 0, state->du.a.io.playbufsz, &lptr1, &lbytes1, NULL, NULL, 0))) {
		if (res != DSERR_BUFFERLOST) {
			lprintf(1, "DirectSoundBufferLock error 0x%lx\n", res);
			goto errsnd;
		}
		if (FAILED(res = IDirectSoundBuffer_Restore(state->du.a.io.playbuf))) {
			lprintf(1, "DirectSoundBufferRestore error 0x%lx\n", res);
			goto errsnd;
		}
		if (FAILED(res = IDirectSoundBuffer_Lock(state->du.a.io.playbuf, 0, state->du.a.io.playbufsz, &lptr1, &lbytes1, NULL, NULL, 0))) {
			lprintf(1, "DirectSoundBufferLock error 0x%lx\n", res);
			goto errsnd;
		}
	}
	memset(lptr1, 0, lbytes1);
	if (FAILED(res = IDirectSoundBuffer_Unlock(state->du.a.io.playbuf, lptr1, lbytes1, NULL, 0))) {
		lprintf(1, "DirectSoundBufferUnlock error 0x%lx\n", res);
		goto errsnd;
	}
	if (FAILED(res = IDirectSoundBuffer_Play(state->du.a.io.playbuf, 0, 0, DSBPLAY_LOOPING))) {
		lprintf(1, "DirectSoundBufferPlay error 0x%lx\n", res);
		goto errsnd;
	}
 	state->du.a.io.playptr = 2*SNDLATENCY;
	state->du.a.io.recptr = state->du.a.io.recbufsz - 2*SNDLATENCY;
        return;

  errsnd:
	IDirectSoundBuffer_Stop(state->du.a.io.playbuf);
	IDirectSoundBuffer_Release(state->du.a.io.playbuf);
  errdspb:
	IDirectSoundCaptureBuffer_Stop(state->du.a.io.recbuf);
	IDirectSoundCaptureBuffer_Release(state->du.a.io.recbuf);
  errdscb:      
	IDirectSoundCapture_Release(state->du.a.io.dsrec);
  errdsb:
	IDirectSound_Release(state->du.a.io.dsplay);
  errdscreate:
	state->du.a.io.recbuf = NULL;
	state->du.a.io.dsrec = NULL;
	state->du.a.io.playbuf = NULL;
	state->du.a.io.dsplay = NULL;
	state->du.a.p.sratedspin = state->du.a.p.sratedspout = state->du.a.p.srateusb;
}

/* ---------------------------------------------------------------------- */

void audio_open(struct trx_thread_state *state)
{
	state->du.a.io.dsplay = NULL;
	state->du.a.io.dsrec = NULL;
	state->du.a.io.playbuf = NULL;
	state->du.a.io.recbuf = NULL;
	state->du.a.io.playptr = 0;
	state->du.a.io.recptr = 0;
	state->du.a.p.srateusb = state->du.a.p.sratedspin = state->du.a.p.sratedspout = AUDIOSAMPLINGRATE;
	start(state);
	audioproc_init(state);
}

void audio_close(struct trx_thread_state *state)
{
	stop(state);
}

void audio_input(struct trx_thread_state *state, const signed char *samples, unsigned int nrsamples)
{
	int16_t sbuf[BUFSIZE];
	HRESULT res;
	DWORD lockbytes1, lockbytes2, delay;
	int16_t *sptr1;
	int16_t *sptr2;
	int cnt;

	/* usb->dsp direction */
	/* get FIFO count */
	cnt = audioproc_convertoutput(state, nrsamples, samples, sbuf);
	if (!state->du.a.io.playbuf)
		return;
	if (FAILED(res = IDirectSoundBuffer_Lock(state->du.a.io.playbuf, state->du.a.io.playptr, cnt*2, 
						 (LPVOID)&sptr1, &lockbytes1,
						 (LPVOID)&sptr2, &lockbytes2, 0))) {
		lprintf(0, "IDirectSoundBuffer_Lock error %lu\n", res);
		goto err;
	}
	memcpy(sptr1, sbuf, lockbytes1);
	if (lockbytes1 < cnt*sizeof(sbuf[0]))
		memcpy(sptr2, sbuf + lockbytes1/sizeof(sbuf[0]), cnt*sizeof(sbuf[0]) - lockbytes1);
	if (FAILED(res = IDirectSoundBuffer_Unlock(state->du.a.io.playbuf, sptr1, lockbytes1, sptr2, lockbytes2))) {
		lprintf(0, "IDirectSoundBuffer_Unlock error %lu\n", res);
		goto err;
	}
	state->du.a.io.playptr = (state->du.a.io.playptr + cnt*sizeof(sbuf[0])) % state->du.a.io.playbufsz;
	/* get output delay */
	if (FAILED(res = IDirectSoundBuffer_GetCurrentPosition(state->du.a.io.playbuf, &delay, NULL))) {
		lprintf(0, "IDirectSoundBuffer_GetCurrentPosition error %lu\n", res);
		goto err;
	}
	delay = (state->du.a.io.playbufsz + state->du.a.io.playptr - delay) % state->du.a.io.playbufsz;
	delay /= sizeof(sbuf[0]);
	/* adjust speed */
	if (audioproc_adjustoutput(state, delay))
		goto err;
	return;

err:
	stop(state);
}

void audio_output(struct trx_thread_state *state, signed char *samples, unsigned int nrsamples)
{
	int16_t *sbuf;
	HRESULT res;
	DWORD lockbytes1, lockbytes2, delay;
	int16_t *sptr1;
	int16_t *sptr2;
	unsigned int isamples, ocnts;

	/* dsp->usb direction */
	if (!nrsamples)
		return;
	if (!state->du.a.io.recbuf) {
		isamples = audioproc_convertinput_isamples(state, nrsamples);
		sbuf = alloca(isamples * sizeof(sbuf[0]));
		memset(sbuf, 0, isamples * sizeof(sbuf[0]));
		audioproc_convertinput(state, isamples, nrsamples, sbuf, samples);
		return;
	}
	isamples = audioproc_convertinput_isamples(state, nrsamples);
	sbuf = alloca(isamples * sizeof(sbuf[0]));
	if (FAILED(res = IDirectSoundCaptureBuffer_Lock(state->du.a.io.recbuf, state->du.a.io.recptr, isamples * sizeof(sbuf[0]), 
							(LPVOID)&sptr1, &lockbytes1,
							(LPVOID)&sptr2, &lockbytes2, 0))) {
		lprintf(0, "IDirectSoundCaptureBuffer_Lock error %lu\n", res);
		goto err;
	}
	memcpy(sbuf, sptr1, lockbytes1);
	if (lockbytes1 < isamples * sizeof(sbuf[0]))
		memcpy(sbuf+lockbytes1/sizeof(sbuf[0]), sptr2, isamples * sizeof(sbuf[0]) - lockbytes1);
	if (FAILED(res = IDirectSoundCaptureBuffer_Unlock(state->du.a.io.recbuf, sptr1, lockbytes1, sptr2, lockbytes2))) {
		lprintf(0, "IDirectSoundCaptureBuffer_Unlock error %lu\n", res);
		goto err;
	}
	state->du.a.io.recptr = (state->du.a.io.recptr + isamples * sizeof(sbuf[0])) % state->du.a.io.recbufsz;
	audioproc_convertinput(state, isamples, nrsamples, sbuf, samples);
	if (FAILED(res = IDirectSoundCaptureBuffer_GetCurrentPosition(state->du.a.io.recbuf, &delay, NULL))) {
		lprintf(0, "IDirectSoundCaptureBuffer_GetCurrentPosition error %lu\n", res);
		goto err;
	}
	ocnts = (state->du.a.io.recbufsz + delay - state->du.a.io.recptr) % state->du.a.io.recbufsz;
	ocnts /= sizeof(sbuf[0]);
	/* adjust speed */
	if (audioproc_adjustinput(state, ocnts))
		goto err;
	return;

err:
	stop(state);
}

/* ---------------------------------------------------------------------- */

static BOOL CALLBACK DSEnumProc(LPGUID guid, LPCSTR lpszDesc, LPCSTR lpszDrvName, LPVOID lpcontext)
{
	struct list_head *list = lpcontext;
        struct dsdrivers *drv;
 
	if (!(drv = malloc(sizeof(struct dsdrivers)))) {
		lprintf(1, "DSEnumProc: out of memory\n");
		return TRUE;
	}
        if (guid)
                drv->guid = *guid;
        else
                drv->guid = GUID_NULL;
	if (!lpszDrvName)
		drv->name[0] = 0;
	else
		strncpy(drv->name, lpszDrvName, sizeof(drv->name));
	strncpy(drv->desc, lpszDesc, sizeof(drv->desc));
	drv->name[sizeof(drv->name)-1] = 0;
	drv->desc[sizeof(drv->desc)-1] = 0;
	list_add_tail(&drv->list, list);
        lprintf(1, "has %sGUID, desc %s drvname %s\n", guid ? "" : "no ", lpszDesc, lpszDrvName ? lpszDrvName : "(null)");
        return TRUE;
}

void audio_globalinit(void)
{
        lprintf(1, "DirectSound drivers\n");
        DirectSoundEnumerateA(DSEnumProc, &dsplaylist);
        lprintf(1, "DirectSoundCapture drivers\n");
        DirectSoundCaptureEnumerateA(DSEnumProc, &dscapturelist);
}

struct trxapi_baycomusb_adapter_audio_devs *audio_get_device_list(void)
{
	struct trxapi_baycomusb_adapter_audio_devs *ad;
	unsigned int nrdevin = 0, nrdevout = 0;
	struct list_head *list;
	struct dsdrivers *drv;
	unsigned int i;

	for (list = dscapturelist.next; list != &dscapturelist; list = list->next)
		nrdevin++;
	for (list = dsplaylist.next; list != &dsplaylist; list = list->next)
		nrdevout++;
	/* retrieve audio devs */
	if (!(ad = calloc(1, sizeof(struct trxapi_baycomusb_adapter_audio_devs) + 
			  nrdevin * sizeof(trxapi_audiodevice_t) + 
			  nrdevout * sizeof(trxapi_audiodevice_t))))
		return NULL;
	ad->audiodevsin = (void *)(ad + 1);
	ad->nraudiodevsin = nrdevin;
	ad->audiodevsout = &ad->audiodevsin[nrdevin];
	ad->nraudiodevsout = nrdevout;
	for (list = dscapturelist.next, i = 0; i < nrdevin; i++, list = list->next) {
		drv = list_entry(list, struct dsdrivers, list);
		strncpy(ad->audiodevsin[i], drv->desc, sizeof(ad->audiodevsin[i]));
		ad->audiodevsin[i][sizeof(ad->audiodevsin[i])-1] = 0;
	}
	for (list = dsplaylist.next, i = 0; i < nrdevout; i++, list = list->next) {
		drv = list_entry(list, struct dsdrivers, list);
		strncpy(ad->audiodevsout[i], drv->desc, sizeof(ad->audiodevsout[i]));
		ad->audiodevsout[i][sizeof(ad->audiodevsout[i])-1] = 0;
	}
	return ad;
}

/* ---------------------------------------------------------------------- */
#else /* !HAVE_DIRECTX */

void audio_globalinit(void)
{
}

struct trxapi_baycomusb_adapter_audio_devs *audio_get_device_list(void)
{
	return NULL;
}

void audio_open(struct trx_thread_state *state)
{
	state->du.a.p.srateusb = state->du.a.p.sratedspin = state->du.a.p.sratedspout = AUDIOSAMPLINGRATE;
	audioproc_init(state);
}

void audio_close(struct trx_thread_state *state)
{
}

void audio_input(struct trx_thread_state *state, const signed char *samples, unsigned int nrsamples)
{
	audioproc_convertoutput(state, nrsamples, samples, sbuf);
}

void audio_output(struct trx_thread_state *state, signed char *samples, unsigned int nrsamples)
{
	int16_t *sbuf;
	unsigned int isamples;

	if (!nrsamples)
		return;
	isamples = audioproc_convertinput_isamples(state, nrsamples);
	sbuf = alloca(isamples * sizeof(sbuf[0]));
	memset(sbuf, 0, isamples * sizeof(sbuf[0]));
	audioproc_convertinput(state, isamples, nrsamples, sbuf, samples);
}

/* ---------------------------------------------------------------------- */
#endif /* HAVE_DIRECTX */
