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

/*
 *      audiowin32.c  --  Audio processing for "virtual transceiver", win32 IO.
 *
 *      Copyright (C) 1999  Thomas Sailer (sailer@ife.ee.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

#ifdef HAVE_DIRECTX

#include "eppfm.h"

#include <directx.h>

#include <gtk/gtk.h>
#include <glib.h>

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

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

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

struct {
	enum { st_mdmerr, st_off, st_play, st_rec } state;

	/* gtk timeout handle */
	gint tmotag;
	/* sampling rate conversion state */
	int16_t sample[OVERLAP];
	/* control register */
	unsigned char ctrlreg;
	/* led blink counter */
	unsigned ledcnt;

	/* low level driver specific data */
	LPDIRECTSOUND dsplay;
	LPDIRECTSOUNDCAPTURE dsrec;
	LPDIRECTSOUNDBUFFER playbuf;
	LPDIRECTSOUNDCAPTUREBUFFER recbuf;
	HANDLE hinst;
	HWND hwnd;
	DWORD playbufsz, recbufsz, playptr, recptr;
} state = { st_mdmerr, };

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

static BOOL CALLBACK DSEnumProc(LPGUID guid, LPCSTR lpszDesc, LPCSTR lpszDrvName, LPVOID lpcontext)
{
        lprintf(1, "has %sGUID, desc %s drvname %s\n", guid ? "" : "no ", lpszDesc, lpszDrvName);
        return TRUE;
}

#if 0
static LRESULT CALLBACK MyWndProc(HWND hwnd, UINT umsg, WPARAM wParam, LPARAM lParam)
{
	switch(umsg) {
		/* Add cases such as WM_CREATE, WM_COMMAND, WM_PAINT if you don't 
		   want to pass these messages along for default processing. */
	case WM_CLOSE:
		DestroyWindow(hwnd);
		return 0;

	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	}
	return DefWindowProc(hwnd, umsg, wParam, lParam);
}
#endif

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

static gint periodic_play(gpointer data)
{
	union {
		unsigned char u[BUFSIZE];
		signed char s[BUFSIZE];
	} buf;
	int16_t sbuf[BUFSIZE];
	HRESULT res;
	DWORD lockbytes1, lockbytes2, delay;
	int16_t *sptr1;
	int16_t *sptr2;
	int icnt, cnt;

	/* epp->dsp direction */
	/* get FIFO count */
	buf.u[0] = state.ctrlreg | 1;
	if (parport_epp_write_addr(buf.u, 1) != 1)
		goto err;
	if (parport_epp_read_addr(buf.u, 2) != 2)
		goto err;
	icnt = buf.u[0] | (((unsigned int)buf.u[1]) << 8);
	buf.u[0] = state.ctrlreg;
	if (parport_epp_write_addr(buf.u, 1) != 1)
		goto err;
	icnt &= 0x7fff;
	if (icnt > 0) {
		if (icnt > BUFSIZE/2)
			icnt = BUFSIZE/2;
		memcpy(buf.s, state.sample, OVERLAP);
		if (parport_epp_read_data(buf.s+OVERLAP, icnt) != icnt)
			goto err;
		memcpy(state.sample, buf.s+icnt, OVERLAP);
		cnt = audio_convertoutput(icnt, buf.s+OVERLAP, sbuf);
		if (FAILED(res = IDirectSoundBuffer_Lock(state.playbuf, state.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 < 2*cnt)
			memcpy(sptr2, sbuf + lockbytes1/2, 2*cnt - lockbytes1);
		if (FAILED(res = IDirectSoundBuffer_Unlock(state.playbuf, sptr1, lockbytes1, sptr2, lockbytes2))) {
			lprintf(0, "IDirectSoundBuffer_Unlock error %lu\n", res);
			goto err;
		}
		state.playptr = (state.playptr + 2*cnt) % state.playbufsz;
		/* get output delay */
		if (FAILED(res = IDirectSoundBuffer_GetCurrentPosition(state.playbuf, &delay, NULL))) {
			lprintf(0, "IDirectSoundBuffer_GetCurrentPosition error %lu\n", res);
			goto err;
		}
		delay = (state.playbufsz + state.playptr - delay) % state.playbufsz;
		delay >>= 1;
		/* adjust speed */
		if (audio_adjustoutput(cnt, icnt, delay))
			goto err;
		/* play games with the LEDS */
		state.ledcnt += icnt;
		if (state.ledcnt >= 4000) {
			state.ledcnt %= 4000;
			state.ctrlreg += 0x40;
		}
		/* DCD led */
		if (audio_getsquelch())
			state.ctrlreg |= 0x10;
		else
			state.ctrlreg &= ~0x10;
	}
	return TRUE;

err:
	lprintf(0, "EPP timeout\n");
	IDirectSoundBuffer_Stop(state.playbuf);
	IDirectSoundBuffer_Release(state.playbuf);
	IDirectSound_Release(state.dsplay);
#if 0
	DestroyWindow(state.hwnd);
	UnregisterClass("MyClass", state.hinst);
#endif
	state.hwnd = NULL;
	reset_modem();
	state.state = st_mdmerr;
	return FALSE;
}

static gint periodic_rec(gpointer data)
{
	union {
		unsigned char u[BUFSIZE];
		signed char s[BUFSIZE];
	} buf;
	int16_t sbuf[BUFSIZE];
	HRESULT res;
	DWORD lockbytes1, lockbytes2, delay;
	int16_t *sptr1;
	int16_t *sptr2;
	int ocnt, cnt, ocnts, omax;

	/* dsp->epp direction */
	/* read sound */
	memcpy(sbuf, state.sample, OVERLAP*2);
	/* read sound */
	if (FAILED(res = IDirectSoundCaptureBuffer_GetCurrentPosition(state.recbuf, &delay, NULL))) {
		lprintf(0, "IDirectSoundCaptureBuffer_GetCurrentPosition error %lu\n", res);
		goto err;
	}
	ocnts = (state.recbufsz + delay - state.recptr) % state.recbufsz;
	ocnts &= ~1;
	if (ocnts > 0) {
		if (FAILED(res = IDirectSoundCaptureBuffer_Lock(state.recbuf, state.recptr, ocnts, 
								(LPVOID)&sptr1, &lockbytes1,
								(LPVOID)&sptr2, &lockbytes2, 0))) {
			lprintf(0, "IDirectSoundCaptureBuffer_Lock error %lu\n", res);
			goto err;
		}
		memcpy(sbuf+OVERLAP, sptr1, lockbytes1);
		if (lockbytes1 < ocnts)
			memcpy(sbuf+OVERLAP+lockbytes1/2, sptr2, ocnts - lockbytes1);
		if (FAILED(res = IDirectSoundCaptureBuffer_Unlock(state.recbuf, sptr1, lockbytes1, sptr2, lockbytes2))) {
			lprintf(0, "IDirectSoundCaptureBuffer_Unlock error %lu\n", res);
			goto err;
		}
		state.recptr = (state.recptr + ocnts) % state.recbufsz;
	}
	ocnts >>= 1;
	memcpy(state.sample, sbuf+ocnts, OVERLAP*2);
	/* get FIFO count */
	buf.u[0] = state.ctrlreg | 2;
	if (parport_epp_write_addr(buf.u, 1) != 1)
		goto err;
	if (parport_epp_read_addr(buf.u, 2) != 2)
		goto err;
	ocnt = buf.u[0] | (((unsigned int)buf.u[1]) << 8);
	buf.u[0] = state.ctrlreg;
	if (parport_epp_write_addr(buf.u, 1) != 1)
		goto err;
	ocnt &= 0x7fff;
	omax = 16384 - ocnt;
	cnt = 0;
	if (ocnts > 0) {
		cnt = audio_convertinput(ocnts, sbuf+OVERLAP, buf.s);
		if (cnt > omax) {
			lprintf(0, "epp adapter output overrun (%d, %d)\n", cnt, omax);
			goto err;
		}
		if (parport_epp_write_data(buf.s, cnt) != cnt)
			goto err;
		/* reget the FIFO count */
		buf.u[0] = state.ctrlreg | 2;
		if (parport_epp_write_addr(buf.u, 1) != 1)
			goto err;
		if (parport_epp_read_addr(buf.u, 2) != 2)
			goto err;
		ocnt = buf.u[0] | (((unsigned int)buf.u[1]) << 8);
		buf.u[0] = state.ctrlreg;
		if (parport_epp_write_addr(buf.u, 1) != 1)
			goto err;
		ocnt &= 0x7fff;
	}
	/* adjust speed */
	if (audio_adjustinput(cnt, ocnts, ocnt))
		goto err;
	return TRUE;

err:
	lprintf(0, "EPP timeout\n");
	IDirectSoundCaptureBuffer_Stop(state.recbuf);
	IDirectSoundCaptureBuffer_Release(state.recbuf);
	IDirectSoundCapture_Release(state.dsrec);
#if 0
	DestroyWindow(state.hwnd);
	UnregisterClass("MyClass", state.hinst);
#endif
	state.hwnd = NULL;
	reset_modem();
	state.state = st_mdmerr;
	return FALSE;
}

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

static void playstop(void)
{
	unsigned char buf;

	if (state.state != st_play)
		return;
	gtk_timeout_remove(state.tmotag);
	IDirectSoundBuffer_Stop(state.playbuf);
	IDirectSoundBuffer_Release(state.playbuf);
	IDirectSound_Release(state.dsplay);
#if 0
	DestroyWindow(state.hwnd);
	UnregisterClass("MyClass", state.hinst);
#endif
	state.hwnd = NULL;
	/* reset the EPP adapter */
	buf = 7;
	if (parport_epp_write_addr(&buf, 1) != 1) {
		state.state = st_mdmerr;
		reset_modem();
	} else
		state.state = st_off;
}

static void recstop(void)
{
	unsigned char buf;

	if (state.state != st_rec)
		return;
	gtk_timeout_remove(state.tmotag);
	IDirectSoundCaptureBuffer_Stop(state.recbuf);
	IDirectSoundCaptureBuffer_Release(state.recbuf);
	IDirectSoundCapture_Release(state.dsrec);
#if 0
	DestroyWindow(state.hwnd);
	UnregisterClass("MyClass", state.hinst);
#endif
	state.hwnd = NULL;
	/* reset the EPP adapter */
	buf = 7;
	if (parport_epp_write_addr(&buf, 1) != 1) {
		state.state = st_mdmerr;
		reset_modem();
	} else
		state.state = st_off;
}

static void playstart(void)
{
	WNDCLASS wndclass;
	HRESULT res;
        WAVEFORMATEX waveformat;
        DSBUFFERDESC bdesc;
	unsigned char buf[2];
	DWORD lockbytes;
	int16_t *sptr;
	unsigned srate;
	int i;

	if (state.state == st_play)
		playstop();
	if (state.state == st_rec)
		recstop();
	state.ctrlreg = 0;
	state.ledcnt = 0;
	memset(state.sample, 0, sizeof(state.sample));
	/* start modem if mdmerr */
	if (state.state == st_mdmerr) {
		cfg.bitrate = SAMPLINGRATE;
		if ((i = adapter_start_eppsamp(&cfg))) {
			lprintf(0, "Cannot initialize the modem\n");
			return;
		}
	}
	state.state = st_off;
	state.dsplay = NULL;
	state.playbuf = NULL;
	state.playptr = 0;
#if 0
	wndclass.style = CS_HREDRAW | CS_VREDRAW;
	wndclass.lpfnWndProc = (WNDPROC)MyWndProc;
	wndclass.cbClsExtra = 0;
	wndclass.cbWndExtra = 0;
	wndclass.hIcon = NULL;
	wndclass.hInstance = state.hinst;
	wndclass.hCursor = NULL;
	wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
	wndclass.lpszMenuName = NULL;
	wndclass.lpszClassName = "MyClass";
	if (!RegisterClass(&wndclass)) {
		lprintf(0, "RegisterClass error, %d\n", GetLastError());
		goto errregcls;
	}
	state.hwnd = CreateWindowEx(WS_EX_APPWINDOW, "MyClass", "eppfpga", WS_DISABLED | WS_POPUP,
				    0, 0, 0, 0, NULL, NULL, state.hinst, NULL);
	if (!state.hwnd) {
		lprintf(0, "CreateWindowEx error, %d\n", GetLastError());
		goto errwnd;
	}
#else
	state.hwnd = GetDesktopWindow();
#endif
        if (FAILED(res = DirectSoundCreate(NULL, &state.dsplay, NULL))) {
                lprintf(0, "DirectSoundCreate error %lu\n", res);
                goto errdscreate;
        }
        if (FAILED(res = IDirectSound_SetCooperativeLevel(state.dsplay, state.hwnd, DSSCL_NORMAL))) {
                lprintf(0, "SetCooperativeLevel error %lu\n", res);
                goto errdsb;
        }
        memset(&waveformat, 0, sizeof(waveformat));
        waveformat.wFormatTag = WAVE_FORMAT_PCM;
        waveformat.wBitsPerSample = 16;
        waveformat.nChannels = 1;
        waveformat.nSamplesPerSec = cfg.bitrate;
        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 | DSBCAPS_CTRLPAN;
        state.playbufsz = bdesc.dwBufferBytes = waveformat.nAvgBytesPerSec;
        bdesc.lpwfxFormat = &waveformat;
        if (FAILED(res = IDirectSound_CreateSoundBuffer(state.dsplay, &bdesc, &state.playbuf, NULL))) {
                lprintf(0, "CreateSoundBuffer error %lu\n", res);
                goto errdsb;
        }
        if (FAILED(res = IDirectSoundBuffer_GetFormat(state.playbuf, &waveformat, sizeof(waveformat), NULL))) {
                lprintf(0, "GetFormat error %lu\n", res);
                goto errsnd;
        }
	srate = waveformat.nSamplesPerSec;
	lprintf(2, "epp adapter sampling rate %u, soundcard sampling rate %u\n", cfg.bitrate, srate);
	if (abs(cfg.bitrate - srate) > cfg.bitrate / 2) {
		lprintf(0, "sampling rates (%u,%u) too different\n", cfg.bitrate, srate);
		goto errsnd;
	}
	audio_initoutput(cfg.bitrate, srate);
	lprintf(0, "Audio Output to Linux Soundcard\n");
	/* reset the EPP adapter */
	buf[0] = 7;
	buf[1] = state.ctrlreg;
	if (parport_epp_write_addr(buf, 2) != 2)
		goto errret;
	/* prefill to nominal queue size and start soundcard */
	if (FAILED(res = IDirectSoundBuffer_Lock(state.playbuf, 0, 2*SNDLATENCY, (LPVOID)&sptr, &lockbytes, NULL, NULL, 0))) {
                lprintf(0, "IDirectSoundBuffer_Lock error %lu\n", res);
                goto errsnd;
        }
	memset(sptr, 0, 2*SNDLATENCY);
	if (FAILED(res = IDirectSoundBuffer_Unlock(state.playbuf, sptr, lockbytes, NULL, 0))) {
                lprintf(0, "IDirectSoundBuffer_Unlock error %lu\n", res);
                goto errsnd;
        }
	state.playptr = 2*SNDLATENCY;
	if (FAILED(res = IDirectSoundBuffer_Play(state.playbuf, 0, 0, DSBPLAY_LOOPING))) {
                lprintf(0, "IDirectSoundBuffer_Play error %lu\n", res);
                goto errsnd;
        }
	state.tmotag = gtk_timeout_add(100, periodic_play, NULL);
	state.state = st_play;
	return;

  errdsb:
	IDirectSound_Release(state.dsplay);
  errdscreate:
#if 0
	DestroyWindow(state.hwnd);
  errwnd:
	UnregisterClass("MyClass", state.hinst);
#endif
  errregcls:
	buf[0] = 7;
	if (parport_epp_write_addr(buf, 1) != 1) {
		state.state = st_mdmerr;
		reset_modem();
	} else		
		state.state = st_off;
	return;
	
  errsnd:
	buf[0] = 7;
	if (parport_epp_write_addr(buf, 1) != 1)
		goto errret;
	state.state = st_off;
	goto errclose;

  errret:
	lprintf(0, "EPP timeout\n");
	state.state = st_mdmerr;
	reset_modem();
  errclose:
	IDirectSoundBuffer_Stop(state.playbuf);
	IDirectSoundBuffer_Release(state.playbuf);
	IDirectSound_Release(state.dsplay);
#if 0
	DestroyWindow(state.hwnd);
	UnregisterClass("MyClass", state.hinst);
#endif
	state.hwnd = NULL;
}

static void recstart(void)
{
	WNDCLASS wndclass;
	HRESULT res;
        WAVEFORMATEX waveformat;
        DSCBUFFERDESC cbdesc;
	unsigned char buf[SNDLATENCY];
	unsigned srate;
	int i;

	if (state.state == st_play)
		playstop();
	if (state.state == st_rec)
		recstop();
	state.ctrlreg = 0x20;
	state.ledcnt = 0;
	memset(state.sample, 0, sizeof(state.sample));
	/* start modem if mdmerr */
	if (state.state == st_mdmerr) {
		cfg.bitrate = SAMPLINGRATE;
		if ((i = adapter_start_eppsamp(&cfg))) {
			lprintf(0, "Cannot initialize the modem\n");
			return;
		}
	}
	state.state = st_off;
	state.dsrec = NULL;
	state.recbuf = NULL;
	state.recptr = 0;
#if 0
	wndclass.style = CS_HREDRAW | CS_VREDRAW;
	wndclass.lpfnWndProc = (WNDPROC)MyWndProc;
	wndclass.cbClsExtra = 0;
	wndclass.cbWndExtra = 0;
	wndclass.hIcon = NULL;
	wndclass.hInstance = state.hinst;
	wndclass.hCursor = NULL;
	wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
	wndclass.lpszMenuName = NULL;
	wndclass.lpszClassName = "MyClass";
	if (!RegisterClass(&wndclass)) {
		lprintf(0, "RegisterClass error, %d\n", GetLastError());
		goto errregcls;
	}
	state.hwnd = CreateWindowEx(WS_EX_APPWINDOW, "MyClass", "eppfpga", WS_DISABLED | WS_POPUP,
				    0, 0, 0, 0, NULL, NULL, state.hinst, NULL);
	if (!state.hwnd) {
		lprintf(0, "CreateWindowEx error, %d\n", GetLastError());
		goto errwnd;
	}
#else
	state.hwnd = GetDesktopWindow();
#endif
        if (FAILED(res = DirectSoundCaptureCreate(NULL, &state.dsrec, NULL))) {
                lprintf(0, "DirectSoundCaptureCreate error %lu\n", res);
                goto errdscreate;
        }
        memset(&waveformat, 0, sizeof(waveformat));
        waveformat.wFormatTag = WAVE_FORMAT_PCM;
        waveformat.wBitsPerSample = 16;
        waveformat.nChannels = 1;
        waveformat.nSamplesPerSec = cfg.bitrate;
        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;
        state.recbufsz = cbdesc.dwBufferBytes = waveformat.nAvgBytesPerSec;
        cbdesc.lpwfxFormat = &waveformat;
        if (FAILED(res = IDirectSoundCapture_CreateCaptureBuffer(state.dsrec, &cbdesc, &state.recbuf, NULL))) {
                lprintf(0, "CreateSoundCaptureBuffer error %lu\n", res);
                goto errdsb;
        }
	/* kludge */ srate = cfg.bitrate;
        if (FAILED(res = IDirectSoundCaptureBuffer_GetFormat(state.recbuf, &waveformat, sizeof(waveformat), NULL))) {
                lprintf(0, "GetFormat error %lu\n", res);
                goto errsnd;
        }
	srate = waveformat.nSamplesPerSec;
	lprintf(2, "epp adapter sampling rate %u, soundcard sampling rate %u\n", cfg.bitrate, srate);
	if (abs(cfg.bitrate - srate) > cfg.bitrate / 2) {
		lprintf(0, "sampling rates (%u,%u) too different\n", cfg.bitrate, srate);
		goto errsnd;
	}
	audio_initinput(cfg.bitrate, srate);
	lprintf(0, "Audio Input from Linux Soundcard\n");
	/* reset the EPP adapter */
	buf[0] = 7;
	buf[1] = state.ctrlreg;
	if (parport_epp_write_addr(buf, 2) != 2)
		goto errret;
	/* prefill EPP adapter and start soundcard */
	memset(buf, 0, sizeof(buf));
	if (parport_epp_write_data(buf, SNDLATENCY) != SNDLATENCY)
		goto errret;
	if (FAILED(res = IDirectSoundCaptureBuffer_Start(state.recbuf, DSCBSTART_LOOPING))) {
                lprintf(0, "IDirectSoundCaptureBuffer_Start error %lu\n", res);
                goto errsnd;
        }
	state.tmotag = gtk_timeout_add(100, periodic_rec, NULL);
	state.state = st_rec;
	return;

  errdsb:
	IDirectSoundCapture_Release(state.dsrec);
  errdscreate:
#if 0
	DestroyWindow(state.hwnd);
  errwnd:
	UnregisterClass("MyClass", state.hinst);
  errregcls:
#endif
	buf[0] = 7;
	if (parport_epp_write_addr(buf, 1) != 1) {
		state.state = st_mdmerr;
		reset_modem();
	} else		
		state.state = st_off;
	return;
	
  errsnd:
	buf[0] = 7;
	if (parport_epp_write_addr(buf, 1) != 1)
		goto errret;
	state.state = st_off;
	goto errclose;

  errret:
	lprintf(0, "EPP timeout\n");
	state.state = st_mdmerr;
	reset_modem();
  errclose:
	IDirectSoundCaptureBuffer_Stop(state.recbuf);
	IDirectSoundCaptureBuffer_Release(state.recbuf);
	IDirectSoundCapture_Release(state.dsrec);
#if 0
	DestroyWindow(state.hwnd);
	UnregisterClass("MyClass", state.hinst);
#endif
	state.hwnd = NULL;
}

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

void audio_ptt(int ptt)
{
	if (ptt && state.state != st_rec)
		recstart();
	else if (!ptt && state.state != st_play)
		playstart();
}

int audio_drvinit(const char *config)
{
	state.dsplay = NULL;
	state.dsrec = NULL;
	state.playbuf = NULL;
	state.recbuf = NULL;
	state.hwnd = NULL;
	state.hinst = GetModuleHandleA(0);
	state.playptr = 0;
	state.recptr = 0;
        lprintf(1, "DirectSound drivers\n");
        DirectSoundEnumerateA(DSEnumProc, NULL);
        lprintf(1, "DirectSoundCapture drivers\n");
        DirectSoundCaptureEnumerateA(DSEnumProc, NULL);
	return 0;
}

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