/*
 * MMSYTEM functions
 *
 * Copyright 1993 Martin Ayotte
 */
/* FIXME: I think there are some segmented vs. linear pointer weirdnesses 
 *        and long term pointers to 16 bit space in here
 */

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/ioctl.h>
#include "windows.h"
#include "win.h"
#include "heap.h"
#include "ldt.h"
#include "user.h"
#include "driver.h"
#include "file.h"
#include "mmsystem.h"
#include "stddebug.h"
/* #define DEBUG_MMSYS */
#include "debug.h"
#include "xmalloc.h"
#include "callback.h"

static int	InstalledCount;
static int	InstalledListLen;
static LPSTR	lpInstallNames = NULL;

struct LINUX_MCIDRIVER mciDrv[MAXMCIDRIVERS];

UINT16 midiGetErrorText(UINT16 uError, LPSTR lpText, UINT16 uSize);
static UINT16 waveGetErrorText(UINT16 uError, LPSTR lpText, UINT16 uSize);
LONG WINAPI DrvDefDriverProc(DWORD dwDevID, HDRVR16 hDriv, WORD wMsg, 
                             DWORD dwParam1, DWORD dwParam2);

LONG WAVE_DriverProc(DWORD dwDevID, HDRVR16 hDriv, WORD wMsg, 
		     DWORD dwParam1, DWORD dwParam2);
LONG MIDI_DriverProc(DWORD dwDevID, HDRVR16 hDriv, WORD wMsg, 
		     DWORD dwParam1, DWORD dwParam2);
LONG CDAUDIO_DriverProc(DWORD dwDevID, HDRVR16 hDriv, WORD wMsg, 
			DWORD dwParam1, DWORD dwParam2);
LONG ANIM_DriverProc(DWORD dwDevID, HDRVR16 hDriv, WORD wMsg, 
		     DWORD dwParam1, DWORD dwParam2);


#define GetDrv(wDevID) (&mciDrv[MMSYSTEM_DevIDToIndex(wDevID)])
#define GetOpenDrv(wDevID) (&(GetDrv(wDevID)->mop))

/* The wDevID's returned by wine were originally in the range 
 * 0 - (MAXMCIDRIVERS - 1) and used directly as array indices.
 * Unfortunately, ms-windows uses wDevID of zero to indicate
 * errors.  Now, multimedia drivers must pass the wDevID through
 * MMSYSTEM_DevIDToIndex to get an index in that range.  An
 * aribtrary value, MMSYSTEM_MAGIC is added to the wDevID seen
 * by the windows programs.
 */

#define MMSYSTEM_MAGIC 0x0F00

/**************************************************************************
 * 				MMSYSTEM_DevIDToIndex	[internal]
 */
int MMSYSTEM_DevIDToIndex(UINT16 wDevID) {
	return wDevID - MMSYSTEM_MAGIC;
}

/**************************************************************************
 * 				MMSYSTEM_FirstDevId	[internal]
 */
UINT16 MMSYSTEM_FirstDevID(void)
{
	return MMSYSTEM_MAGIC;
}

/**************************************************************************
 * 				MMSYSTEM_NextDevId	[internal]
 */
UINT16 MMSYSTEM_NextDevID(UINT16 wDevID) {
	return wDevID + 1;
}

/**************************************************************************
 * 				MMSYSTEM_DevIdValid	[internal]
 */
BOOL32 MMSYSTEM_DevIDValid(UINT16 wDevID) {
	return wDevID >= 0x0F00 && wDevID < (0x0F00 + MAXMCIDRIVERS);
}

/**************************************************************************
 * 				MMSYSTEM_WEP		[MMSYSTEM.1]
 */
int WINAPI MMSYSTEM_WEP(HINSTANCE16 hInstance, WORD wDataSeg,
                        WORD cbHeapSize, LPSTR lpCmdLine)
{
	fprintf(stderr, "STUB: Unloading MMSystem DLL ... hInst=%04X \n", hInstance);
	return(TRUE);
}

void
MMSYSTEM_MMTIME32to16(LPMMTIME16 mmt16,LPMMTIME32 mmt32) {
	mmt16->wType = mmt32->wType;
	/* layout of rest is the same for 32/16 */
	memcpy(&(mmt32->u),&(mmt16->u),sizeof(mmt16->u));
}

void
MMSYSTEM_MMTIME16to32(LPMMTIME32 mmt32,LPMMTIME16 mmt16) {
	mmt32->wType = mmt16->wType;
	/* layout of rest is the same for 32/16,
	 * Note: mmt16->u is 2 bytes smaller than mmt32->u
	 */
	memcpy(&(mmt16->u),&(mmt32->u),sizeof(mmt16->u));
}

/**************************************************************************
 * 				PlaySoundA		[WINMM.1]
 */
BOOL32 WINAPI PlaySound32A(LPCSTR pszSound, HMODULE32 hmod, DWORD fdwSound)
{
  dprintf_mmsys(stddeb, "PlaySoundA: pszSound='%p' hmod=%04X fdwSound=%08lX\n",
		pszSound, hmod, fdwSound);
  if(hmod != 0 || !(fdwSound & SND_FILENAME)) {
    fprintf(stderr, "PlaySoundA: only disk sound files are supported\n");
    return FALSE;
  } else
    return  sndPlaySound(pszSound, (UINT16) fdwSound);
}

/**************************************************************************
 * 				PlaySoundW		[WINMM.18]
 */
BOOL32 WINAPI PlaySound32W(LPCWSTR pszSound, HMODULE32 hmod, DWORD fdwSound)
{
  LPSTR pszSoundA = HEAP_strdupWtoA(GetProcessHeap(),0,pszSound);
  BOOL32 bSound;

  bSound = PlaySound32A(pszSoundA, hmod, fdwSound);
  HeapFree(GetProcessHeap(),0,pszSoundA);
  return bSound;
}

/**************************************************************************
* 				sndPlaySound		[MMSYSTEM.2]
*/
BOOL16 WINAPI sndPlaySound(LPCSTR lpszSoundName, UINT16 uFlags)
{
	BOOL16			bRet = FALSE;
	HMMIO16			hmmio;
	MMCKINFO                ckMainRIFF;
	char			str[128];
	LPSTR			ptr;

	dprintf_mmsys(stddeb, "sndPlaySound // SoundName='%s' uFlags=%04X !\n", 
									lpszSoundName, uFlags);
	if (lpszSoundName == NULL) {
		dprintf_mmsys(stddeb, "sndPlaySound // Stop !\n");
		return FALSE;
		}
	hmmio = mmioOpen16((LPSTR)lpszSoundName, NULL, 
		MMIO_ALLOCBUF | MMIO_READ | MMIO_DENYWRITE);

	if (uFlags & SND_MEMORY) {
		dprintf_mmsys(stddeb, "sndPlaySound // SND_MEMORY flag not implemented!\n");
		return FALSE;
	}

	if (hmmio == 0) 
	{
		dprintf_mmsys(stddeb, "sndPlaySound // searching in SystemSound List !\n");
		GetProfileString32A("Sounds", (LPSTR)lpszSoundName, "", str, sizeof(str));
		if (strlen(str) == 0) return FALSE;
		if ( (ptr = (LPSTR)strchr(str, ',')) != NULL) *ptr = '\0';
		hmmio = mmioOpen16(str, NULL, MMIO_ALLOCBUF | MMIO_READ | MMIO_DENYWRITE);
		if (hmmio == 0) 
		{
			dprintf_mmsys(stddeb, "sndPlaySound // can't find SystemSound='%s' !\n", str);
			return FALSE;
		}
	}

	if (mmioDescend(hmmio, &ckMainRIFF, NULL, 0) == 0) 
        {
	    dprintf_mmsys(stddeb, "sndPlaySound // ParentChunk ckid=%.4s fccType=%.4s cksize=%08lX \n",
				(LPSTR)&ckMainRIFF.ckid, (LPSTR)&ckMainRIFF.fccType, ckMainRIFF.cksize);

	    if ((ckMainRIFF.ckid == FOURCC_RIFF) &&
	    	(ckMainRIFF.fccType == mmioFOURCC('W', 'A', 'V', 'E')))
	    {
		MMCKINFO        mmckInfo;

		mmckInfo.ckid = mmioFOURCC('f', 'm', 't', ' ');

		if (mmioDescend(hmmio, &mmckInfo, &ckMainRIFF, MMIO_FINDCHUNK) == 0)
		{
		    PCMWAVEFORMAT           pcmWaveFormat;

		    dprintf_mmsys(stddeb, "sndPlaySound // Chunk Found ckid=%.4s fccType=%.4s cksize=%08lX \n",
				(LPSTR)&mmckInfo.ckid, (LPSTR)&mmckInfo.fccType, mmckInfo.cksize);

		    if (mmioRead(hmmio, (HPSTR) &pcmWaveFormat,
			        (long) sizeof(PCMWAVEFORMAT)) == (long) sizeof(PCMWAVEFORMAT))
		    {

			dprintf_mmsys(stddeb, "sndPlaySound // wFormatTag=%04X !\n", pcmWaveFormat.wf.wFormatTag);
			dprintf_mmsys(stddeb, "sndPlaySound // nChannels=%d \n", pcmWaveFormat.wf.nChannels);
			dprintf_mmsys(stddeb, "sndPlaySound // nSamplesPerSec=%ld\n", pcmWaveFormat.wf.nSamplesPerSec);
			dprintf_mmsys(stddeb, "sndPlaySound // nAvgBytesPerSec=%ld\n", pcmWaveFormat.wf.nAvgBytesPerSec);
			dprintf_mmsys(stddeb, "sndPlaySound // nBlockAlign=%d \n", pcmWaveFormat.wf.nBlockAlign);
			dprintf_mmsys(stddeb, "sndPlaySound // wBitsPerSample=%u !\n", pcmWaveFormat.wBitsPerSample);

			mmckInfo.ckid = mmioFOURCC('d', 'a', 't', 'a');
			if (mmioDescend(hmmio, &mmckInfo, &ckMainRIFF, MMIO_FINDCHUNK) == 0)
			{
			    WAVEOPENDESC	waveDesc;
			    DWORD		dwRet;

			    dprintf_mmsys(stddeb, "sndPlaySound // Chunk Found \
 ckid=%.4s fccType=%.4s cksize=%08lX \n", (LPSTR)&mmckInfo.ckid, (LPSTR)&mmckInfo.fccType, mmckInfo.cksize);

			    pcmWaveFormat.wf.nAvgBytesPerSec = pcmWaveFormat.wf.nSamplesPerSec * 
							   pcmWaveFormat.wf.nBlockAlign;
			    waveDesc.hWave    = 0;
			    waveDesc.lpFormat = (LPWAVEFORMAT)&pcmWaveFormat;

			    dwRet = wodMessage( 0, WODM_OPEN, 0, (DWORD)&waveDesc, CALLBACK_NULL);
			    if (dwRet == MMSYSERR_NOERROR) 
			    {
				WAVEHDR		waveHdr;
				HGLOBAL16	hData;
				INT32		count, bufsize;

				bufsize = 64000;
				hData = GlobalAlloc16(GMEM_MOVEABLE, bufsize);
				waveHdr.lpData = (LPSTR)GlobalLock16(hData);
				waveHdr.dwBufferLength = bufsize;
				waveHdr.dwUser = 0L;
				waveHdr.dwFlags = 0L;
				waveHdr.dwLoops = 0L;

				dwRet = wodMessage(0,WODM_PREPARE,0,(DWORD)&waveHdr,sizeof(WAVEHDR));
				if (dwRet == MMSYSERR_NOERROR) 
				{
				    while( TRUE )
				    {
					count = mmioRead(hmmio, waveHdr.lpData, bufsize);
					if (count < 1) break;
					waveHdr.dwBufferLength = count;
				/*	waveHdr.dwBytesRecorded = count; */
					/* FIXME: doesn't expect async ops */ 
					wodMessage( 0, WODM_WRITE, 0, (DWORD)&waveHdr, sizeof(WAVEHDR));
				    }
				    wodMessage( 0, WODM_UNPREPARE, 0, (DWORD)&waveHdr, sizeof(WAVEHDR));
				    wodMessage( 0, WODM_CLOSE, 0, 0L, 0L);

				    bRet = TRUE;
				}
				else dprintf_mmsys(stddeb, "sndPlaySound // can't prepare WaveOut device !\n");

				GlobalUnlock16(hData);
				GlobalFree16(hData);
			    }
			}
		    }
		}
	    }
	}

	if (hmmio != 0) mmioClose(hmmio, 0);
	return bRet;
}

/**************************************************************************
 * 				mmsystemGetVersion	[WINMM.134]
 */
UINT32 WINAPI mmsystemGetVersion32()
{
	return mmsystemGetVersion16();
}

/**************************************************************************
 * 				mmsystemGetVersion	[MMSYSTEM.5]
 * return value borrowed from Win95 winmm.dll ;)
 */
UINT16 WINAPI mmsystemGetVersion16()
{
	dprintf_mmsys(stddeb, "mmsystemGetVersion // 3.10 (Win95?)\n");
	return 0x030a;
}

/**************************************************************************
* 				DriverProc	[MMSYSTEM.6]
*/
LRESULT WINAPI DriverProc(DWORD dwDevID, HDRVR16 hDriv, WORD wMsg, 
                          DWORD dwParam1, DWORD dwParam2)
{
	return DrvDefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
}

/**************************************************************************
* 				DriverCallback	[MMSYSTEM.31]
*/
BOOL16 WINAPI DriverCallback(DWORD dwCallBack, UINT16 uFlags, HANDLE16 hDev, 
                             WORD wMsg, DWORD dwUser, DWORD dwParam1, DWORD dwParam2)
{
	LPWAVEOPENDESC	lpDesc;

	dprintf_mmsys(stddeb, "DriverCallback(%08lX, %04X, %04X, %04X, %08lX, %08lX, %08lX); !\n",
		dwCallBack, uFlags, hDev, wMsg, dwUser, dwParam1, dwParam2);
	switch(uFlags & DCB_TYPEMASK) {
		case DCB_NULL:
			dprintf_mmsys(stddeb, "DriverCallback() // CALLBACK_NULL !\n");
			break;
		case DCB_WINDOW:
			dprintf_mmsys(stddeb, "DriverCallback() // CALLBACK_WINDOW = %04lX handle = %04X!\n",dwCallBack,hDev);
			if (!IsWindow32(dwCallBack)) return FALSE;
			lpDesc = (LPWAVEOPENDESC) USER_HEAP_LIN_ADDR(hDev);
			if (lpDesc == NULL) return FALSE;

			PostMessage16((HWND16)dwCallBack, wMsg, hDev, dwParam1);
			break;
		case DCB_TASK:
			dprintf_mmsys(stddeb, "DriverCallback() // CALLBACK_TASK !\n");
			return FALSE;
		case DCB_FUNCTION:
			dprintf_mmsys(stddeb, "DriverCallback() // CALLBACK_FUNCTION !\n");
			Callbacks->CallDriverCallback( (FARPROC16)dwCallBack,
                                                       hDev, wMsg, dwUser,
                                                       dwParam1, dwParam2 );
			break;
		}
	return TRUE;
}

/**************************************************************************
 * 	Mixer devices. New to Win95
 */
/**************************************************************************
 * find out the real mixer ID depending on hmix (depends on dwFlags)
 * FIXME: also fix dwInstance passing to mixMessage 
 */
static UINT32 _get_mixerID_from_handle(HMIXEROBJ32 hmix,DWORD dwFlags) {
	/* FIXME: Check dwFlags for MIXER_OBJECTF_xxxx entries and modify hmix 
	 * accordingly. For now we always use mixerdevice 0. 
	 */
	return 0;
}
/**************************************************************************
 * 				mixerGetNumDevs		[WINMM.108]
 */
UINT32 WINAPI mixerGetNumDevs32() 
{
	return mixerGetNumDevs16();
}

/**************************************************************************
 * 				mixerGetNumDevs	
 */
UINT16 WINAPI mixerGetNumDevs16() 
{
	UINT16	count;

	count = mixMessage(0,MXDM_GETNUMDEVS,0L,0L,0L);
	dprintf_mmaux(stddeb,"mixerGetNumDevs returns %d\n",count);
	return count;
}

/**************************************************************************
 * 				mixerGetDevCapsW		[WINMM.102]
 */
UINT32 WINAPI mixerGetDevCaps32W(UINT32 devid,LPMIXERCAPS32W mixcaps,UINT32 size) 
{
	MIXERCAPS16	mic16;
	UINT32	ret = mixerGetDevCaps16(devid,&mic16,sizeof(mic16));

	mixcaps->wMid = mic16.wMid;
	mixcaps->wPid = mic16.wPid;
	mixcaps->vDriverVersion = mic16.vDriverVersion;
	lstrcpyAtoW(mixcaps->szPname,mic16.szPname);
	mixcaps->fdwSupport = mic16.fdwSupport;
	mixcaps->cDestinations = mic16.cDestinations;
	return ret;
}
/**************************************************************************
 * 				mixerGetDevCaps		[WINMM.101]
 */
UINT32 WINAPI mixerGetDevCaps32A(UINT32 devid,LPMIXERCAPS32A mixcaps,UINT32 size) 
{
	MIXERCAPS16	mic16;
	UINT32	ret = mixerGetDevCaps16(devid,&mic16,sizeof(mic16));

	mixcaps->wMid = mic16.wMid;
	mixcaps->wPid = mic16.wPid;
	mixcaps->vDriverVersion = mic16.vDriverVersion;
	strcpy(mixcaps->szPname,mic16.szPname);
	mixcaps->fdwSupport = mic16.fdwSupport;
	mixcaps->cDestinations = mic16.cDestinations;
	return ret;
}

/**************************************************************************
 * 				mixerGetDevCaps	
 */
UINT16 WINAPI mixerGetDevCaps16(UINT16 devid,LPMIXERCAPS16 mixcaps,UINT16 size) 
{
	fprintf(stderr,"mixerGetDevCaps!\n");
	return mixMessage(devid,MXDM_GETDEVCAPS,0L,(DWORD)mixcaps,(DWORD)size);
}

/**************************************************************************
 * 				mixerOpen		[WINMM.110]
 */
UINT32 WINAPI mixerOpen32(LPHMIXER32 lphmix,UINT32 uDeviceID,DWORD dwCallback,
DWORD dwInstance,DWORD fdwOpen) 
{
	HMIXER16	hmix16;
	UINT32		ret;

	fprintf(stderr,"mixerOpen32(%p,%d,%08lx,%08lx,%08lx)\n",
		lphmix,uDeviceID,dwCallback,dwInstance,fdwOpen
	);
	ret = mixerOpen16(&hmix16,uDeviceID,dwCallback,dwInstance,fdwOpen);
	if (lphmix) *lphmix = hmix16;
	return ret;
}

/**************************************************************************
 * 				mixerOpen
 */
UINT16 WINAPI mixerOpen16(LPHMIXER16 lphmix,UINT16 uDeviceID,DWORD dwCallback,
DWORD dwInstance,DWORD fdwOpen) 
{
	HMIXER16	hmix;
	LPMIXEROPENDESC	lpmod;
	BOOL32		mapperflag = (uDeviceID==0);
	DWORD		dwRet;

	fprintf(stderr,"mixerOpen(%p,%d,%08lx,%08lx,%08lx)\n",
		lphmix,uDeviceID,dwCallback,dwInstance,fdwOpen
	);
	hmix = USER_HEAP_ALLOC(sizeof(MIXEROPENDESC));
	if (lphmix) *lphmix = hmix;
	lpmod = (LPMIXEROPENDESC)USER_HEAP_LIN_ADDR(hmix);
	lpmod->hmx = hmix;
	lpmod->dwCallback = dwCallback;
	lpmod->dwInstance = dwInstance;
	if (uDeviceID >= MAXMIXERDRIVERS)
		uDeviceID = 0;
	while(uDeviceID < MAXMIXERDRIVERS) {
		dwRet=mixMessage(uDeviceID,MXDM_OPEN,dwInstance,(DWORD)lpmod,fdwOpen);
		if (dwRet == MMSYSERR_NOERROR) break;
		if (!mapperflag) break;
		uDeviceID++;
	}
	lpmod->uDeviceID = uDeviceID;
	return dwRet;
}

/**************************************************************************
 * 				mixerClose		[WINMM.98]
 */
UINT32 WINAPI mixerClose32(HMIXER32 hmix) {
	return mixerClose16(hmix);
}

/**************************************************************************
 * 				mixerClose
 */
UINT16 WINAPI mixerClose16(HMIXER16 hmix) {
	LPMIXEROPENDESC	lpmod;

	fprintf(stderr,"mixerClose(%04x)\n",hmix);
	lpmod = (LPMIXEROPENDESC)USER_HEAP_LIN_ADDR(hmix);
	return mixMessage(lpmod->uDeviceID,MXDM_CLOSE,lpmod->dwInstance,0L,0L);
}

/**************************************************************************
 * 				mixerGetID		[WINMM.103]
 */
UINT32 WINAPI mixerGetID32(HMIXEROBJ32 hmix,LPUINT32 lpid,DWORD fdwID) {
	UINT16	xid;

	UINT32	ret = mixerGetID16(hmix,&xid,fdwID);
	if (*lpid) *lpid = xid;
	return ret;
}

/**************************************************************************
 * 				mixerGetID
 */
UINT16 WINAPI mixerGetID16(HMIXEROBJ16 hmix,LPUINT16 lpid,DWORD fdwID) {
	fprintf(stderr,"mixerGetID(%04x)\n",hmix);
	return _get_mixerID_from_handle(hmix,fdwID);
}

/**************************************************************************
 * 				mixerGetControlDetailsA	[WINMM.99]
 */
UINT32 mixerGetControlDetails32A(HMIXEROBJ32 hmix,LPMIXERCONTROLDETAILS32 lpmcd,DWORD fdwDetails) {
	fprintf(stderr,"mixerGetControlDetails(%04x,%p,%08lx),stub!\n",
		hmix,lpmcd,fdwDetails
	);
	return MMSYSERR_NOTENABLED;
}

/**************************************************************************
 * 				mixerGetControlDetailsW	[WINMM.100]
 */
UINT32 mixerGetControlDetails32W(HMIXEROBJ32 hmix,LPMIXERCONTROLDETAILS32 lpmcd,DWORD fdwDetails) {
	fprintf(stderr,"mixerGetControlDetails(%04x,%p,%08lx),stub!\n",
		hmix,lpmcd,fdwDetails
	);
	return MMSYSERR_NOTENABLED;
}

/**************************************************************************
 * 				mixerGetControlDetails	[MMSYSTEM.808]
 */
UINT16 mixerGetControlDetails16(HMIXEROBJ16 hmix,LPMIXERCONTROLDETAILS16 lpmcd,DWORD fdwDetails) {
	fprintf(stderr,"mixerGetControlDetails(%04x,%p,%08lx),stub!\n",
		hmix,lpmcd,fdwDetails
	);
	return MMSYSERR_NOTENABLED;
}

/**************************************************************************
 * 				mixerGetLineControlsA	[WINMM.104]
 */
UINT32 WINAPI mixerGetLineControls32A(HMIXEROBJ32 hmix,LPMIXERLINECONTROLS32A lpmlc,DWORD fdwControls) {
	fprintf(stderr,"mixerGetLineControlsA(%04x,%p,%08lx),stub!\n",
		hmix,lpmlc,fdwControls
	);
	return MMSYSERR_NOTENABLED;
}

/**************************************************************************
 * 				mixerGetLineControlsW	[WINMM.105]
 */
UINT32 WINAPI mixerGetLineControls32W(HMIXEROBJ32 hmix,LPMIXERLINECONTROLS32W lpmlc,DWORD fdwControls) {
	fprintf(stderr,"mixerGetLineControlsA(%04x,%p,%08lx),stub!\n",
		hmix,lpmlc,fdwControls
	);
	return MMSYSERR_NOTENABLED;
}

/**************************************************************************
 * 				mixerGetLineControls	[MMSYSTEM.807]
 */
UINT16 WINAPI mixerGetLineControls16(HMIXEROBJ16 hmix,LPMIXERLINECONTROLS16 lpmlc,DWORD fdwControls) {
	fprintf(stderr,"mixerGetLineControls(%04x,%p,%08lx),stub!\n",
		hmix,lpmlc,fdwControls
	);
	return MMSYSERR_NOTENABLED;
}

/**************************************************************************
 * 				mixerGetLineInfoA	[WINMM.106]
 */
UINT32 WINAPI mixerGetLineInfo32A(HMIXEROBJ32 hmix,LPMIXERLINE32A lpml,DWORD fdwInfo) {
	MIXERLINE16	ml16;
	UINT32		ret;

	ml16.dwDestination = lpml->dwDestination;
	fprintf(stderr,"mixerGetLineInfoA(%04x,%p,%08lx),stub!\n",
		hmix,lpml,fdwInfo
	);
	ret = mixerGetLineInfo16(hmix,&ml16,fdwInfo);
	lpml->cbStruct = sizeof(*lpml);
	lpml->dwSource = ml16.dwSource;
	lpml->dwLineID = ml16.dwLineID;
	lpml->fdwLine = ml16.fdwLine;
	lpml->dwUser = ml16.dwUser;
	lpml->dwComponentType = ml16.dwComponentType;
	lpml->cChannels = ml16.cChannels;
	lpml->cConnections = ml16.cConnections;
	lpml->cControls = ml16.cControls;
	strcpy(lpml->szShortName,ml16.szShortName);
	strcpy(lpml->szName,ml16.szName);
	lpml->Target.dwType = ml16.Target.dwType;
	lpml->Target.dwDeviceID = ml16.Target.dwDeviceID;
	lpml->Target.wMid = ml16.Target.wMid;
	lpml->Target.wPid = ml16.Target.wPid;
	lpml->Target.vDriverVersion = ml16.Target.vDriverVersion;
	strcpy(lpml->Target.szPname,ml16.Target.szPname);
	return ret;
}

/**************************************************************************
 * 				mixerGetLineInfoW	[WINMM.107]
 */
UINT32 WINAPI mixerGetLineInfo32W(HMIXEROBJ32 hmix,LPMIXERLINE32W lpml,DWORD fdwInfo) {
	MIXERLINE16	ml16;
	UINT32		ret;

	ml16.dwDestination = lpml->dwDestination;
	fprintf(stderr,"mixerGetLineInfoW(%04x,%p,%08lx),stub!\n",
		hmix,lpml,fdwInfo
	);
	ret = mixerGetLineInfo16(hmix,&ml16,fdwInfo);
	lpml->cbStruct = sizeof(*lpml);
	lpml->dwSource = ml16.dwSource;
	lpml->dwLineID = ml16.dwLineID;
	lpml->fdwLine = ml16.fdwLine;
	lpml->dwUser = ml16.dwUser;
	lpml->dwComponentType = ml16.dwComponentType;
	lpml->cChannels = ml16.cChannels;
	lpml->cConnections = ml16.cConnections;
	lpml->cControls = ml16.cControls;
	lstrcpyAtoW(lpml->szShortName,ml16.szShortName);
	lstrcpyAtoW(lpml->szName,ml16.szName);
	lpml->Target.dwType = ml16.Target.dwType;
	lpml->Target.dwDeviceID = ml16.Target.dwDeviceID;
	lpml->Target.wMid = ml16.Target.wMid;
	lpml->Target.wPid = ml16.Target.wPid;
	lpml->Target.vDriverVersion = ml16.Target.vDriverVersion;
	/*lstrcpyAtoW(lpml->Target.szPname,ml16.Target.szPname);*/
	return ret;
}

/**************************************************************************
 * 				mixerGetLineInfo	[MMSYSTEM.805]
 */
UINT16 WINAPI mixerGetLineInfo16(HMIXEROBJ16 hmix,LPMIXERLINE16 lpml,DWORD fdwInfo) {
	UINT16 devid =  _get_mixerID_from_handle(hmix,fdwInfo);

	fprintf(stderr,"mixerGetLineInfo16(%04x,%p[line %08lx],%08lx)\n",
		hmix,lpml,lpml->dwDestination,fdwInfo
	);
	return mixMessage(devid,MXDM_GETLINEINFO,0,(DWORD)lpml,fdwInfo);
}

/**************************************************************************
 * 				mixerSetControlDetails	[WINMM.111]
 */
UINT32 WINAPI mixerSetControlDetails32(HMIXEROBJ32 hmix,LPMIXERCONTROLDETAILS32 lpmcd,DWORD fdwDetails) {
	fprintf(stderr,"mixerSetControlDetails32(%04x,%p,%08lx),stub!\n",
		hmix,lpmcd,fdwDetails
	);
	return MMSYSERR_NOTENABLED;
}

/**************************************************************************
 * 				mixerSetControlDetails	[MMSYSTEM.809]
 */
UINT16 WINAPI mixerSetControlDetails16(HMIXEROBJ16 hmix,LPMIXERCONTROLDETAILS16 lpmcd,DWORD fdwDetails) {
	fprintf(stderr,"mixerSetControlDetails16(%04x,%p,%08lx),stub!\n",
		hmix,lpmcd,fdwDetails
	);
	return MMSYSERR_NOTENABLED;
}

/**************************************************************************
 * 				mixerMessage		[WINMM.109]
 */
UINT32 WINAPI mixerMessage32(HMIXER32 hmix,UINT32 uMsg,DWORD dwParam1,DWORD dwParam2) {
	LPMIXEROPENDESC	lpmod;
	UINT16	uDeviceID;

	lpmod = (LPMIXEROPENDESC)USER_HEAP_LIN_ADDR(hmix);
	if (lpmod)
		uDeviceID = lpmod->uDeviceID;
	else
		uDeviceID = 0;
	fprintf(stderr,"mixerMessage(%04lx,%d,%08lx,%08lx)\n",(DWORD)hmix,uMsg,dwParam1,dwParam2);
	return mixMessage(uDeviceID,uMsg,0L,dwParam1,dwParam2);
}

/**************************************************************************
 * 				mixerMessage		[MMSYSTEM.804]
 */
UINT16 WINAPI mixerMessage16(HMIXER16 hmix,UINT16 uMsg,DWORD dwParam1,DWORD dwParam2) {
	LPMIXEROPENDESC	lpmod;
	UINT16	uDeviceID;

	lpmod = (LPMIXEROPENDESC)USER_HEAP_LIN_ADDR(hmix);
	if (lpmod)
		uDeviceID = lpmod->uDeviceID;
	else
		uDeviceID = 0;
	fprintf(stderr,"mixerMessage(%04x,%d,%08lx,%08lx)\n",hmix,uMsg,dwParam1,dwParam2);
	return mixMessage(uDeviceID,uMsg,0L,dwParam1,dwParam2);
}

/**************************************************************************
 * 				auxGetNumDevs		[WINMM.22]
 */
UINT32 WINAPI auxGetNumDevs32()
{
	return auxGetNumDevs16();
}

/**************************************************************************
 * 				auxGetNumDevs		[MMSYSTEM.350]
 */
UINT16 WINAPI auxGetNumDevs16()
{
	UINT16	count = 0;
	dprintf_mmsys(stddeb, "auxGetNumDevs !\n");
	count += auxMessage(0, AUXDM_GETNUMDEVS, 0L, 0L, 0L);
	dprintf_mmsys(stddeb, "auxGetNumDevs return %u \n", count);
	return count;
}

/**************************************************************************
 * 				auxGetDevCaps		[WINMM.20]
 */
UINT32 WINAPI auxGetDevCaps32W(UINT32 uDeviceID,LPAUXCAPS32W lpCaps,UINT32 uSize)
{
	AUXCAPS16	ac16;
	UINT32	ret = auxGetDevCaps16(uDeviceID,&ac16,sizeof(ac16));

	lpCaps->wMid = ac16.wMid;
	lpCaps->wPid = ac16.wPid;
	lpCaps->vDriverVersion = ac16.vDriverVersion;
	lstrcpyAtoW(lpCaps->szPname,ac16.szPname);
	lpCaps->wTechnology = ac16.wTechnology;
	lpCaps->dwSupport = ac16.dwSupport;
	return ret;
}

/**************************************************************************
 * 				auxGetDevCaps		[WINMM.21]
 */
UINT32 WINAPI auxGetDevCaps32A(UINT32 uDeviceID,LPAUXCAPS32A lpCaps,UINT32 uSize)
{
	AUXCAPS16	ac16;
	UINT32	ret = auxGetDevCaps16(uDeviceID,&ac16,sizeof(ac16));

	lpCaps->wMid = ac16.wMid;
	lpCaps->wPid = ac16.wPid;
	lpCaps->vDriverVersion = ac16.vDriverVersion;
	strcpy(lpCaps->szPname,ac16.szPname);
	lpCaps->wTechnology = ac16.wTechnology;
	lpCaps->dwSupport = ac16.dwSupport;
	return ret;
}

/**************************************************************************
 * 				auxGetDevCaps		[MMSYSTEM.351]
 */
UINT16 WINAPI auxGetDevCaps16(UINT16 uDeviceID,LPAUXCAPS16 lpCaps, UINT16 uSize)
{
	dprintf_mmsys(stddeb, "auxGetDevCaps(%04X, %p, %d) !\n", 
					uDeviceID, lpCaps, uSize);
	return auxMessage(uDeviceID, AUXDM_GETDEVCAPS,
				0L, (DWORD)lpCaps, (DWORD)uSize);
}

/**************************************************************************
 * 				auxGetVolume		[WINM.23]
 */
UINT32 WINAPI auxGetVolume32(UINT32 uDeviceID, DWORD * lpdwVolume)
{
	return auxGetVolume16(uDeviceID,lpdwVolume);
}

/**************************************************************************
 * 				auxGetVolume		[MMSYSTEM.352]
 */
UINT16 WINAPI auxGetVolume16(UINT16 uDeviceID, DWORD * lpdwVolume)
{
	dprintf_mmsys(stddeb, "auxGetVolume(%04X, %p) !\n", uDeviceID, lpdwVolume);
	return auxMessage(uDeviceID, AUXDM_GETVOLUME, 0L, (DWORD)lpdwVolume, 0L);
}

/**************************************************************************
 * 				auxSetVolume		[WINMM.25]
 */
UINT32 WINAPI auxSetVolume32(UINT32 uDeviceID, DWORD dwVolume)
{
	return auxSetVolume16(uDeviceID,dwVolume);
}

/**************************************************************************
 * 				auxSetVolume		[MMSYSTEM.353]
 */
UINT16 WINAPI auxSetVolume16(UINT16 uDeviceID, DWORD dwVolume)
{
	dprintf_mmsys(stddeb, "auxSetVolume(%04X, %08lX) !\n", uDeviceID, dwVolume);
	return auxMessage(uDeviceID, AUXDM_SETVOLUME, 0L, dwVolume, 0L);
}

/**************************************************************************
 * 				auxOutMessage		[MMSYSTEM.354]
 */
DWORD WINAPI auxOutMessage32(UINT32 uDeviceID,UINT32 uMessage,DWORD dw1,DWORD dw2)
{
	switch (uMessage) {
	case AUXDM_GETNUMDEVS:
	case AUXDM_GETVOLUME:
	case AUXDM_SETVOLUME:
		/* no argument conversion needed */
		break;
	case AUXDM_GETDEVCAPS:
		return auxGetDevCaps32A(uDeviceID,(LPAUXCAPS32A)dw1,dw2);
	default:
		fprintf(stderr,"unhandled auxMessage32(%04x,%04x,%08lx,%08lx)\n",
			uDeviceID,uMessage,dw1,dw2
		);
		break;
	}
	return auxMessage(uDeviceID,uMessage,0L,dw1,dw2);
}

/**************************************************************************
 * 				auxOutMessage		[MMSYSTEM.354]
 */
DWORD WINAPI auxOutMessage16(UINT16 uDeviceID, UINT16 uMessage, DWORD dw1, DWORD dw2)
{
	dprintf_mmsys(stddeb, "auxOutMessage(%04X, %04X, %08lX, %08lX)\n", 
				uDeviceID, uMessage, dw1, dw2);
	switch (uMessage) {
	case AUXDM_GETNUMDEVS:
	case AUXDM_SETVOLUME:
		/* no argument conversion needed */
		break;
	case AUXDM_GETVOLUME:
		return auxGetVolume16(uDeviceID,(LPDWORD)PTR_SEG_TO_LIN(dw1));
	case AUXDM_GETDEVCAPS:
		return auxGetDevCaps16(uDeviceID,(LPAUXCAPS16)PTR_SEG_TO_LIN(dw1),dw2);
	default:
		fprintf(stderr,"unhandled auxMessage32(%04x,%04x,%08lx,%08lx)\n",
			uDeviceID,uMessage,dw1,dw2
		);
		break;
	}
	return auxMessage(uDeviceID, uMessage, 0L, dw1, dw2);
}

/**************************************************************************
 * 				mciGetErrorStringW		[WINMM.46]
 */
BOOL32 WINAPI mciGetErrorString32W(DWORD wError,LPWSTR lpstrBuffer,UINT32 uLength)
{
	LPSTR	bufstr = HeapAlloc(GetProcessHeap(),0,uLength);
	BOOL32	ret = mciGetErrorString32A(wError,bufstr,uLength);

	lstrcpyAtoW(lpstrBuffer,bufstr);
	HeapFree(GetProcessHeap(),0,bufstr);
	return ret;
}

/**************************************************************************
 * 				mciGetErrorStringA		[WINMM.45]
 */
BOOL32 WINAPI mciGetErrorString32A(DWORD wError,LPSTR lpstrBuffer,UINT32 uLength)
{
	return mciGetErrorString16(wError,lpstrBuffer,uLength);
}

/**************************************************************************
 * 				mciGetErrorString		[MMSYSTEM.706]
 */
BOOL16 WINAPI mciGetErrorString16(DWORD wError,LPSTR lpstrBuffer,UINT16 uLength)
{
	LPSTR	msgptr;
	dprintf_mmsys(stddeb, "mciGetErrorString(%08lX, %p, %d);\n", wError, lpstrBuffer, uLength);
	if ((lpstrBuffer == NULL) || (uLength < 1)) return(FALSE);
	lpstrBuffer[0] = '\0';
	switch(wError) {
		case MCIERR_INVALID_DEVICE_ID:
			msgptr = "Invalid MCI device ID. Use the ID returned when opening the MCI device.";
			break;
		case MCIERR_UNRECOGNIZED_KEYWORD:
			msgptr = "The driver cannot recognize the specified command parameter.";
			break;
		case MCIERR_UNRECOGNIZED_COMMAND:
			msgptr = "The driver cannot recognize the specified command.";
			break;
		case MCIERR_HARDWARE:
			msgptr = "There is a problem with your media device. Make sure it is working correctly or contact the device manufacturer.";
			break;
		case MCIERR_INVALID_DEVICE_NAME:
			msgptr = "The specified device is not open or is not recognized by MCI.";
			break;
		case MCIERR_OUT_OF_MEMORY:
			msgptr = "Not enough memory available for this task. \nQuit one or more applications to increase available memory, and then try again.";
			break;
		case MCIERR_DEVICE_OPEN:
			msgptr = "The device name is already being used as an alias by this application. Use a unique alias.";
			break;
		case MCIERR_CANNOT_LOAD_DRIVER:
			msgptr = "There is an undetectable problem in loading the specified device driver.";
			break;
		case MCIERR_MISSING_COMMAND_STRING:
			msgptr = "No command was specified.";
			break;
		case MCIERR_PARAM_OVERFLOW:
			msgptr = "The output string was to large to fit in the return buffer. Increase the size of the buffer.";
			break;
		case MCIERR_MISSING_STRING_ARGUMENT:
			msgptr = "The specified command requires a character-string parameter. Please provide one.";
			break;
		case MCIERR_BAD_INTEGER:
			msgptr = "The specified integer is invalid for this command.";
			break;
		case MCIERR_PARSER_INTERNAL:
			msgptr = "The device driver returned an invalid return type. Check with the device manufacturer about obtaining a new driver.";
			break;
		case MCIERR_DRIVER_INTERNAL:
			msgptr = "There is a problem with the device driver. Check with the device manufacturer about obtaining a new driver.";
			break;
		case MCIERR_MISSING_PARAMETER:
			msgptr = "The specified command requires a parameter. Please supply one.";
			break;
		case MCIERR_UNSUPPORTED_FUNCTION:
			msgptr = "The MCI device you are using does not support the specified command.";
			break;
		case MCIERR_FILE_NOT_FOUND:
			msgptr = "Cannot find the specified file. Make sure the path and filename are correct.";
			break;
		case MCIERR_DEVICE_NOT_READY:
			msgptr = "The device driver is not ready.";
			break;
		case MCIERR_INTERNAL:
			msgptr = "A problem occurred in initializing MCI. Try restarting Windows.";
			break;
		case MCIERR_DRIVER:
			msgptr = "There is a problem with the device driver. The driver has closed. Cannot access error.";
			break;
		case MCIERR_CANNOT_USE_ALL:
			msgptr = "Cannot use 'all' as the device name with the specified command.";
			break;
		case MCIERR_MULTIPLE:
			msgptr = "Errors occurred in more than one device. Specify each command and device separately to determine which devices caused the error";
			break;
		case MCIERR_EXTENSION_NOT_FOUND:
			msgptr = "Cannot determine the device type from the given filename extension.";
			break;
		case MCIERR_OUTOFRANGE:
			msgptr = "The specified parameter is out of range for the specified command.";
			break;
		case MCIERR_FLAGS_NOT_COMPATIBLE:
			msgptr = "The specified parameters cannot be used together.";
			break;
		case MCIERR_FILE_NOT_SAVED:
			msgptr = "Cannot save the specified file. Make sure you have enough disk space or are still connected to the network.";
			break;
		case MCIERR_DEVICE_TYPE_REQUIRED:
			msgptr = "Cannot find the specified device. Make sure it is installed or that the device name is spelled correctly.";
			break;
		case MCIERR_DEVICE_LOCKED:
			msgptr = "The specified device is now being closed. Wait a few seconds, and then try again.";
			break;
		case MCIERR_DUPLICATE_ALIAS:
			msgptr = "The specified alias is already being used in this application. Use a unique alias.";
			break;
		case MCIERR_BAD_CONSTANT:
			msgptr = "The specified parameter is invalid for this command.";
			break;
		case MCIERR_MUST_USE_SHAREABLE:
			msgptr = "The device driver is already in use. To share it, use the 'shareable' parameter with each 'open' command.";
			break;
		case MCIERR_MISSING_DEVICE_NAME:
			msgptr = "The specified command requires an alias, file, driver, or device name. Please supply one.";
			break;
		case MCIERR_BAD_TIME_FORMAT:
			msgptr = "The specified value for the time format is invalid. Refer to the MCI documentation for valid formats.";
			break;
		case MCIERR_NO_CLOSING_QUOTE:
			msgptr = "A closing double-quotation mark is missing from the parameter value. Please supply one.";
			break;
		case MCIERR_DUPLICATE_FLAGS:
			msgptr = "A parameter or value was specified twice. Only specify it once.";
			break;
		case MCIERR_INVALID_FILE:
			msgptr = "The specified file cannot be played on the specified MCI device. The file may be corrupt, or not in the correct format.";
			break;
		case MCIERR_NULL_PARAMETER_BLOCK:
			msgptr = "A null parameter block was passed to MCI.";
			break;
		case MCIERR_UNNAMED_RESOURCE:
			msgptr = "Cannot save an unnamed file. Supply a filename.";
			break;
		case MCIERR_NEW_REQUIRES_ALIAS:
			msgptr = "You must specify an alias when using the 'new' parameter.";
			break;
		case MCIERR_NOTIFY_ON_AUTO_OPEN:
			msgptr = "Cannot use the 'notify' flag with auto-opened devices.";
			break;
		case MCIERR_NO_ELEMENT_ALLOWED:
			msgptr = "Cannot use a filename with the specified device.";
			break;
		case MCIERR_NONAPPLICABLE_FUNCTION:
			msgptr = "Cannot carry out the commands in the order specified. Correct the command sequence, and then try again.";
			break;
		case MCIERR_ILLEGAL_FOR_AUTO_OPEN:
			msgptr = "Cannot carry out the specified command on an auto-opened device. Wait until the device is closed, and then try again.";
			break;
		case MCIERR_FILENAME_REQUIRED:
			msgptr = "The filename is invalid. Make sure the filename is not longer than 8 characters, followed by a period and an extension.";
			break;
		case MCIERR_EXTRA_CHARACTERS:
			msgptr = "Cannot specify extra characters after a string enclosed in quotation marks.";
			break;
		case MCIERR_DEVICE_NOT_INSTALLED:
			msgptr = "The specified device is not installed on the system. Use the Drivers option in Control Panel to install the device.";
			break;
		case MCIERR_GET_CD:
			msgptr = "Cannot access the specified file or MCI device. Try changing directories or restarting your computer.";
			break;
		case MCIERR_SET_CD:
			msgptr = "Cannot access the specified file or MCI device because the application cannot change directories.";
			break;
		case MCIERR_SET_DRIVE:
			msgptr = "Cannot access specified file or MCI device because the application cannot change drives.";
			break;
		case MCIERR_DEVICE_LENGTH:
			msgptr = "Specify a device or driver name that is less than 79 characters.";
			break;
		case MCIERR_DEVICE_ORD_LENGTH:
			msgptr = "Specify a device or driver name that is less than 69 characters.";
			break;
		case MCIERR_NO_INTEGER:
			msgptr = "The specified command requires an integer parameter. Please provide one.";
			break;
		case MCIERR_WAVE_OUTPUTSINUSE:
			msgptr = "All wave devices that can play files in the current format are in use. Wait until a wave device is free, and then try again.";
			break;
		case MCIERR_WAVE_SETOUTPUTINUSE:
			msgptr = "Cannot set the current wave device for play back because it is in use. Wait until the device is free, and then try again.";
			break;
		case MCIERR_WAVE_INPUTSINUSE:
			msgptr = "All wave devices that can record files in the current format are in use. Wait until a wave device is free, and then try again.";
			break;
		case MCIERR_WAVE_SETINPUTINUSE:
			msgptr = "Cannot set the current wave device for recording because it is in use. Wait until the device is free, and then try again.";
			break;
		case MCIERR_WAVE_OUTPUTUNSPECIFIED:
			msgptr = "Any compatible waveform playback device may be used.";
			break;
		case MCIERR_WAVE_INPUTUNSPECIFIED:
			msgptr = "Any compatible waveform recording device may be used.";
			break;
		case MCIERR_WAVE_OUTPUTSUNSUITABLE:
			msgptr = "No wave device that can play files in the current format is installed. Use the Drivers option to install the wave device.";
			break;
		case MCIERR_WAVE_SETOUTPUTUNSUITABLE:
			msgptr = "The device you are trying to play to cannot recognize the current file format.";
			break;
		case MCIERR_WAVE_INPUTSUNSUITABLE:
			msgptr = "No wave device that can record files in the current format is installed. Use the Drivers option to install the wave device.";
			break;
		case MCIERR_WAVE_SETINPUTUNSUITABLE:
			msgptr = "The device you are trying to record from cannot recognize the current file format.";
			break;
		case MCIERR_NO_WINDOW:
			msgptr = "There is no display window.";
			break;
		case MCIERR_CREATEWINDOW:
			msgptr = "Could not create or use window.";
			break;
		case MCIERR_FILE_READ:
			msgptr = "Cannot read the specified file. Make sure the file is still present, or check your disk or network connection.";
			break;
		case MCIERR_FILE_WRITE:
			msgptr = "Cannot write to the specified file. Make sure you have enough disk space or are still connected to the network.";
			break;
		case MCIERR_SEQ_DIV_INCOMPATIBLE:
			msgptr = "The time formats of the \"song pointer\" and SMPTE are mutually exclusive. You can't use them together.";
			break;
		case MCIERR_SEQ_NOMIDIPRESENT:
			msgptr = "The system has no installed MIDI devices. Use the Drivers option from the Control Panel to install a MIDI driver.";
			break;
		case MCIERR_SEQ_PORT_INUSE:
			msgptr = "The specified MIDI port is already in use. Wait until it is free; the try again.";
			break;
		case MCIERR_SEQ_PORT_MAPNODEVICE:
			msgptr = "The current MIDI Mapper setup refers to a MIDI device that is not installed on the system. Use the MIDI Mapper option from the Control Panel to edit the setup.";
			break;
		case MCIERR_SEQ_PORT_MISCERROR:
			msgptr = "An error occurred with the specified port.";
			break;
		case MCIERR_SEQ_PORT_NONEXISTENT:
			msgptr = "The specified MIDI device is not installed on the system. Use the Drivers option from the Control Panel to install a MIDI device.";
			break;
		case MCIERR_SEQ_PORTUNSPECIFIED:
			msgptr = "The system doesnot have a current MIDI port specified.";
			break;
		case MCIERR_SEQ_TIMER:
			msgptr = "All multimedia timers are being used by other applications. Quit one of these applications; then, try again.";
			break;

/* 
msg# 513 : vcr
msg# 514 : videodisc
msg# 515 : overlay
msg# 516 : cdaudio
msg# 517 : dat
msg# 518 : scanner
msg# 519 : animation
msg# 520 : digitalvideo
msg# 521 : other
msg# 522 : waveaudio
msg# 523 : sequencer
msg# 524 : not ready
msg# 525 : stopped
msg# 526 : playing
msg# 527 : recording
msg# 528 : seeking
msg# 529 : paused
msg# 530 : open
msg# 531 : false
msg# 532 : true
msg# 533 : milliseconds
msg# 534 : hms
msg# 535 : msf
msg# 536 : frames
msg# 537 : smpte 24
msg# 538 : smpte 25
msg# 539 : smpte 30
msg# 540 : smpte 30 drop
msg# 541 : bytes
msg# 542 : samples
msg# 543 : tmsf
*/
		default:
			msgptr = "Unknown MCI Error !\n";
			break;
	}
        lstrcpyn32A(lpstrBuffer, msgptr, uLength);
	dprintf_mmsys(stddeb, "mciGetErrorString // msg = %s;\n", msgptr);
	return TRUE;
}


/**************************************************************************
 * 				mciDriverNotify			[MMSYSTEM.711]
 */
BOOL16 WINAPI mciDriverNotify(HWND16 hWndCallBack, UINT16 wDevID, UINT16 wStatus)
{
	dprintf_mmsys(stddeb, "mciDriverNotify(%04X, %u, %04X)\n", hWndCallBack, wDevID, wStatus);
	if (!IsWindow32(hWndCallBack)) return FALSE;
	dprintf_mmsys(stddeb, "mciDriverNotify // before PostMessage\n");
	PostMessage16( hWndCallBack, MM_MCINOTIFY, wStatus, 
                       MAKELONG(wDevID, 0));
	return TRUE;
}

/**************************************************************************
 * 			mciOpen					[internal]
 */

DWORD mciOpen(DWORD dwParam, LPMCI_OPEN_PARMS16 lp16Parms)
{
	char	str[128];
	LPMCI_OPEN_PARMS16 lpParms;
	UINT16	uDevTyp = 0;
	UINT16	wDevID = MMSYSTEM_FirstDevID();
	DWORD dwret;

	lpParms = PTR_SEG_TO_LIN(lp16Parms);
	dprintf_mmsys(stddeb, "mciOpen(%08lX, %p (%p))\n", dwParam, lp16Parms, lpParms);
	if (lp16Parms == NULL) return MCIERR_INTERNAL;

	while(GetDrv(wDevID)->modp.wType != 0) {
		wDevID = MMSYSTEM_NextDevID(wDevID);
		if (!MMSYSTEM_DevIDValid(wDevID)) {
			dprintf_mmsys(stddeb, "MCI_OPEN // MAXMCIDRIVERS reached !\n");
			return MCIERR_INTERNAL;
		}
	}
	dprintf_mmsys(stddeb, "mciOpen // wDevID=%04X \n", wDevID);
	memcpy(GetOpenDrv(wDevID),lpParms,sizeof(*lpParms));

	if (dwParam & MCI_OPEN_ELEMENT) {
		char	*s,*t;

		dprintf_mmsys(stddeb,"mciOpen // lpstrElementName='%s'\n",
			(char*)PTR_SEG_TO_LIN(lpParms->lpstrElementName)
		);
		s=(char*)PTR_SEG_TO_LIN(lpParms->lpstrElementName);
		t=strrchr(s,'.');
		if (t) {
			GetProfileString32A("mci extensions",t+1,"*",str,sizeof(str));
			CharUpper32A(str);
			dprintf_mmsys(stddeb, "mciOpen // str = %s \n", str);
			if (strcmp(str, "CDAUDIO") == 0) {
				uDevTyp = MCI_DEVTYPE_CD_AUDIO;
			} else
			if (strcmp(str, "WAVEAUDIO") == 0) {
				uDevTyp = MCI_DEVTYPE_WAVEFORM_AUDIO;
			} else
			if (strcmp(str, "SEQUENCER") == 0)	{
				uDevTyp = MCI_DEVTYPE_SEQUENCER;
			} else
			if (strcmp(str, "ANIMATION1") == 0) {
				uDevTyp = MCI_DEVTYPE_ANIMATION;
			} else
			if (strcmp(str, "AVIVIDEO") == 0) {
				uDevTyp = MCI_DEVTYPE_DIGITAL_VIDEO;
			} else 
			if (strcmp(str,"*") == 0) {
				dprintf_mmsys(stddeb,"No [mci extensions] entry for %s found.\n",t);
				return MCIERR_EXTENSION_NOT_FOUND;
#if testing16
			} else  {
				HDRVR16 hdrv = OpenDriver(str,"mci",NULL);
				if (hdrv) {
					HMODULE16	hmod;

					hmod = GetDriverModuleHandle(hdrv);
					GetDrv(wDevID)->hdrv = hdrv;
					GetDrv(wDevID)->driverproc = GetProcAddress16(hmod,SEGPTR_GET(SEGPTR_STRDUP("DRIVERPROC")));
					uDevTyp = MCI_DEVTYPE_OTHER;
				} else {
					dprintf_mmsys(stddeb,"[mci extensions] entry %s for %s not supported.\n",str,t);
					return MCIERR_DEVICE_NOT_INSTALLED;
				}
#endif
			}
		} else
			return MCIERR_EXTENSION_NOT_FOUND;
	}

	if (dwParam & MCI_OPEN_ALIAS) {
		dprintf_mmsys(stddeb, "MCI_OPEN // Alias='%s' !\n",
			(char*)PTR_SEG_TO_LIN(lpParms->lpstrAlias));
                GetOpenDrv(wDevID)->lpstrAlias = (LPSTR)SEGPTR_GET(
                    SEGPTR_STRDUP((char*)PTR_SEG_TO_LIN(lpParms->lpstrAlias)));
		/* mplayer does allocate alias to CDAUDIO */
	}
	if (dwParam & MCI_OPEN_TYPE) {
		if (dwParam & MCI_OPEN_TYPE_ID) {
			dprintf_mmsys(stddeb, "MCI_OPEN // Dev=%08lx!\n", (DWORD)lpParms->lpstrDeviceType);
			uDevTyp = LOWORD((DWORD)lpParms->lpstrDeviceType);
 			GetOpenDrv(wDevID)->lpstrDeviceType=(LPSTR)lpParms->lpstrDeviceType;
		} else {
			if (lpParms->lpstrDeviceType == NULL) return MCIERR_INTERNAL;
			dprintf_mmsys(stddeb, "MCI_OPEN // Dev='%s' !\n",
                              (char*)PTR_SEG_TO_LIN(lpParms->lpstrDeviceType));
                        GetOpenDrv(wDevID)->lpstrDeviceType=(LPSTR)SEGPTR_GET(
              SEGPTR_STRDUP((char*)PTR_SEG_TO_LIN(lpParms->lpstrDeviceType)));
			strcpy(str, PTR_SEG_TO_LIN(lpParms->lpstrDeviceType));
			CharUpper32A(str);
			if (strcmp(str, "CDAUDIO") == 0) {
				uDevTyp = MCI_DEVTYPE_CD_AUDIO;
			} else
			if (strcmp(str, "WAVEAUDIO") == 0) {
				uDevTyp = MCI_DEVTYPE_WAVEFORM_AUDIO;
			} else
			if (strcmp(str, "SEQUENCER") == 0)	{
				uDevTyp = MCI_DEVTYPE_SEQUENCER;
			} else
			if (strcmp(str, "ANIMATION1") == 0) {
				uDevTyp = MCI_DEVTYPE_ANIMATION;
			} else
			if (strcmp(str, "AVIVIDEO") == 0) {
				uDevTyp = MCI_DEVTYPE_DIGITAL_VIDEO;
			} else {
#if testing16
				HDRVR16 hdrv;
				fprintf(stderr,"trying to load driver...\n");
				hdrv = OpenDriver(str,"mci",NULL);
				if (hdrv) {
					HMODULE16	hmod;

					hmod = GetDriverModuleHandle(hdrv);
					GetDrv(wDevID)->hdrv = hdrv;
					GetDrv(wDevID)->driverproc = GetProcAddress16(hmod,SEGPTR_GET(SEGPTR_STRDUP("DRIVERPROC")));
					uDevTyp = MCI_DEVTYPE_OTHER;
				} else
#endif
					return MCIERR_DEVICE_NOT_INSTALLED;
			}
		}
	}
	GetDrv(wDevID)->modp.wType = uDevTyp;
	GetDrv(wDevID)->modp.wDeviceID = 0;  /* FIXME? for multiple devices */
	lpParms->wDeviceID = wDevID;
	dprintf_mmsys(stddeb, "MCI_OPEN // mcidev=%d, uDevTyp=%04X wDeviceID=%04X !\n", 
				wDevID, uDevTyp, lpParms->wDeviceID);
	switch(uDevTyp)
        {
        case MCI_DEVTYPE_CD_AUDIO:
	  dwret = CDAUDIO_DriverProc( 0, 0, MCI_OPEN_DRIVER,
				     dwParam, (DWORD)lp16Parms);
	  break;
        case MCI_DEVTYPE_WAVEFORM_AUDIO:
	  dwret =  WAVE_DriverProc( 0, 0, MCI_OPEN_DRIVER, 
				   dwParam, (DWORD)lp16Parms);
	  break;
	case MCI_DEVTYPE_SEQUENCER:
	  dwret = MIDI_DriverProc( 0, 0, MCI_OPEN_DRIVER, 
				  dwParam, (DWORD)lp16Parms);
	  break;
        case MCI_DEVTYPE_ANIMATION:
	  dwret = ANIM_DriverProc( 0, 0, MCI_OPEN_DRIVER, 
				  dwParam, (DWORD)lp16Parms);
	  break;
        case MCI_DEVTYPE_DIGITAL_VIDEO:
	  dprintf_mmsys(stddeb, "MCI_OPEN // No DIGITAL_VIDEO yet !\n");
	  return MCIERR_DEVICE_NOT_INSTALLED;
        default:
#if testing16
	  dwret = Callbacks->CallDriverProc(GetDrv(wDevID)->driverproc,0,GetDrv(wDevID)->hdrv,MCI_OPEN_DRIVER,dwParam,(DWORD)lp16Parms);
	  dprintf_mmsys(stddeb, "MCI_OPEN // Invalid Device Name '%08lx' !\n", (DWORD)lpParms->lpstrDeviceType);
#endif
	  return MCIERR_INVALID_DEVICE_NAME;
        }


	if (dwParam&MCI_NOTIFY)
	  mciDriverNotify(lpParms->dwCallback,wDevID,
			  (dwret==0?MCI_NOTIFY_SUCCESSFUL:MCI_NOTIFY_FAILURE));

	/* only handled devices fall through */
	dprintf_mmsys(stddeb, "MCI_OPEN // wDevID = %04X wDeviceID = %d dwret = %ld\n",wDevID, lpParms->wDeviceID, dwret);
	return dwret;
}

/**************************************************************************
 * 			mciGetDriverData			[MMSYSTEM.708]
 */
DWORD WINAPI mciGetDriverData16(HDRVR16 hdrv) {
	fprintf(stderr,"mciGetDriverData(%04x),stub!\n",hdrv);
	return 0x42;
}

/**************************************************************************
 * 			mciSetDriverData			[MMSYSTEM.707]
 */
DWORD WINAPI mciSetDriverData16(HDRVR16 hdrv,DWORD data) {
	fprintf(stderr,"mciSetDriverData(%04x,%08lx),stub!\n",hdrv,data);
	return 0;
}

/**************************************************************************
 * 			mciClose				[internal]
 */
DWORD mciClose(UINT16 wDevID, DWORD dwParam, LPMCI_GENERIC_PARMS lpParms)
{
	DWORD	dwRet = MCIERR_INTERNAL;

	dprintf_mmsys(stddeb, "mciClose(%04x, %08lX, %p)\n", wDevID, dwParam, lpParms);
	switch(GetDrv(wDevID)->modp.wType) {
	case MCI_DEVTYPE_CD_AUDIO:
		dwRet = CDAUDIO_DriverProc(GetDrv(wDevID)->modp.wDeviceID,0,
				   MCI_CLOSE, dwParam, (DWORD)lpParms);
		break;
	case MCI_DEVTYPE_WAVEFORM_AUDIO:
		dwRet = WAVE_DriverProc(GetDrv(wDevID)->modp.wDeviceID, 0, 
					MCI_CLOSE, dwParam,
					(DWORD)lpParms);
		break;
	case MCI_DEVTYPE_SEQUENCER:
		dwRet = MIDI_DriverProc(GetDrv(wDevID)->modp.wDeviceID, 0, 
					MCI_CLOSE, dwParam,
					(DWORD)lpParms);
		break;
	/*
	case MCI_DEVTYPE_ANIMATION:
		dwRet = ANIM_DriverProc(GetDrv(wDevID)->modp.wDeviceID, 0, 
					MCI_CLOSE, dwParam,
					(DWORD)lpParms);
		break;
	 */
	default:
		dwRet = Callbacks->CallDriverProc(GetDrv(wDevID)->driverproc,GetDrv(wDevID)->modp.wDeviceID,GetDrv(wDevID)->hdrv,MCI_CLOSE,dwParam,(DWORD)lpParms);
	}
	GetDrv(wDevID)->modp.wType = 0;

	if (dwParam&MCI_NOTIFY)
	  mciDriverNotify(lpParms->dwCallback,wDevID,
			  (dwRet==0?MCI_NOTIFY_SUCCESSFUL:MCI_NOTIFY_FAILURE));

	dprintf_mmsys(stddeb, "mciClose() // returns %ld\n",dwRet);
	return dwRet;
}


/**************************************************************************
 * 			mciSysinfo				[internal]
 */
DWORD mciSysInfo(DWORD dwFlags, LPMCI_SYSINFO_PARMS16 lpParms)
{
	int	len;
	LPSTR	ptr;
	LPSTR	lpstrReturn;
	DWORD	*lpdwRet;
	LPSTR	SysFile = "SYSTEM.INI";
	dprintf_mci(stddeb, "mciSysInfo(%08lX, %08lX)\n", dwFlags, (DWORD)lpParms);
	lpstrReturn = PTR_SEG_TO_LIN(lpParms->lpstrReturn);
	switch(dwFlags) {
	case MCI_SYSINFO_QUANTITY:
		dprintf_mci(stddeb, "mciSysInfo // MCI_SYSINFO_QUANTITY \n");
		lpdwRet = (DWORD *)lpstrReturn;
		*(lpdwRet) = InstalledCount;
		return 0;
	case MCI_SYSINFO_INSTALLNAME:
		dprintf_mci(stddeb, "mciSysInfo // MCI_SYSINFO_INSTALLNAME \n");
		if (lpInstallNames == NULL) {
			InstalledCount = 0;
			InstalledListLen = 0;
			ptr = lpInstallNames = xmalloc(2048);
			GetPrivateProfileString32A("mci", NULL, "", lpInstallNames, 2000, SysFile);
			while(strlen(ptr) > 0) {
				dprintf_mci(stddeb, "---> '%s' \n", ptr);
				len = strlen(ptr) + 1;
				ptr += len;
				InstalledListLen += len;
				InstalledCount++;
			}
		}
		if (lpParms->dwRetSize < InstalledListLen)
			lstrcpyn32A(lpstrReturn, lpInstallNames, lpParms->dwRetSize - 1);
		else
			strcpy(lpstrReturn, lpInstallNames);
		return 0;
	case MCI_SYSINFO_NAME:
		dprintf_mci(stddeb, "mciSysInfo // MCI_SYSINFO_NAME \n");
		return 0;
	case MCI_SYSINFO_OPEN:
		dprintf_mci(stddeb, "mciSysInfo // MCI_SYSINFO_OPEN \n");
		return 0;
	}
	return MMSYSERR_INVALPARAM;
}

/**************************************************************************
 *                    	mciLoadCommandResource
 */
UINT16 mciLoadCommandResource16(HANDLE16 hinst,LPCSTR resname,UINT16 type)
{
      char            buf[200];
      OFSTRUCT        ofs;
      HANDLE16        xhinst;
      HRSRC16         hrsrc;
      HGLOBAL16       hmem;
      LPSTR           segstr;
      SEGPTR          xmem;
      LPBYTE          lmem;
      static          mcidevtype = 0;

      fprintf(stderr,"mciLoadCommandResource16(%04x,%s,%d),stub!\n",
              hinst,resname,type
      );
      if (!lstrcmpi32A(resname,"core")) {
              fprintf(stderr,"mciLoadCommandResource(...,\"core\",...), have to use internal tables... (not there yet)\n");
              return 0;
      }
      /* if file exists "resname.mci", then load resource "resname" from it
       * otherwise directly from driver
       */
      strcpy(buf,resname);
      strcat(buf,".mci");
      if (OpenFile32(buf,&ofs,OF_EXIST)!=HFILE_ERROR32) {
              xhinst = LoadLibrary16(buf);
              if (xhinst >32)
                      hinst = xhinst;
      } /* else use passed hinst */
      segstr = SEGPTR_STRDUP(resname);
      hrsrc = FindResource16(hinst,SEGPTR_GET(segstr),type);
      SEGPTR_FREE(segstr);
      if (!hrsrc) {
              fprintf(stderr,"mciLoadCommandResource:no special commandlist found in resource\n");
              return MCI_NO_COMMAND_TABLE;
      }
      hmem = LoadResource16(hinst,hrsrc);
      if (!hmem) {
              fprintf(stderr,"mciLoadCommandResource:couldn't load resource??\n");
              return MCI_NO_COMMAND_TABLE;
      }
      xmem = WIN16_LockResource16(hmem);
      if (!xmem) {
              fprintf(stderr,"mciLoadCommandResource:couldn't lock resource??\n");
              FreeResource16(hmem);
              return MCI_NO_COMMAND_TABLE;
      }
      lmem = PTR_SEG_TO_LIN(xmem);
      fprintf(stderr,"first resource entry is %s\n",(char*)lmem);
      /* parse resource, register stuff, return unique id */
      return ++mcidevtype;
}


/**************************************************************************
 * 			mciSound				[internal]
 *  not used anymore ??

DWORD mciSound(UINT16 wDevID, DWORD dwParam, LPMCI_SOUND_PARMS lpParms)
{
	if (lpParms == NULL) return MCIERR_INTERNAL;
	if (dwParam & MCI_SOUND_NAME)
		dprintf_mci(stddeb, "MCI_SOUND // file='%s' !\n", lpParms->lpstrSoundName);
	return MCIERR_INVALID_DEVICE_ID;
}
 *
 */

static const char *_mciCommandToString(UINT16 wMsg)
{
	static char buffer[100];

#define CASE(s) case (s): return #s

	switch (wMsg) {
		CASE(MCI_OPEN);
		CASE(MCI_CLOSE);
		CASE(MCI_ESCAPE);
		CASE(MCI_PLAY);
		CASE(MCI_SEEK);
		CASE(MCI_STOP);
		CASE(MCI_PAUSE);
		CASE(MCI_INFO);
		CASE(MCI_GETDEVCAPS);
		CASE(MCI_SPIN);
		CASE(MCI_SET);
		CASE(MCI_STEP);
		CASE(MCI_RECORD);
		CASE(MCI_SYSINFO);
		CASE(MCI_BREAK);
		CASE(MCI_SAVE);
		CASE(MCI_STATUS);
		CASE(MCI_CUE);
		CASE(MCI_REALIZE);
		CASE(MCI_WINDOW);
		CASE(MCI_PUT);
		CASE(MCI_WHERE);
		CASE(MCI_FREEZE);
		CASE(MCI_UNFREEZE);
		CASE(MCI_LOAD);
		CASE(MCI_CUT);
		CASE(MCI_COPY);
		CASE(MCI_PASTE);
		CASE(MCI_UPDATE);
		CASE(MCI_RESUME);
		CASE(MCI_DELETE);
		default:
			sprintf(buffer, "%04X", wMsg);
			return buffer;

	}
}

/**************************************************************************
 * 				mciSendCommandA			[WINMM.49]
 */
DWORD WINAPI mciSendCommand32A(UINT32 wDevID, UINT32 wMsg, DWORD dwParam1,
                            DWORD dwParam2)
{
    fprintf(stderr,"mciSendCommand32A(%08x,%s,%08lx,%08lx),stub!\n",
	    wDevID,_mciCommandToString(wMsg),dwParam1,dwParam2
    );
    switch (wMsg) {
    case MCI_OPEN: {
    	LPMCI_OPEN_PARMS32A	lpmop = (LPMCI_OPEN_PARMS32A)dwParam2;
    	fprintf(stderr,"	MCI_OPEN(%s,%s,%s)\n",
		(dwParam1&MCI_OPEN_TYPE)   ?lpmop->lpstrDeviceType:"<null>",
		(dwParam1&MCI_OPEN_ELEMENT)?lpmop->lpstrElementName:"<null>",
		(dwParam1&MCI_OPEN_ALIAS)  ?lpmop->lpstrAlias:"<null>"
	);
	break;
    }
    }
    return 0x1; /* !ok */
}
/**************************************************************************
 * 				mciSendCommand			[MMSYSTEM.701]
 */
DWORD WINAPI mciSendCommand(UINT16 wDevID, UINT16 wMsg, DWORD dwParam1,
                            DWORD dwParam2)
{
    HDRVR16 hDrv = 0;
    dprintf_mci(stddeb, "mciSendCommand(%04X, %s, %08lX, %08lX)\n", 
                wDevID, _mciCommandToString(wMsg), dwParam1, dwParam2);
    switch(wMsg)
    {
    case MCI_OPEN:
        return mciOpen(dwParam1, (LPMCI_OPEN_PARMS16)dwParam2);
    case MCI_CLOSE:
        return mciClose( wDevID, dwParam1,
                         (LPMCI_GENERIC_PARMS)PTR_SEG_TO_LIN(dwParam2));
    case MCI_SYSINFO:
        return mciSysInfo( dwParam1,
                           (LPMCI_SYSINFO_PARMS16)PTR_SEG_TO_LIN(dwParam2));
    default:
        switch(GetDrv(wDevID)->modp.wType)
        {
        case MCI_DEVTYPE_CD_AUDIO:
            return CDAUDIO_DriverProc(GetDrv(wDevID)->modp.wDeviceID, hDrv, 
                                      wMsg, dwParam1, dwParam2);
        case MCI_DEVTYPE_WAVEFORM_AUDIO:
            return WAVE_DriverProc(GetDrv(wDevID)->modp.wDeviceID, hDrv, 
                                   wMsg, dwParam1, dwParam2);
        case MCI_DEVTYPE_SEQUENCER:
            return MIDI_DriverProc(GetDrv(wDevID)->modp.wDeviceID, hDrv, 
                                   wMsg, dwParam1, dwParam2);
	/*
        case MCI_DEVTYPE_ANIMATION:
            return ANIM_DriverProc(GetDrv(wDevID)->modp.wDeviceID, hDrv, 
                                   wMsg, dwParam1, dwParam2);
 	 */
        default:
	    return Callbacks->CallDriverProc(GetDrv(wDevID)->driverproc,GetDrv(wDevID)->modp.wDeviceID,GetDrv(wDevID)->hdrv,MCI_CLOSE,dwParam1,dwParam2);

            dprintf_mci(stddeb,
                        "mciSendCommand() // unknown device type=%04X !\n", 
                        GetDrv(wDevID)->modp.wType);
        }
    }
    return MMSYSERR_INVALPARAM;
}

/**************************************************************************
* 				mciGetDeviceID	       	[MMSYSTEM.703]
*/
UINT16 WINAPI mciGetDeviceID (LPCSTR lpstrName)
{
    UINT16 wDevID;

    dprintf_mci(stddeb, "mciGetDeviceID(\"%s\")\n", lpstrName);
    if (lpstrName && !lstrcmpi32A(lpstrName, "ALL"))
        return MCI_ALL_DEVICE_ID;

    if (!lpstrName)
	return 0;

    wDevID = MMSYSTEM_FirstDevID();
    while(MMSYSTEM_DevIDValid(wDevID) && GetDrv(wDevID)->modp.wType) {
	if (GetOpenDrv(wDevID)->lpstrDeviceType && 
            strcmp(PTR_SEG_TO_LIN(GetOpenDrv(wDevID)->lpstrDeviceType), lpstrName) == 0)
	    return wDevID;

	if (GetOpenDrv(wDevID)->lpstrAlias && 
            strcmp(PTR_SEG_TO_LIN(GetOpenDrv(wDevID)->lpstrAlias), lpstrName) == 0)
	    return wDevID;

	wDevID = MMSYSTEM_NextDevID(wDevID);
    }

    return 0;
}

/**************************************************************************
* 				mciSetYieldProc		[MMSYSTEM.714]
*/
BOOL16 WINAPI mciSetYieldProc (UINT16 uDeviceID, 
                               YIELDPROC fpYieldProc, DWORD dwYieldData)
{
    return FALSE;
}

/**************************************************************************
* 				mciGetDeviceIDFromElementID	[MMSYSTEM.715]
*/
UINT16 WINAPI mciGetDeviceIDFromElementID(DWORD dwElementID, LPCSTR lpstrType)
{
    return 0;
}

/**************************************************************************
* 				mciGetYieldProc		[MMSYSTEM.716]
*/
YIELDPROC WINAPI mciGetYieldProc(UINT16 uDeviceID, DWORD * lpdwYieldData)
{
    return NULL;
}

/**************************************************************************
* 				mciGetCreatorTask	[MMSYSTEM.717]
*/
HTASK16 WINAPI mciGetCreatorTask(UINT16 uDeviceID)
{
    return 0;
}

/**************************************************************************
 * 				midiOutGetNumDevs	[WINMM.80]
 */
UINT32 WINAPI midiOutGetNumDevs32(void)
{
	return midiOutGetNumDevs16();
}
/**************************************************************************
 * 				midiOutGetNumDevs	[MMSYSTEM.201]
 */
UINT16 WINAPI midiOutGetNumDevs16(void)
{
	UINT16	count = 0;
	dprintf_mmsys(stddeb, "midiOutGetNumDevs\n");
	count += modMessage(0, MODM_GETNUMDEVS, 0L, 0L, 0L);
	dprintf_mmsys(stddeb, "midiOutGetNumDevs return %u \n", count);
	return count;
}

/**************************************************************************
 * 				midiOutGetDevCapsW	[WINMM.76]
 */
UINT32 WINAPI midiOutGetDevCaps32W(UINT32 uDeviceID,LPMIDIOUTCAPS32W lpCaps, UINT32 uSize)
{
	MIDIOUTCAPS16	moc16;
	UINT32		ret;

	ret = midiOutGetDevCaps16(uDeviceID,&moc16,sizeof(moc16));
	lpCaps->wMid		= moc16.wMid;
	lpCaps->wPid		= moc16.wPid;
	lpCaps->vDriverVersion	= moc16.vDriverVersion;
	lstrcpyAtoW(lpCaps->szPname,moc16.szPname);
	lpCaps->wTechnology	= moc16.wTechnology;
	lpCaps->wVoices		= moc16.wVoices;
	lpCaps->wNotes		= moc16.wNotes;
	lpCaps->wChannelMask	= moc16.wChannelMask;
	lpCaps->dwSupport	= moc16.dwSupport;
	return ret;
}
/**************************************************************************
 * 				midiOutGetDevCapsA	[WINMM.75]
 */
UINT32 WINAPI midiOutGetDevCaps32A(UINT32 uDeviceID,LPMIDIOUTCAPS32A lpCaps, UINT32 uSize)
{
	MIDIOUTCAPS16	moc16;
	UINT32		ret;

	ret = midiOutGetDevCaps16(uDeviceID,&moc16,sizeof(moc16));
	lpCaps->wMid		= moc16.wMid;
	lpCaps->wPid		= moc16.wPid;
	lpCaps->vDriverVersion	= moc16.vDriverVersion;
	strcpy(lpCaps->szPname,moc16.szPname);
	lpCaps->wTechnology	= moc16.wTechnology;
	lpCaps->wVoices		= moc16.wVoices;
	lpCaps->wNotes		= moc16.wNotes;
	lpCaps->wChannelMask	= moc16.wChannelMask;
	lpCaps->dwSupport	= moc16.dwSupport;
	return ret;
}

/**************************************************************************
 * 				midiOutGetDevCaps	[MMSYSTEM.202]
 */
UINT16 WINAPI midiOutGetDevCaps16(UINT16 uDeviceID,LPMIDIOUTCAPS16 lpCaps, UINT16 uSize)
{
	dprintf_mmsys(stddeb, "midiOutGetDevCaps\n");
	return modMessage(uDeviceID,MODM_GETDEVCAPS,0,(DWORD)lpCaps,uSize);
}

/**************************************************************************
 * 				midiOutGetErrorTextA 	[WINMM.77]
 */
UINT32 WINAPI midiOutGetErrorText32A(UINT32 uError, LPSTR lpText, UINT32 uSize)
{
	dprintf_mmsys(stddeb, "midiOutGetErrorText\n");
	return midiGetErrorText(uError, lpText, uSize);
}

/**************************************************************************
 * 				midiOutGetErrorTextW 	[WINMM.78]
 */
UINT32 WINAPI midiOutGetErrorText32W(UINT32 uError, LPWSTR lpText, UINT32 uSize)
{
	LPSTR	xstr = HeapAlloc(GetProcessHeap(),0,uSize);
	UINT32	ret;
	dprintf_mmsys(stddeb, "midiOutGetErrorText\n");
	ret = midiGetErrorText(uError, xstr, uSize);
	lstrcpyAtoW(lpText,xstr);
	HeapFree(GetProcessHeap(),0,xstr);
	return ret;
}
/**************************************************************************
 * 				midiOutGetErrorText 	[MMSYSTEM.203]
 */
UINT16 WINAPI midiOutGetErrorText16(UINT16 uError, LPSTR lpText, UINT16 uSize)
{
	dprintf_mmsys(stddeb, "midiOutGetErrorText\n");
	return midiGetErrorText(uError, lpText, uSize);
}

/**************************************************************************
 * 				midiGetErrorText       	[internal]
 */
UINT16 WINAPI midiGetErrorText(UINT16 uError, LPSTR lpText, UINT16 uSize)
{
	LPSTR	msgptr;
	if ((lpText == NULL) || (uSize < 1)) return(FALSE);
	lpText[0] = '\0';
	switch(uError) {
		case MIDIERR_UNPREPARED:
			msgptr = "The MIDI header was not prepared. Use the Prepare function to prepare the header, and then try again.";
			break;
		case MIDIERR_STILLPLAYING:
			msgptr = "Cannot perform this operation while media data is still playing. Reset the device, or wait until the data is finished playing.";
			break;
		case MIDIERR_NOMAP:
			msgptr = "A MIDI map was not found. There may be a problem with the driver, or the MIDIMAP.CFG file may be corrupt or missing.";
			break;
		case MIDIERR_NOTREADY:
			msgptr = "The port is transmitting data to the device. Wait until the data has been transmitted, and then try again.";
			break;
		case MIDIERR_NODEVICE:
			msgptr = "The current MIDI Mapper setup refers to a MIDI device that is not installed on the system. Use MIDI Mapper to edit the setup.";
			break;
		case MIDIERR_INVALIDSETUP:
			msgptr = "The current MIDI setup is damaged. Copy the original MIDIMAP.CFG file to the Windows SYSTEM directory, and then try again.";
			break;
/*
msg# 336 : Cannot use the song-pointer time format and the SMPTE time-format together.
msg# 337 : The specified MIDI device is already in use. Wait until it is free, and then try again.
msg# 338 : The specified MIDI device is not installed on the system. Use the Drivers option in Control Panel to install the driver.
msg# 339 : The current MIDI Mapper setup refers to a MIDI device that is not installed on the system. Use MIDI Mapper to edit the setup.
msg# 340 : An error occurred using the specified port.
msg# 341 : All multimedia timers are being used by other applications. Quit one of these applications, and then try again.
msg# 342 : There is no current MIDI port.
msg# 343 : There are no MIDI devices installed on the system. Use the Drivers option in Control Panel to install the driver.
*/
		default:
			msgptr = "Unknown MIDI Error !\n";
			break;
		}
	lstrcpyn32A(lpText, msgptr, uSize);
	return TRUE;
}

/**************************************************************************
 * 				midiOutOpen    		[WINM.84]
 */
UINT32 WINAPI midiOutOpen32(HMIDIOUT32 * lphMidiOut, UINT32 uDeviceID,
                            DWORD dwCallback, DWORD dwInstance, DWORD dwFlags)
{
	HMIDIOUT16	hmo16;
	UINT32		ret;

	ret = midiOutOpen16(&hmo16,uDeviceID,dwCallback,dwInstance,dwFlags);
	if (lphMidiOut) *lphMidiOut = hmo16;
	return ret;
}
/**************************************************************************
 * 				midiOutOpen    		[MMSYSTEM.204]
 */
UINT16 WINAPI midiOutOpen16(HMIDIOUT16 * lphMidiOut, UINT16 uDeviceID,
                            DWORD dwCallback, DWORD dwInstance, DWORD dwFlags)
{
	HMIDI16	hMidiOut;
	LPMIDIOPENDESC	lpDesc;
	DWORD	dwRet = 0;
	BOOL32	bMapperFlg = FALSE;
	if (lphMidiOut != NULL) *lphMidiOut = 0;
	dprintf_mmsys(stddeb, "midiOutOpen(%p, %d, %08lX, %08lX, %08lX);\n", 
		lphMidiOut, uDeviceID, dwCallback, dwInstance, dwFlags);
	if (uDeviceID == (UINT16)MIDI_MAPPER) {
		dprintf_mmsys(stddeb, "midiOutOpen	// MIDI_MAPPER mode requested !\n");
		bMapperFlg = TRUE;
		uDeviceID = 0;
	}
	hMidiOut = USER_HEAP_ALLOC(sizeof(MIDIOPENDESC));
	if (lphMidiOut != NULL) *lphMidiOut = hMidiOut;
	lpDesc = (LPMIDIOPENDESC) USER_HEAP_LIN_ADDR(hMidiOut);
	if (lpDesc == NULL)
		return MMSYSERR_NOMEM;
	lpDesc->hMidi = hMidiOut;
	lpDesc->dwCallback = dwCallback;
	lpDesc->dwInstance = dwInstance;
	while(uDeviceID < MAXMIDIDRIVERS) {
		dwRet = modMessage(uDeviceID, MODM_OPEN, 
			lpDesc->dwInstance, (DWORD)lpDesc, 0L);
		if (dwRet == MMSYSERR_NOERROR) break;
		if (!bMapperFlg) break;
		uDeviceID++;
		dprintf_mmsys(stddeb, "midiOutOpen	// MIDI_MAPPER mode ! try next driver...\n");
	}
	return dwRet;
}

/**************************************************************************
 * 				midiOutClose		[WINMM.74]
 */
UINT32 WINAPI midiOutClose32(HMIDIOUT32 hMidiOut)
{
	return midiOutClose16(hMidiOut);
}

/**************************************************************************
 * 				midiOutClose		[MMSYSTEM.205]
 */
UINT16 WINAPI midiOutClose16(HMIDIOUT16 hMidiOut)
{
	LPMIDIOPENDESC	lpDesc;
	dprintf_mmsys(stddeb, "midiOutClose(%04X)\n", hMidiOut);
	lpDesc = (LPMIDIOPENDESC) USER_HEAP_LIN_ADDR(hMidiOut);
	if (lpDesc == NULL) return MMSYSERR_INVALHANDLE;
	return modMessage(0, MODM_CLOSE, lpDesc->dwInstance, 0L, 0L);
}

/**************************************************************************
 * 				midiOutPrepareHeader	[WINMM.85]
 */
UINT32 WINAPI midiOutPrepareHeader32(HMIDIOUT32 hMidiOut,
                                     MIDIHDR * lpMidiOutHdr, UINT32 uSize)
{
	return midiOutPrepareHeader16(hMidiOut,lpMidiOutHdr,uSize);
}

/**************************************************************************
 * 				midiOutPrepareHeader	[MMSYSTEM.206]
 */
UINT16 WINAPI midiOutPrepareHeader16(HMIDIOUT16 hMidiOut,
                                     MIDIHDR * lpMidiOutHdr, UINT16 uSize)
{
	LPMIDIOPENDESC	lpDesc;
	dprintf_mmsys(stddeb, "midiOutPrepareHeader(%04X, %p, %d)\n", 
					hMidiOut, lpMidiOutHdr, uSize);
	lpDesc = (LPMIDIOPENDESC) USER_HEAP_LIN_ADDR(hMidiOut);
	if (lpDesc == NULL) return MMSYSERR_INVALHANDLE;
	return modMessage(0, MODM_PREPARE, lpDesc->dwInstance, 
			  (DWORD)lpMidiOutHdr, (DWORD)uSize);
}

/**************************************************************************
 * 				midiOutUnprepareHeader	[WINMM.89]
 */
UINT32 WINAPI midiOutUnprepareHeader32(HMIDIOUT32 hMidiOut,
                                       MIDIHDR * lpMidiOutHdr, UINT32 uSize)
{
	return midiOutUnprepareHeader16(hMidiOut,lpMidiOutHdr,uSize);
}
/**************************************************************************
 * 				midiOutUnprepareHeader	[MMSYSTEM.207]
 */
UINT16 WINAPI midiOutUnprepareHeader16(HMIDIOUT16 hMidiOut,
                                     MIDIHDR * lpMidiOutHdr, UINT16 uSize)
{
	LPMIDIOPENDESC	lpDesc;
	dprintf_mmsys(stddeb, "midiOutUnprepareHeader(%04X, %p, %d)\n", 
					hMidiOut, lpMidiOutHdr, uSize);
	lpDesc = (LPMIDIOPENDESC) USER_HEAP_LIN_ADDR(hMidiOut);
	if (lpDesc == NULL) return MMSYSERR_INVALHANDLE;
	return modMessage(0, MODM_UNPREPARE, lpDesc->dwInstance, 
						(DWORD)lpMidiOutHdr, (DWORD)uSize);
}

/**************************************************************************
 * 				midiOutShortMsg		[WINMM.88]
 */
UINT32 WINAPI midiOutShortMsg32(HMIDIOUT32 hMidiOut, DWORD dwMsg)
{
	return midiOutShortMsg16(hMidiOut,dwMsg);
}
/**************************************************************************
 * 				midiOutShortMsg		[MMSYSTEM.208]
 */
UINT16 WINAPI midiOutShortMsg16(HMIDIOUT16 hMidiOut, DWORD dwMsg)
{
	LPMIDIOPENDESC	lpDesc;
	dprintf_mmsys(stddeb, "midiOutShortMsg(%04X, %08lX)\n", hMidiOut, dwMsg);
	lpDesc = (LPMIDIOPENDESC) USER_HEAP_LIN_ADDR(hMidiOut);
	if (lpDesc == NULL) return MMSYSERR_INVALHANDLE;
	return modMessage(0, MODM_DATA, lpDesc->dwInstance, dwMsg, 0L);
}

/**************************************************************************
 * 				midiOutLongMsg		[WINMM.82]
 */
UINT32 WINAPI midiOutLongMsg32(HMIDIOUT32 hMidiOut,
                               MIDIHDR * lpMidiOutHdr, UINT32 uSize)
{
	return midiOutLongMsg16(hMidiOut,lpMidiOutHdr,uSize);
}

/**************************************************************************
 * 				midiOutLongMsg		[MMSYSTEM.209]
 */
UINT16 WINAPI midiOutLongMsg16(HMIDIOUT16 hMidiOut,
                               MIDIHDR * lpMidiOutHdr, UINT16 uSize)
{
	LPMIDIOPENDESC	lpDesc;
	dprintf_mmsys(stddeb, "midiOutLongMsg(%04X, %p, %d)\n", 
				hMidiOut, lpMidiOutHdr, uSize);
	lpDesc = (LPMIDIOPENDESC) USER_HEAP_LIN_ADDR(hMidiOut);
	if (lpDesc == NULL) return MMSYSERR_INVALHANDLE;
	return modMessage(0, MODM_LONGDATA, lpDesc->dwInstance, 
						(DWORD)lpMidiOutHdr, (DWORD)uSize);
}

/**************************************************************************
 * 				midiOutReset		[WINMM.86]
 */
UINT32 WINAPI midiOutReset32(HMIDIOUT32 hMidiOut)
{
	return midiOutReset16(hMidiOut);
}

/**************************************************************************
 * 				midiOutReset		[MMSYSTEM.210]
 */
UINT16 WINAPI midiOutReset16(HMIDIOUT16 hMidiOut)
{
	LPMIDIOPENDESC	lpDesc;
	dprintf_mmsys(stddeb, "midiOutReset(%04X)\n", hMidiOut);
	lpDesc = (LPMIDIOPENDESC) USER_HEAP_LIN_ADDR(hMidiOut);
	if (lpDesc == NULL) return MMSYSERR_INVALHANDLE;
	return modMessage(0, MODM_RESET, lpDesc->dwInstance, 0L, 0L);
}

/**************************************************************************
 * 				midiOutGetVolume	[WINM.81]
 */
UINT32 WINAPI midiOutGetVolume32(UINT32 uDeviceID, DWORD * lpdwVolume)
{
	return midiOutGetVolume16(uDeviceID,lpdwVolume);
}
/**************************************************************************
 * 				midiOutGetVolume	[MMSYSTEM.211]
 */
UINT16 WINAPI midiOutGetVolume16(UINT16 uDeviceID, DWORD * lpdwVolume)
{
	dprintf_mmsys(stddeb, "midiOutGetVolume(%04X, %p);\n", uDeviceID, lpdwVolume);
	return modMessage(uDeviceID, MODM_GETVOLUME, 0L, (DWORD)lpdwVolume, 0L);
}

/**************************************************************************
 * 				midiOutSetVolume	[WINMM.87]
 */
UINT32 WINAPI midiOutSetVolume32(UINT32 uDeviceID, DWORD dwVolume)
{
	return midiOutSetVolume16(uDeviceID,dwVolume);
}

/**************************************************************************
 * 				midiOutSetVolume	[MMSYSTEM.212]
 */
UINT16 WINAPI midiOutSetVolume16(UINT16 uDeviceID, DWORD dwVolume)
{
	dprintf_mmsys(stddeb, "midiOutSetVolume(%04X, %08lX);\n", uDeviceID, dwVolume);
	return modMessage(uDeviceID, MODM_SETVOLUME, 0L, dwVolume, 0L);
}

/**************************************************************************
 * 				midiOutCachePatches		[WINMM.73]
 */
UINT32 WINAPI midiOutCachePatches32(HMIDIOUT32 hMidiOut, UINT32 uBank,
                                    WORD * lpwPatchArray, UINT32 uFlags)
{
	return midiOutCachePatches16(hMidiOut,uBank,lpwPatchArray,uFlags);
}

/**************************************************************************
 * 				midiOutCachePatches		[MMSYSTEM.213]
 */
UINT16 WINAPI midiOutCachePatches16(HMIDIOUT16 hMidiOut, UINT16 uBank,
                                    WORD * lpwPatchArray, UINT16 uFlags)
{
        /* not really necessary to support this */
	fprintf(stdnimp, "midiOutCachePatches: not supported yet\n");
	return MMSYSERR_NOTSUPPORTED;
}

/**************************************************************************
 * 				midiOutCacheDrumPatches	[WINMM.72]
 */
UINT32 WINAPI midiOutCacheDrumPatches32(HMIDIOUT32 hMidiOut, UINT32 uPatch,
                                        WORD * lpwKeyArray, UINT32 uFlags)
{
	return midiOutCacheDrumPatches16(hMidiOut,uPatch,lpwKeyArray,uFlags);
}

/**************************************************************************
 * 				midiOutCacheDrumPatches	[MMSYSTEM.214]
 */
UINT16 WINAPI midiOutCacheDrumPatches16(HMIDIOUT16 hMidiOut, UINT16 uPatch,
                                        WORD * lpwKeyArray, UINT16 uFlags)
{
	fprintf(stdnimp, "midiOutCacheDrumPatchesi: not supported yet\n");
	return MMSYSERR_NOTSUPPORTED;
}

/**************************************************************************
 * 				midiOutGetID		[WINMM.79]
 */
UINT32 WINAPI midiOutGetID32(HMIDIOUT32 hMidiOut, UINT32 * lpuDeviceID)
{
	UINT16	xid;
	UINT32	ret;

	ret = midiOutGetID16(hMidiOut,&xid);
	*lpuDeviceID = xid;
	return ret;
}

/**************************************************************************
 * 				midiOutGetID		[MMSYSTEM.215]
 */
UINT16 WINAPI midiOutGetID16(HMIDIOUT16 hMidiOut, UINT16 * lpuDeviceID)
{
	dprintf_mmsys(stddeb, "midiOutGetID\n");
	return 0;
}

/**************************************************************************
 * 				midiOutMessage		[WINMM.83]
 */
DWORD WINAPI midiOutMessage32(HMIDIOUT32 hMidiOut, UINT32 uMessage, 
                              DWORD dwParam1, DWORD dwParam2)
{
	LPMIDIOPENDESC	lpDesc;

	dprintf_mmsys(stddeb, "midiOutMessage(%04X, %04X, %08lX, %08lX)\n", 
			hMidiOut, uMessage, dwParam1, dwParam2);
	lpDesc = (LPMIDIOPENDESC) USER_HEAP_LIN_ADDR(hMidiOut);
	if (lpDesc == NULL) return MMSYSERR_INVALHANDLE;
	switch (uMessage) {
	case MODM_OPEN:
		fprintf(stderr,"midiOutMessage32: can't handle MODM_OPEN!\n");
		return 0;
	case MODM_GETDEVCAPS:
		return midiOutGetDevCaps32A(hMidiOut,(LPMIDIOUTCAPS32A)dwParam1,dwParam2);
	case MODM_GETNUMDEVS:
	case MODM_RESET:
	case MODM_CLOSE:
	case MODM_GETVOLUME:
	case MODM_SETVOLUME:
	case MODM_LONGDATA:
	case MODM_PREPARE:
	case MODM_UNPREPARE:
		/* no argument conversion needed */
		break;
	default:
		fprintf(stderr,"unhandled midiOutMessage32(%04x,%04x,%08lx,%08lx)\n",
			hMidiOut,uMessage,dwParam1,dwParam2
		);
		break;
	}
	return modMessage(0, uMessage, lpDesc->dwInstance, dwParam1, dwParam2);
}

/**************************************************************************
 * 				midiOutMessage		[MMSYSTEM.216]
 */
DWORD WINAPI midiOutMessage16(HMIDIOUT16 hMidiOut, UINT16 uMessage, 
                              DWORD dwParam1, DWORD dwParam2)
{
	LPMIDIOPENDESC	lpDesc;

	dprintf_mmsys(stddeb, "midiOutMessage(%04X, %04X, %08lX, %08lX)\n", 
			hMidiOut, uMessage, dwParam1, dwParam2);
	lpDesc = (LPMIDIOPENDESC) USER_HEAP_LIN_ADDR(hMidiOut);
	if (lpDesc == NULL) return MMSYSERR_INVALHANDLE;
	switch (uMessage) {
	case MODM_OPEN:
		fprintf(stderr,"midiOutMessage16: can't handle MODM_OPEN!\n");
		return 0;
	case MODM_GETNUMDEVS:
	case MODM_RESET:
	case MODM_CLOSE:
	case MODM_SETVOLUME:
		/* no argument conversion needed */
		break;
	case MODM_GETVOLUME:
		return midiOutGetVolume16(hMidiOut,(LPDWORD)PTR_SEG_TO_LIN(dwParam1));
	case MODM_LONGDATA:
		return midiOutLongMsg16(hMidiOut,(LPMIDIHDR)PTR_SEG_TO_LIN(dwParam1),dwParam2);
	case MODM_PREPARE:
		return midiOutPrepareHeader16(hMidiOut,(LPMIDIHDR)PTR_SEG_TO_LIN(dwParam1),dwParam2);
	case MODM_UNPREPARE:
		return midiOutUnprepareHeader16(hMidiOut,(LPMIDIHDR)PTR_SEG_TO_LIN(dwParam1),dwParam2);
	default:
		fprintf(stderr,"unhandled midiOutMessage16(%04x,%04x,%08lx,%08lx)\n",
			hMidiOut,uMessage,dwParam1,dwParam2
		);
		break;
	}
	return modMessage(0, uMessage, lpDesc->dwInstance, dwParam1, dwParam2);
}

/**************************************************************************
 * 				midiInGetNumDevs	[WINMM.64]
 */
UINT32 WINAPI midiInGetNumDevs32(void)
{
	return midiInGetNumDevs16();
}

/**************************************************************************
 * 				midiInGetNumDevs	[MMSYSTEM.301]
 */
UINT16 WINAPI midiInGetNumDevs16(void)
{
	UINT16	count = 0;
	dprintf_mmsys(stddeb, "midiInGetNumDevs\n");
	count += midMessage(0, MIDM_GETNUMDEVS, 0L, 0L, 0L);
	dprintf_mmsys(stddeb, "midiInGetNumDevs return %u \n", count);
	return count;
}

/**************************************************************************
 * 				midiInGetDevCaps	[WINMM.60]
 */
UINT32 WINAPI midiInGetDevCaps32W(UINT32 uDeviceID,
                                  LPMIDIINCAPS32W lpCaps, UINT32 uSize)
{
	MIDIINCAPS16	mic16;
	UINT32		ret = midiInGetDevCaps16(uDeviceID,&mic16,uSize);

	lpCaps->wMid = mic16.wMid;
	lpCaps->wPid = mic16.wPid;
	lpCaps->vDriverVersion = mic16.vDriverVersion;
	lstrcpyAtoW(lpCaps->szPname,mic16.szPname);
	lpCaps->dwSupport = mic16.dwSupport;
	return ret;
}

/**************************************************************************
 * 				midiInGetDevCaps	[WINMM.59]
 */
UINT32 WINAPI midiInGetDevCaps32A(UINT32 uDeviceID,
                                  LPMIDIINCAPS32A lpCaps, UINT32 uSize)
{
	MIDIINCAPS16	mic16;
	UINT32		ret = midiInGetDevCaps16(uDeviceID,&mic16,uSize);

	lpCaps->wMid = mic16.wMid;
	lpCaps->wPid = mic16.wPid;
	lpCaps->vDriverVersion = mic16.vDriverVersion;
	strcpy(lpCaps->szPname,mic16.szPname);
	lpCaps->dwSupport = mic16.dwSupport;
	return ret;
}

/**************************************************************************
 * 				midiInGetDevCaps	[MMSYSTEM.302]
 */
UINT16 WINAPI midiInGetDevCaps16(UINT16 uDeviceID,
                               LPMIDIINCAPS16 lpCaps, UINT16 uSize)
{
	dprintf_mmsys(stddeb, "midiInGetDevCaps\n");
	return midMessage(uDeviceID,MIDM_GETDEVCAPS,0,(DWORD)lpCaps,uSize);;
}

/**************************************************************************
 * 				midiInGetErrorText 		[WINMM.62]
 */
UINT32 WINAPI midiInGetErrorText32W(UINT32 uError, LPWSTR lpText, UINT32 uSize)
{
	LPSTR	xstr = HeapAlloc(GetProcessHeap(),0,uSize);
	UINT32	ret = midiInGetErrorText16(uError,xstr,uSize);
	lstrcpyAtoW(lpText,xstr);
	HeapFree(GetProcessHeap(),0,xstr);
	return ret;
}
/**************************************************************************
 * 				midiInGetErrorText 		[WINMM.61]
 */
UINT32 WINAPI midiInGetErrorText32A(UINT32 uError, LPSTR lpText, UINT32 uSize)
{
	return midiInGetErrorText16(uError,lpText,uSize);
}

/**************************************************************************
 * 				midiInGetErrorText 		[MMSYSTEM.303]
 */
UINT16 WINAPI midiInGetErrorText16(UINT16 uError, LPSTR lpText, UINT16 uSize)
{
	dprintf_mmsys(stddeb, "midiInGetErrorText\n");
	return (midiGetErrorText(uError, lpText, uSize));
}

/**************************************************************************
 * 				midiInOpen		[WINMM.66]
 */
UINT32 WINAPI midiInOpen32(HMIDIIN32 * lphMidiIn, UINT32 uDeviceID,
                           DWORD dwCallback, DWORD dwInstance, DWORD dwFlags)
{
	HMIDIIN16	xhmid16;
	UINT32 ret = midiInOpen16(&xhmid16,uDeviceID,dwCallback,dwInstance,dwFlags);
	if (lphMidiIn) *lphMidiIn = xhmid16;
	return ret;
}

/**************************************************************************
 * 				midiInOpen		[MMSYSTEM.304]
 */
UINT16 WINAPI midiInOpen16(HMIDIIN16 * lphMidiIn, UINT16 uDeviceID,
                           DWORD dwCallback, DWORD dwInstance, DWORD dwFlags)
{
	HMIDI16	hMidiIn;
	LPMIDIOPENDESC	lpDesc;
	DWORD	dwRet = 0;
	BOOL32	bMapperFlg = FALSE;

	if (lphMidiIn != NULL) *lphMidiIn = 0;
	dprintf_mmsys(stddeb, "midiInOpen(%p, %d, %08lX, %08lX, %08lX);\n", 
		lphMidiIn, uDeviceID, dwCallback, dwInstance, dwFlags);
	if (uDeviceID == (UINT16)MIDI_MAPPER) {
		dprintf_mmsys(stddeb, "midiInOpen	// MIDI_MAPPER mode requested !\n");
		bMapperFlg = TRUE;
		uDeviceID = 0;
	}
	hMidiIn = USER_HEAP_ALLOC(sizeof(MIDIOPENDESC));
	if (lphMidiIn != NULL) *lphMidiIn = hMidiIn;
	lpDesc = (LPMIDIOPENDESC) USER_HEAP_LIN_ADDR(hMidiIn);
	if (lpDesc == NULL) return MMSYSERR_NOMEM;
	lpDesc->hMidi = hMidiIn;
	lpDesc->dwCallback = dwCallback;
	lpDesc->dwInstance = dwInstance;
	while(uDeviceID < MAXMIDIDRIVERS) {
		dwRet = midMessage(uDeviceID, MIDM_OPEN, 
			lpDesc->dwInstance, (DWORD)lpDesc, 0L);
		if (dwRet == MMSYSERR_NOERROR) break;
		if (!bMapperFlg) break;
		uDeviceID++;
		dprintf_mmsys(stddeb, "midiInOpen	// MIDI_MAPPER mode ! try next driver...\n");
	}
	return dwRet;
}

/**************************************************************************
 * 				midiInClose		[WINMM.58]
 */
UINT32 WINAPI midiInClose32(HMIDIIN32 hMidiIn)
{
	return midiInClose16(hMidiIn);
}

/**************************************************************************
 * 				midiInClose		[MMSYSTEM.305]
 */
UINT16 WINAPI midiInClose16(HMIDIIN16 hMidiIn)
{
	LPMIDIOPENDESC	lpDesc;
	dprintf_mmsys(stddeb, "midiInClose(%04X)\n", hMidiIn);
	lpDesc = (LPMIDIOPENDESC) USER_HEAP_LIN_ADDR(hMidiIn);
	if (lpDesc == NULL) return MMSYSERR_INVALHANDLE;
	return midMessage(0, MIDM_CLOSE, lpDesc->dwInstance, 0L, 0L);
}

/**************************************************************************
 * 				midiInPrepareHeader	[WINMM.67]
 */
UINT32 WINAPI midiInPrepareHeader32(HMIDIIN32 hMidiIn,
                                    MIDIHDR * lpMidiInHdr, UINT32 uSize)
{
	return midiInPrepareHeader16(hMidiIn,lpMidiInHdr,uSize);
}

/**************************************************************************
 * 				midiInPrepareHeader	[MMSYSTEM.306]
 */
UINT16 WINAPI midiInPrepareHeader16(HMIDIIN16 hMidiIn,
                                    MIDIHDR * lpMidiInHdr, UINT16 uSize)
{
	LPMIDIOPENDESC	lpDesc;
	dprintf_mmsys(stddeb, "midiInPrepareHeader(%04X, %p, %d)\n", 
					hMidiIn, lpMidiInHdr, uSize);
	lpDesc = (LPMIDIOPENDESC) USER_HEAP_LIN_ADDR(hMidiIn);
	if (lpDesc == NULL) return MMSYSERR_INVALHANDLE;
	return midMessage(0, MIDM_PREPARE, lpDesc->dwInstance, 
						(DWORD)lpMidiInHdr, (DWORD)uSize);
}

/**************************************************************************
 * 				midiInUnprepareHeader	[WINMM.71]
 */
UINT32 WINAPI midiInUnprepareHeader32(HMIDIIN32 hMidiIn,
                                      MIDIHDR * lpMidiInHdr, UINT32 uSize)
{
	return midiInUnprepareHeader16(hMidiIn,lpMidiInHdr,uSize);
}

/**************************************************************************
 * 				midiInUnprepareHeader	[MMSYSTEM.307]
 */
UINT16 WINAPI midiInUnprepareHeader16(HMIDIIN16 hMidiIn,
                                      MIDIHDR * lpMidiInHdr, UINT16 uSize)
{
	LPMIDIOPENDESC	lpDesc;
	dprintf_mmsys(stddeb, "midiInUnprepareHeader(%04X, %p, %d)\n", 
					hMidiIn, lpMidiInHdr, uSize);
	lpDesc = (LPMIDIOPENDESC) USER_HEAP_LIN_ADDR(hMidiIn);
	if (lpDesc == NULL) return MMSYSERR_INVALHANDLE;
	return midMessage(0, MIDM_UNPREPARE, lpDesc->dwInstance, 
						(DWORD)lpMidiInHdr, (DWORD)uSize);
}

/**************************************************************************
 * 				midiInAddBuffer		[WINMM.57]
 */
UINT32 WINAPI midiInAddBuffer32(HMIDIIN32 hMidiIn,
                                MIDIHDR * lpMidiInHdr, UINT32 uSize)
{
	return midiInAddBuffer16(hMidiIn,lpMidiInHdr,uSize);
}

/**************************************************************************
 * 				midiInAddBuffer		[MMSYSTEM.308]
 */
UINT16 WINAPI midiInAddBuffer16(HMIDIIN16 hMidiIn,
                                MIDIHDR * lpMidiInHdr, UINT16 uSize)
{
	dprintf_mmsys(stddeb, "midiInAddBuffer\n");
	return 0;
}

/**************************************************************************
 * 				midiInStart			[WINMM.69]
 */
UINT32 WINAPI midiInStart32(HMIDIIN32 hMidiIn)
{
	return midiInStart16(hMidiIn);
}

/**************************************************************************
 * 				midiInStart			[MMSYSTEM.309]
 */
UINT16 WINAPI midiInStart16(HMIDIIN16 hMidiIn)
{
	dprintf_mmsys(stddeb, "midiInStart\n");
	return 0;
}

/**************************************************************************
 * 				midiInStop			[WINMM.70]
 */
UINT32 WINAPI midiInStop32(HMIDIIN32 hMidiIn)
{
	return midiInStop16(hMidiIn);
}

/**************************************************************************
 * 				midiInStop			[MMSYSTEM.310]
 */
UINT16 WINAPI midiInStop16(HMIDIIN16 hMidiIn)
{
	dprintf_mmsys(stddeb, "midiInStop\n");
	return 0;
}

/**************************************************************************
 * 				midiInReset			[WINMM.68]
 */
UINT32 WINAPI midiInReset32(HMIDIIN32 hMidiIn)
{
	return midiInReset16(hMidiIn);
}

/**************************************************************************
 * 				midiInReset			[MMSYSTEM.311]
 */
UINT16 WINAPI midiInReset16(HMIDIIN16 hMidiIn)
{
	dprintf_mmsys(stddeb, "midiInReset\n");
	return 0;
}

/**************************************************************************
 * 				midiInGetID			[WINMM.63]
 */
UINT32 WINAPI midiInGetID32(HMIDIIN32 hMidiIn, UINT32 * lpuDeviceID)
{
	dprintf_mmsys(stddeb, "midiInGetID\n");
	return 0;
}

/**************************************************************************
 * 				midiInGetID			[MMSYSTEM.312]
 */
UINT16 WINAPI midiInGetID16(HMIDIIN16 hMidiIn, UINT16 * lpuDeviceID)
{
	dprintf_mmsys(stddeb, "midiInGetID\n");
	return 0;
}

/**************************************************************************
 * 				midiInMessage		[WINMM.65]
 */
DWORD WINAPI midiInMessage32(HMIDIIN32 hMidiIn, UINT32 uMessage, 
                             DWORD dwParam1, DWORD dwParam2)
{
	LPMIDIOPENDESC	lpDesc;
	dprintf_mmsys(stddeb, "midiInMessage(%04X, %04X, %08lX, %08lX)\n", 
			hMidiIn, uMessage, dwParam1, dwParam2);
	lpDesc = (LPMIDIOPENDESC) USER_HEAP_LIN_ADDR(hMidiIn);
	if (lpDesc == NULL) return MMSYSERR_INVALHANDLE;
	switch (uMessage) {
	case MIDM_OPEN:
		fprintf(stderr,"midiInMessage32: can't handle MIDM_OPEN!\n");
		return 0;
	case MIDM_GETDEVCAPS:
		return midiInGetDevCaps32A(hMidiIn,(LPMIDIINCAPS32A)dwParam1,dwParam2);
	case MIDM_GETNUMDEVS:
	case MIDM_RESET:
	case MIDM_STOP:
	case MIDM_START:
	case MIDM_CLOSE:
		/* no argument conversion needed */
		break;
	case MIDM_PREPARE:
		return midiInPrepareHeader32(hMidiIn,(LPMIDIHDR)dwParam1,dwParam2);
	case MIDM_UNPREPARE:
		return midiInUnprepareHeader32(hMidiIn,(LPMIDIHDR)dwParam1,dwParam2);
	case MIDM_ADDBUFFER:
		return midiInAddBuffer32(hMidiIn,(LPMIDIHDR)dwParam1,dwParam2);
	default:
		fprintf(stderr,"unhandled midiInMessage32(%04x,%04x,%08lx,%08lx)\n",
			hMidiIn,uMessage,dwParam1,dwParam2
		);
		break;
	}
	return midMessage(0, uMessage, lpDesc->dwInstance, dwParam1, dwParam2);
}

/**************************************************************************
 * 				midiInMessage		[MMSYSTEM.313]
 */
DWORD WINAPI midiInMessage16(HMIDIIN16 hMidiIn, UINT16 uMessage, 
                             DWORD dwParam1, DWORD dwParam2)
{
	LPMIDIOPENDESC	lpDesc;
	dprintf_mmsys(stddeb, "midiInMessage(%04X, %04X, %08lX, %08lX)\n", 
			hMidiIn, uMessage, dwParam1, dwParam2);
	lpDesc = (LPMIDIOPENDESC) USER_HEAP_LIN_ADDR(hMidiIn);
	if (lpDesc == NULL) return MMSYSERR_INVALHANDLE;
	switch (uMessage) {
	case MIDM_OPEN:
		fprintf(stderr,"midiInMessage16: can't handle MIDM_OPEN!\n");
		return 0;
	case MIDM_GETDEVCAPS:
		return midiInGetDevCaps16(hMidiIn,(LPMIDIINCAPS16)PTR_SEG_TO_LIN(dwParam1),dwParam2);
	case MIDM_GETNUMDEVS:
	case MIDM_RESET:
	case MIDM_STOP:
	case MIDM_START:
	case MIDM_CLOSE:
		/* no argument conversion needed */
		break;
	case MIDM_PREPARE:
		return midiInPrepareHeader16(hMidiIn,(LPMIDIHDR)PTR_SEG_TO_LIN(dwParam1),dwParam2);
	case MIDM_UNPREPARE:
		return midiInUnprepareHeader16(hMidiIn,(LPMIDIHDR)PTR_SEG_TO_LIN(dwParam1),dwParam2);
	case MIDM_ADDBUFFER:
		return midiInAddBuffer16(hMidiIn,(LPMIDIHDR)PTR_SEG_TO_LIN(dwParam1),dwParam2);
	default:
		fprintf(stderr,"unhandled midiInMessage16(%04x,%04x,%08lx,%08lx)\n",
			hMidiIn,uMessage,dwParam1,dwParam2
		);
		break;
	}
	return midMessage(0, uMessage, lpDesc->dwInstance, dwParam1, dwParam2);
}


/**************************************************************************
 * 				waveOutGetNumDevs		[MMSYSTEM.401]
 */
UINT32 WINAPI waveOutGetNumDevs32() {
	return waveOutGetNumDevs16();
}

/**************************************************************************
 * 				waveOutGetNumDevs		[WINMM.167]
 */
UINT16 WINAPI waveOutGetNumDevs16()
{
	UINT16	count = 0;
	dprintf_mmsys(stddeb, "waveOutGetNumDevs\n");
	count += wodMessage( MMSYSTEM_FirstDevID(), WODM_GETNUMDEVS, 0L, 0L, 0L);
	dprintf_mmsys(stddeb, "waveOutGetNumDevs return %u \n", count);
	return count;
}

/**************************************************************************
 * 				waveOutGetDevCaps		[MMSYSTEM.402]
 */
UINT16 WINAPI waveOutGetDevCaps16(UINT16 uDeviceID, WAVEOUTCAPS16 * lpCaps,
                                UINT16 uSize)
{
	if (uDeviceID > waveOutGetNumDevs16() - 1) return MMSYSERR_BADDEVICEID;
	if (uDeviceID == (UINT16)WAVE_MAPPER) return MMSYSERR_BADDEVICEID; /* FIXME: do we have a wave mapper ? */
	dprintf_mmsys(stddeb, "waveOutGetDevCaps\n");
	return wodMessage(uDeviceID, WODM_GETDEVCAPS, 0L, (DWORD)lpCaps, uSize);
}

/**************************************************************************
 * 				waveOutGetDevCapsA		[WINMM.162]
 */
UINT32 WINAPI waveOutGetDevCaps32A(UINT32 uDeviceID, LPWAVEOUTCAPS32A lpCaps,
                                UINT32 uSize)
{
	WAVEOUTCAPS16	woc16;
	UINT16 ret = waveOutGetDevCaps16(uDeviceID,&woc16,sizeof(woc16));
	
	lpCaps->wMid = woc16.wMid;
	lpCaps->wPid = woc16.wPid;
	lpCaps->vDriverVersion = woc16.vDriverVersion;
	strcpy(lpCaps->szPname,woc16.szPname);
	lpCaps->dwFormats = woc16.dwFormats;
	lpCaps->wChannels = woc16.wChannels;
	lpCaps->dwSupport = woc16.dwSupport;
	return ret;
}

/**************************************************************************
 * 				waveOutGetDevCapsW		[WINMM.163]
 */
UINT32 WINAPI waveOutGetDevCaps32W(UINT32 uDeviceID, LPWAVEOUTCAPS32W lpCaps,
                                UINT32 uSize)
{
	WAVEOUTCAPS16	woc16;
	UINT32 ret = waveOutGetDevCaps16(uDeviceID,&woc16,sizeof(woc16));

	lpCaps->wMid = woc16.wMid;
	lpCaps->wPid = woc16.wPid;
	lpCaps->vDriverVersion = woc16.vDriverVersion;
	lstrcpyAtoW(lpCaps->szPname,woc16.szPname);
	lpCaps->dwFormats = woc16.dwFormats;
	lpCaps->wChannels = woc16.wChannels;
	lpCaps->dwSupport = woc16.dwSupport;
	return ret;
}

/**************************************************************************
 * 				waveOutGetErrorText 	[MMSYSTEM.403]
 */
UINT16 WINAPI waveOutGetErrorText16(UINT16 uError, LPSTR lpText, UINT16 uSize)
{
	dprintf_mmsys(stddeb, "waveOutGetErrorText\n");
	return(waveGetErrorText(uError, lpText, uSize));
}

/**************************************************************************
 * 				waveOutGetErrorTextA 	[WINMM.164]
 */
UINT32 WINAPI waveOutGetErrorText32A(UINT32 uError, LPSTR lpText, UINT32 uSize)
{
	return(waveOutGetErrorText16(uError, lpText, uSize));
}

/**************************************************************************
 * 				waveOutGetErrorTextW 	[WINMM.165]
 */
UINT32 WINAPI waveOutGetErrorText32W(UINT32 uError, LPWSTR lpText, UINT32 uSize)
{
	LPSTR	xstr = HeapAlloc(GetProcessHeap(),0,uSize);
	UINT32	ret = waveOutGetErrorText32A(uError, xstr, uSize);
	
	lstrcpyAtoW(lpText,xstr);
	HeapFree(GetProcessHeap(),0,xstr);
	return ret;
}


/**************************************************************************
 * 				waveGetErrorText 		[internal]
 */
static UINT16 waveGetErrorText(UINT16 uError, LPSTR lpText, UINT16 uSize)
{
	LPSTR	msgptr;
	dprintf_mmsys(stddeb, "waveGetErrorText(%04X, %p, %d);\n", uError, lpText, uSize);
	if ((lpText == NULL) || (uSize < 1)) return(FALSE);
	lpText[0] = '\0';
	switch(uError) {
		case MMSYSERR_NOERROR:
			msgptr = "The specified command was carried out.";
			break;
		case MMSYSERR_ERROR:
			msgptr = "Undefined external error.";
			break;
		case MMSYSERR_BADDEVICEID:
			msgptr = "A device ID has been used that is out of range for your system.";
			break;
		case MMSYSERR_NOTENABLED:
			msgptr = "The driver was not enabled.";
			break;
		case MMSYSERR_ALLOCATED:
			msgptr = "The specified device is already in use. Wait until it is free, and then try again.";
			break;
		case MMSYSERR_INVALHANDLE:
			msgptr = "The specified device handle is invalid.";
			break;
		case MMSYSERR_NODRIVER:
			msgptr = "There is no driver installed on your system !\n";
			break;
		case MMSYSERR_NOMEM:
			msgptr = "Not enough memory available for this task. Quit one or more applications to increase available memory, and then try again.";
			break;
		case MMSYSERR_NOTSUPPORTED:
			msgptr = "This function is not supported. Use the Capabilities function to determine which functions and messages the driver supports.";
			break;
		case MMSYSERR_BADERRNUM:
			msgptr = "An error number was specified that is not defined in the system.";
			break;
		case MMSYSERR_INVALFLAG:
			msgptr = "An invalid flag was passed to a system function.";
			break;
		case MMSYSERR_INVALPARAM:
			msgptr = "An invalid parameter was passed to a system function.";
			break;
		case WAVERR_BADFORMAT:
			msgptr = "The specified format is not supported or cannot be translated. Use the Capabilities function to determine the supported formats";
			break;
		case WAVERR_STILLPLAYING:
			msgptr = "Cannot perform this operation while media data is still playing. Reset the device, or wait until the data is finished playing.";
			break;
		case WAVERR_UNPREPARED:
			msgptr = "The wave header was not prepared. Use the Prepare function to prepare the header, and then try again.";
			break;
		case WAVERR_SYNC:
			msgptr = "Cannot open the device without using the WAVE_ALLOWSYNC flag. Use the flag, and then try again.";
			break;
		default:
			msgptr = "Unknown MMSYSTEM Error !\n";
			break;
		}
	lstrcpyn32A(lpText, msgptr, uSize);
	return TRUE;
}

/**************************************************************************
 *			waveOutOpen			[WINMM.173]
 * All the args/structs have the same layout as the win16 equivalents
 */
UINT32 WINAPI waveOutOpen32(HWAVEOUT32 * lphWaveOut, UINT32 uDeviceID,
                            const LPWAVEFORMATEX lpFormat, DWORD dwCallback,
                            DWORD dwInstance, DWORD dwFlags)
{
	HWAVEOUT16	hwo16;
	UINT32	ret=waveOutOpen16(&hwo16,uDeviceID,lpFormat,dwCallback,dwInstance,dwFlags);
	if (lphWaveOut) *lphWaveOut=hwo16;
	return ret;
}
/**************************************************************************
 *			waveOutOpen			[MMSYSTEM.404]
 */
UINT16 WINAPI waveOutOpen16(HWAVEOUT16 * lphWaveOut, UINT16 uDeviceID,
                            const LPWAVEFORMATEX lpFormat, DWORD dwCallback,
                            DWORD dwInstance, DWORD dwFlags)
{
	HWAVEOUT16	hWaveOut;
	LPWAVEOPENDESC	lpDesc;
	DWORD		dwRet = 0;
	BOOL32		bMapperFlg = FALSE;

	dprintf_mmsys(stddeb, "waveOutOpen(%p, %d, %p, %08lX, %08lX, %08lX);\n", 
		lphWaveOut, uDeviceID, lpFormat, dwCallback, dwInstance, dwFlags);
	if (dwFlags & WAVE_FORMAT_QUERY)
		dprintf_mmsys(stddeb, "waveOutOpen	// WAVE_FORMAT_QUERY requested !\n");
	if (uDeviceID == (UINT16)WAVE_MAPPER) {
		dprintf_mmsys(stddeb, "waveOutOpen	// WAVE_MAPPER mode requested !\n");
		bMapperFlg = TRUE;
		uDeviceID = 0;
	}
	if (lpFormat == NULL) return WAVERR_BADFORMAT;

	hWaveOut = USER_HEAP_ALLOC(sizeof(WAVEOPENDESC));
	if (lphWaveOut != NULL) *lphWaveOut = hWaveOut;
	lpDesc = (LPWAVEOPENDESC) USER_HEAP_LIN_ADDR(hWaveOut);
	if (lpDesc == NULL) return MMSYSERR_NOMEM;
	lpDesc->hWave = hWaveOut;
	lpDesc->lpFormat = (LPWAVEFORMAT)lpFormat;  /* should the struct be copied iso pointer? */
	lpDesc->dwCallBack = dwCallback;
	lpDesc->dwInstance = dwInstance;
	if (uDeviceID >= MAXWAVEDRIVERS)
		uDeviceID = 0;
	while(uDeviceID < MAXWAVEDRIVERS) {
		dwRet = wodMessage(uDeviceID, WODM_OPEN, 
			lpDesc->dwInstance, (DWORD)lpDesc, dwFlags);
		if (dwRet == MMSYSERR_NOERROR) break;
		if (!bMapperFlg) break;
		uDeviceID++;
		dprintf_mmsys(stddeb, "waveOutOpen	// WAVE_MAPPER mode ! try next driver...\n");
	}
	lpDesc->uDeviceID = uDeviceID;  /* save physical Device ID */
	if (dwFlags & WAVE_FORMAT_QUERY) {
		dprintf_mmsys(stddeb, "waveOutOpen	// End of WAVE_FORMAT_QUERY !\n");
		dwRet = waveOutClose32(hWaveOut);
	}
	return dwRet;
}

/**************************************************************************
 * 				waveOutClose		[WINMM.161]
 */
UINT32 WINAPI waveOutClose32(HWAVEOUT32 hWaveOut)
{
	return waveOutClose16(hWaveOut);
}
/**************************************************************************
 * 				waveOutClose		[MMSYSTEM.405]
 */
UINT16 WINAPI waveOutClose16(HWAVEOUT16 hWaveOut)
{
	LPWAVEOPENDESC	lpDesc;

	dprintf_mmsys(stddeb, "waveOutClose(%04X)\n", hWaveOut);
	lpDesc = (LPWAVEOPENDESC) USER_HEAP_LIN_ADDR(hWaveOut);
	if (lpDesc == NULL) return MMSYSERR_INVALHANDLE;
	return wodMessage( lpDesc->uDeviceID, WODM_CLOSE, lpDesc->dwInstance, 0L, 0L);
}

/**************************************************************************
 * 				waveOutPrepareHeader	[WINMM.175]
 */
UINT32 WINAPI waveOutPrepareHeader32(HWAVEOUT32 hWaveOut,
                                   WAVEHDR * lpWaveOutHdr, UINT32 uSize)
{
	LPWAVEOPENDESC	lpDesc;

	dprintf_mmsys(stddeb, "waveOutPrepareHeader(%04X, %p, %u);\n", 
					hWaveOut, lpWaveOutHdr, uSize);
	lpDesc = (LPWAVEOPENDESC) USER_HEAP_LIN_ADDR(hWaveOut);
	if (lpDesc == NULL) return MMSYSERR_INVALHANDLE;
	return wodMessage( lpDesc->uDeviceID, WODM_PREPARE, lpDesc->dwInstance, 
			   (DWORD)lpWaveOutHdr,uSize);
}
/**************************************************************************
 * 				waveOutPrepareHeader	[MMSYSTEM.406]
 */
UINT16 WINAPI waveOutPrepareHeader16(HWAVEOUT16 hWaveOut,
                                     WAVEHDR * lpWaveOutHdr, UINT16 uSize)
{
	LPWAVEOPENDESC	lpDesc;
	LPBYTE		saveddata = lpWaveOutHdr->lpData;
	UINT16		ret;

	dprintf_mmsys(stddeb, "waveOutPrepareHeader(%04X, %p, %u);\n", 
					hWaveOut, lpWaveOutHdr, uSize);
	lpDesc = (LPWAVEOPENDESC) USER_HEAP_LIN_ADDR(hWaveOut);
	if (lpDesc == NULL) return MMSYSERR_INVALHANDLE;
	lpWaveOutHdr->lpData = PTR_SEG_TO_LIN(lpWaveOutHdr->lpData);
	ret = wodMessage( lpDesc->uDeviceID, WODM_PREPARE, lpDesc->dwInstance, 
			   (DWORD)lpWaveOutHdr,uSize);
	lpWaveOutHdr->lpData = saveddata;
	return ret;
}

/**************************************************************************
 * 				waveOutUnprepareHeader	[WINMM.181]
 */
UINT32 WINAPI waveOutUnprepareHeader32(HWAVEOUT32 hWaveOut,
                                       WAVEHDR * lpWaveOutHdr, UINT32 uSize)
{
	LPWAVEOPENDESC	lpDesc;

	dprintf_mmsys(stddeb, "waveOutUnprepareHeader(%04X, %p, %u);\n", 
						hWaveOut, lpWaveOutHdr, uSize);
	lpDesc = (LPWAVEOPENDESC) USER_HEAP_LIN_ADDR(hWaveOut);
	if (lpDesc == NULL) return MMSYSERR_INVALHANDLE;
	return wodMessage(lpDesc->uDeviceID,WODM_UNPREPARE,lpDesc->dwInstance, 
					(DWORD)lpWaveOutHdr, uSize);
}
/**************************************************************************
 * 				waveOutUnprepareHeader	[MMSYSTEM.407]
 */
UINT16 WINAPI waveOutUnprepareHeader16(HWAVEOUT16 hWaveOut,
                                     WAVEHDR * lpWaveOutHdr, UINT16 uSize)
{
	LPWAVEOPENDESC	lpDesc;
	LPBYTE		saveddata = lpWaveOutHdr->lpData;
	UINT16		ret;

	dprintf_mmsys(stddeb, "waveOutUnprepareHeader(%04X, %p, %u);\n", 
						hWaveOut, lpWaveOutHdr, uSize);
	lpDesc = (LPWAVEOPENDESC) USER_HEAP_LIN_ADDR(hWaveOut);
	if (lpDesc == NULL) return MMSYSERR_INVALHANDLE;
	lpWaveOutHdr->lpData = PTR_SEG_TO_LIN(lpWaveOutHdr->lpData);
	ret = wodMessage(lpDesc->uDeviceID,WODM_UNPREPARE,lpDesc->dwInstance, 
			  (DWORD)lpWaveOutHdr, uSize);
	lpWaveOutHdr->lpData = saveddata;
	return ret;
}

/**************************************************************************
 * 				waveOutWrite		[MMSYSTEM.408]
 */
UINT32 WINAPI waveOutWrite32(HWAVEOUT32 hWaveOut, WAVEHDR * lpWaveOutHdr,
                             UINT32 uSize)
{
	LPWAVEOPENDESC	lpDesc;
	dprintf_mmsys(stddeb, "waveOutWrite(%04X, %p, %u);\n", hWaveOut, lpWaveOutHdr, uSize);
	lpDesc = (LPWAVEOPENDESC) USER_HEAP_LIN_ADDR(hWaveOut);
	if (lpDesc == NULL) return MMSYSERR_INVALHANDLE;
	lpWaveOutHdr->reserved = (DWORD)lpWaveOutHdr->lpData;
	return wodMessage( lpDesc->uDeviceID, WODM_WRITE, lpDesc->dwInstance, (DWORD)lpWaveOutHdr, uSize);
}
/**************************************************************************
 * 				waveOutWrite		[MMSYSTEM.408]
 */
UINT16 WINAPI waveOutWrite16(HWAVEOUT16 hWaveOut, WAVEHDR * lpWaveOutHdr,
                           UINT16 uSize)
{
	LPWAVEOPENDESC	lpDesc;
	UINT16		ret;

	dprintf_mmsys(stddeb, "waveOutWrite(%04X, %p, %u);\n", hWaveOut, lpWaveOutHdr, uSize);
	lpDesc = (LPWAVEOPENDESC) USER_HEAP_LIN_ADDR(hWaveOut);
	if (lpDesc == NULL) return MMSYSERR_INVALHANDLE;
	lpWaveOutHdr->reserved=(DWORD)lpWaveOutHdr->lpData;/*save original ptr*/
	lpWaveOutHdr->lpData = PTR_SEG_TO_LIN(lpWaveOutHdr->lpData);
	ret = wodMessage( lpDesc->uDeviceID, WODM_WRITE, lpDesc->dwInstance, (DWORD)lpWaveOutHdr, uSize);
	lpWaveOutHdr->lpData = (LPBYTE)lpWaveOutHdr->reserved;
	return ret;
}

/**************************************************************************
 * 				waveOutPause		[WINMM.174]
 */
UINT32 WINAPI waveOutPause32(HWAVEOUT32 hWaveOut)
{
	return waveOutPause16(hWaveOut);
}

/**************************************************************************
 * 				waveOutPause		[MMSYSTEM.409]
 */
UINT16 WINAPI waveOutPause16(HWAVEOUT16 hWaveOut)
{
	LPWAVEOPENDESC	lpDesc;

	dprintf_mmsys(stddeb, "waveOutPause(%04X)\n", hWaveOut);
	lpDesc = (LPWAVEOPENDESC) USER_HEAP_LIN_ADDR(hWaveOut);
	if (lpDesc == NULL) return MMSYSERR_INVALHANDLE;
	return wodMessage( lpDesc->uDeviceID, WODM_PAUSE, lpDesc->dwInstance, 0L, 0L);
}

/**************************************************************************
 * 				waveOutRestart		[WINMM.177]
 */
UINT32 WINAPI waveOutRestart32(HWAVEOUT32 hWaveOut)
{
	return waveOutRestart16(hWaveOut);
}
/**************************************************************************
 * 				waveOutRestart		[MMSYSTEM.410]
 */
UINT16 WINAPI waveOutRestart16(HWAVEOUT16 hWaveOut)
{
	LPWAVEOPENDESC	lpDesc;

	dprintf_mmsys(stddeb, "waveOutRestart(%04X)\n", hWaveOut);
	lpDesc = (LPWAVEOPENDESC) USER_HEAP_LIN_ADDR(hWaveOut);
	if (lpDesc == NULL) return MMSYSERR_INVALHANDLE;
	return wodMessage( lpDesc->uDeviceID, WODM_RESTART, lpDesc->dwInstance, 0L, 0L);
}

/**************************************************************************
 * 				waveOutReset		[WINMM.176]
 */
UINT32 WINAPI waveOutReset32(HWAVEOUT32 hWaveOut)
{
	return waveOutReset16(hWaveOut);
}

/**************************************************************************
 * 				waveOutReset		[MMSYSTEM.411]
 */
UINT16 WINAPI waveOutReset16(HWAVEOUT16 hWaveOut)
{
	LPWAVEOPENDESC	lpDesc;
	dprintf_mmsys(stddeb, "waveOutReset(%04X)\n", hWaveOut);
	lpDesc = (LPWAVEOPENDESC) USER_HEAP_LIN_ADDR(hWaveOut);
	if (lpDesc == NULL) return MMSYSERR_INVALHANDLE;
	return wodMessage( lpDesc->uDeviceID, WODM_RESET, lpDesc->dwInstance, 0L, 0L);
}

/**************************************************************************
 * 				waveOutGetPosition	[WINMM.170]
 */
UINT32 WINAPI waveOutGetPosition32(HWAVEOUT32 hWaveOut, LPMMTIME32 lpTime,
                                   UINT32 uSize)
{
	MMTIME16	mmt16;
	UINT32 ret = waveOutGetPosition16(hWaveOut,&mmt16,sizeof(mmt16));
	MMSYSTEM_MMTIME16to32(lpTime,&mmt16);
	return ret;
}
/**************************************************************************
 * 				waveOutGetPosition	[MMSYSTEM.412]
 */
UINT16 WINAPI waveOutGetPosition16(HWAVEOUT16 hWaveOut,LPMMTIME16 lpTime,
                                   UINT16 uSize)
{
	LPWAVEOPENDESC	lpDesc;
	dprintf_mmsys(stddeb, "waveOutGetPosition(%04X, %p, %u);\n", hWaveOut, lpTime, uSize);
	lpDesc = (LPWAVEOPENDESC) USER_HEAP_LIN_ADDR(hWaveOut);
	if (lpDesc == NULL) return MMSYSERR_INVALHANDLE;
	return wodMessage( lpDesc->uDeviceID, WODM_GETPOS, lpDesc->dwInstance, 
							(DWORD)lpTime, (DWORD)uSize);
}

#define WAVEOUT_SHORTCUT_1(xx,XX,atype) \
	UINT32 WINAPI waveOut##xx##32(HWAVEOUT32 hWaveOut, atype x)	\
{									\
	return waveOut##xx##16(hWaveOut,x);				\
}									\
UINT16 WINAPI waveOut##xx##16(HWAVEOUT16 hWaveOut, atype x)		\
{									\
	LPWAVEOPENDESC	lpDesc;						\
	dprintf_mmsys(stddeb, "waveOut"#xx"(%04X, %08lx);\n", hWaveOut,(DWORD)x);\
	lpDesc = (LPWAVEOPENDESC) USER_HEAP_LIN_ADDR(hWaveOut);		\
	if (lpDesc == NULL) return MMSYSERR_INVALHANDLE;		\
	return wodMessage(lpDesc->uDeviceID, WODM_##XX, lpDesc->dwInstance,\
			  (DWORD)x, 0L);				\
}

WAVEOUT_SHORTCUT_1(GetPitch,GETPITCH,DWORD*)
WAVEOUT_SHORTCUT_1(SetPitch,SETPITCH,DWORD)
WAVEOUT_SHORTCUT_1(GetPlaybackRate,GETPLAYBACKRATE,DWORD*)
WAVEOUT_SHORTCUT_1(SetPlaybackRate,SETPLAYBACKRATE,DWORD)

#define WAVEOUT_SHORTCUT_2(xx,XX,atype) \
	UINT32 WINAPI waveOut##xx##32(UINT32 devid, atype x)		\
{									\
	return waveOut##xx##16(devid,x);				\
}									\
UINT16 WINAPI waveOut##xx##16(UINT16 devid, atype x)			\
{									\
	dprintf_mmsys(stddeb, "waveOut"#xx"(%04X, %08lx);\n", devid,(DWORD)x);	\
	return wodMessage(devid, WODM_##XX, 0L,	(DWORD)x, 0L);		\
}
	

WAVEOUT_SHORTCUT_2(GetVolume,GETVOLUME,DWORD*)
WAVEOUT_SHORTCUT_2(SetVolume,SETVOLUME,DWORD)


/**************************************************************************
 * 				waveOutBreakLoop 	[MMSYSTEM.419]
 */
UINT32 WINAPI waveOutBreakLoop32(HWAVEOUT32 hWaveOut)
{
	return waveOutBreakLoop16(hWaveOut);
}
/**************************************************************************
 * 				waveOutBreakLoop 	[MMSYSTEM.419]
 */
UINT16 WINAPI waveOutBreakLoop16(HWAVEOUT16 hWaveOut)
{
	dprintf_mmsys(stddeb, "waveOutBreakLoop(%04X)\n", hWaveOut);
	return MMSYSERR_INVALHANDLE;
}

/**************************************************************************
 * 				waveOutGetID	 	[MMSYSTEM.420]
 */
UINT32 WINAPI waveOutGetID32(HWAVEOUT32 hWaveOut, UINT32 * lpuDeviceID)
{
	LPWAVEOPENDESC	lpDesc;
	dprintf_mmsys(stddeb, "waveOutGetID(%04X, %p);\n", hWaveOut, lpuDeviceID);
	lpDesc = (LPWAVEOPENDESC) USER_HEAP_LIN_ADDR(hWaveOut);
	if (lpDesc == NULL) return MMSYSERR_INVALHANDLE;
	if (lpuDeviceID == NULL) return MMSYSERR_INVALHANDLE;
	*lpuDeviceID = lpDesc->uDeviceID;
        return 0;
}
/**************************************************************************
 * 				waveOutGetID	 	[MMSYSTEM.420]
 */
UINT16 WINAPI waveOutGetID16(HWAVEOUT16 hWaveOut, UINT16 * lpuDeviceID)
{
	LPWAVEOPENDESC	lpDesc;
	dprintf_mmsys(stddeb, "waveOutGetID(%04X, %p);\n", hWaveOut, lpuDeviceID);
	lpDesc = (LPWAVEOPENDESC) USER_HEAP_LIN_ADDR(hWaveOut);
	if (lpDesc == NULL) return MMSYSERR_INVALHANDLE;
	if (lpuDeviceID == NULL) return MMSYSERR_INVALHANDLE;
	*lpuDeviceID = lpDesc->uDeviceID;
        return 0;
}

/**************************************************************************
 * 				waveOutMessage 		[MMSYSTEM.421]
 */
DWORD WINAPI waveOutMessage32(HWAVEOUT32 hWaveOut, UINT32 uMessage, 
                              DWORD dwParam1, DWORD dwParam2)
{
	LPWAVEOPENDESC	lpDesc;

	lpDesc = (LPWAVEOPENDESC) USER_HEAP_LIN_ADDR(hWaveOut);
	if (lpDesc == NULL) return MMSYSERR_INVALHANDLE;
	switch (uMessage) {
	case WODM_GETNUMDEVS:
	case WODM_GETPOS:
	case WODM_GETVOLUME:
	case WODM_GETPITCH:
	case WODM_GETPLAYBACKRATE:
	case WODM_SETVOLUME:
	case WODM_SETPITCH:
	case WODM_SETPLAYBACKRATE:
	case WODM_RESET:
	case WODM_PAUSE:
	case WODM_PREPARE:
	case WODM_UNPREPARE:
	case WODM_STOP:
	case WODM_CLOSE:
		/* no argument conversion needed */
		break;
	case WODM_WRITE:
		return waveOutWrite32(hWaveOut,(LPWAVEHDR)dwParam1,dwParam2);
	case WODM_GETDEVCAPS:
		/* FIXME: UNICODE/ANSI? */
		return waveOutGetDevCaps32A(hWaveOut,(LPWAVEOUTCAPS32A)dwParam1,dwParam2);
	case WODM_OPEN:
		fprintf(stderr,"waveOutMessage32 can't handle WODM_OPEN, please report.\n");
		break;
	default:
		fprintf(stderr,"unhandled waveOutMessage32(0x%04x,0x%04x,%08lx,%08lx)\n",
			hWaveOut,uMessage,dwParam1,dwParam2
		);
		break;
	}
	return wodMessage( lpDesc->uDeviceID, uMessage, lpDesc->dwInstance, dwParam1, dwParam2);
}

/**************************************************************************
 * 				waveOutMessage 		[MMSYSTEM.421]
 */
DWORD WINAPI waveOutMessage16(HWAVEOUT16 hWaveOut, UINT16 uMessage, 
                              DWORD dwParam1, DWORD dwParam2)
{
	LPWAVEOPENDESC	lpDesc;

	lpDesc = (LPWAVEOPENDESC) USER_HEAP_LIN_ADDR(hWaveOut);
	if (lpDesc == NULL) return MMSYSERR_INVALHANDLE;
	switch (uMessage) {
	case WODM_GETNUMDEVS:
	case WODM_SETVOLUME:
	case WODM_SETPITCH:
	case WODM_SETPLAYBACKRATE:
	case WODM_RESET:
	case WODM_PAUSE:
	case WODM_STOP:
	case WODM_CLOSE:
		/* no argument conversion needed */
		break;
	case WODM_GETPOS:
		return waveOutGetPosition16(hWaveOut,(LPMMTIME16)PTR_SEG_TO_LIN(dwParam1),dwParam2);
	case WODM_GETVOLUME:
		return waveOutGetVolume16(hWaveOut,(LPDWORD)PTR_SEG_TO_LIN(dwParam1));
	case WODM_GETPITCH:
		return waveOutGetPitch16(hWaveOut,(LPDWORD)PTR_SEG_TO_LIN(dwParam1));
	case WODM_GETPLAYBACKRATE:
		return waveOutGetPlaybackRate16(hWaveOut,(LPDWORD)PTR_SEG_TO_LIN(dwParam1));
	case WODM_GETDEVCAPS:
		return waveOutGetDevCaps16(hWaveOut,(LPWAVEOUTCAPS16)PTR_SEG_TO_LIN(dwParam1),dwParam2);
	case WODM_PREPARE:
		return waveOutPrepareHeader16(hWaveOut,(LPWAVEHDR)PTR_SEG_TO_LIN(dwParam1),dwParam2);
	case WODM_UNPREPARE:
		return waveOutUnprepareHeader16(hWaveOut,(LPWAVEHDR)PTR_SEG_TO_LIN(dwParam1),dwParam2);
	case WODM_WRITE:
		return waveOutWrite16(hWaveOut,(LPWAVEHDR)PTR_SEG_TO_LIN(dwParam1),dwParam2);
	case WODM_OPEN:
		fprintf(stderr,"waveOutMessage16 can't handle WODM_OPEN, please report.\n");
		break;
	default:
		fprintf(stderr,"unhandled waveOutMessage16(0x%04x,0x%04x,%08lx,%08lx)\n",
			hWaveOut,uMessage,dwParam1,dwParam2
		);
	}
	return wodMessage( lpDesc->uDeviceID, uMessage, lpDesc->dwInstance, dwParam1, dwParam2);
}

/**************************************************************************
 * 				waveInGetNumDevs 		[WINMM.151]
 */
UINT32 WINAPI waveInGetNumDevs32()
{
	return waveInGetNumDevs16();
}

/**************************************************************************
 * 				waveInGetNumDevs 		[MMSYSTEM.501]
 */
UINT16 WINAPI waveInGetNumDevs16()
{
	UINT16	count = 0;
	dprintf_mmsys(stddeb, "waveInGetNumDevs\n");
	count += widMessage(0, WIDM_GETNUMDEVS, 0L, 0L, 0L);
	dprintf_mmsys(stddeb, "waveInGetNumDevs return %u \n", count);
	return count;
}

/**************************************************************************
 * 				waveInGetDevCapsA 		[WINMM.147]
 */
UINT32 WINAPI waveInGetDevCaps32W(UINT32 uDeviceID, LPWAVEINCAPS32W lpCaps, UINT32 uSize)
{
	WAVEINCAPS16	wic16;
	UINT32	ret = waveInGetDevCaps16(uDeviceID,&wic16,uSize);

	lpCaps->wMid = wic16.wMid;
	lpCaps->wPid = wic16.wPid;
	lpCaps->vDriverVersion = wic16.vDriverVersion;
	lstrcpyAtoW(lpCaps->szPname,wic16.szPname);
	lpCaps->dwFormats = wic16.dwFormats;
	lpCaps->wChannels = wic16.wChannels;

	return ret;
}
/**************************************************************************
 * 				waveInGetDevCapsA 		[WINMM.146]
 */
UINT32 WINAPI waveInGetDevCaps32A(UINT32 uDeviceID, LPWAVEINCAPS32A lpCaps, UINT32 uSize)
{
	WAVEINCAPS16	wic16;
	UINT32	ret = waveInGetDevCaps16(uDeviceID,&wic16,uSize);
	
	lpCaps->wMid = wic16.wMid;
	lpCaps->wPid = wic16.wPid;
	lpCaps->vDriverVersion = wic16.vDriverVersion;
	strcpy(lpCaps->szPname,wic16.szPname);
	lpCaps->dwFormats = wic16.dwFormats;
	lpCaps->wChannels = wic16.wChannels;
	return ret;
}
/**************************************************************************
 * 				waveInGetDevCaps 		[MMSYSTEM.502]
 */
UINT16 WINAPI waveInGetDevCaps16(UINT16 uDeviceID, LPWAVEINCAPS16 lpCaps, UINT16 uSize)
{
	dprintf_mmsys(stddeb, "waveInGetDevCaps\n");
	return widMessage(uDeviceID, WIDM_GETDEVCAPS, 0L, (DWORD)lpCaps, uSize);
}

/**************************************************************************
 * 				waveInGetErrorTextA 	[WINMM.148]
 */
UINT32 WINAPI waveInGetErrorText32A(UINT32 uError, LPSTR lpText, UINT32 uSize)
{
   dprintf_mmsys(stddeb, "waveInGetErrorText\n");
   return(waveGetErrorText(uError, lpText, uSize));
}

/**************************************************************************
 * 				waveInGetErrorTextW 	[WINMM.149]
 */
UINT32 WINAPI waveInGetErrorText32W(UINT32 uError, LPWSTR lpText, UINT32 uSize)
{
	LPSTR txt = HeapAlloc(GetProcessHeap(),0,uSize);
	UINT32	ret = waveGetErrorText(uError, txt, uSize);

	lstrcpyAtoW(lpText,txt);
	HeapFree(GetProcessHeap(),0,txt);
	return ret;
}

/**************************************************************************
 * 				waveInGetErrorText 	[MMSYSTEM.503]
 */
UINT16 WINAPI waveInGetErrorText16(UINT16 uError, LPSTR lpText, UINT16 uSize)
{
   dprintf_mmsys(stddeb, "waveInGetErrorText\n");
   return(waveGetErrorText(uError, lpText, uSize));
}


/**************************************************************************
 * 				waveInOpen			[WINMM.154]
 */
UINT32 WINAPI waveInOpen32(HWAVEIN32 * lphWaveIn, UINT32 uDeviceID,
                           const LPWAVEFORMAT lpFormat, DWORD dwCallback,
                           DWORD dwInstance, DWORD dwFlags)
{
	HWAVEIN16	hwin16;
	UINT32	ret=waveInOpen16(&hwin16,uDeviceID,lpFormat,dwCallback,dwInstance,dwFlags);
	if (lphWaveIn) *lphWaveIn = hwin16;
	return ret;
}

/**************************************************************************
 * 				waveInOpen			[MMSYSTEM.504]
 */
UINT16 WINAPI waveInOpen16(HWAVEIN16 * lphWaveIn, UINT16 uDeviceID,
                           const LPWAVEFORMAT lpFormat, DWORD dwCallback,
                           DWORD dwInstance, DWORD dwFlags)
{
	HWAVEIN16 hWaveIn;
	LPWAVEOPENDESC	lpDesc;
	DWORD	dwRet = 0;
	BOOL32	bMapperFlg = FALSE;
	dprintf_mmsys(stddeb, "waveInOpen(%p, %d, %p, %08lX, %08lX, %08lX);\n", 
		lphWaveIn, uDeviceID, lpFormat, dwCallback, dwInstance, dwFlags);
	if (dwFlags & WAVE_FORMAT_QUERY)
		dprintf_mmsys(stddeb, "waveInOpen // WAVE_FORMAT_QUERY requested !\n");
	if (uDeviceID == (UINT16)WAVE_MAPPER) {
		dprintf_mmsys(stddeb, "waveInOpen	// WAVE_MAPPER mode requested !\n");
		bMapperFlg = TRUE;
		uDeviceID = 0;
	}
	if (lpFormat == NULL) return WAVERR_BADFORMAT;
	hWaveIn = USER_HEAP_ALLOC(sizeof(WAVEOPENDESC));
	if (lphWaveIn != NULL) *lphWaveIn = hWaveIn;
	lpDesc = (LPWAVEOPENDESC) USER_HEAP_LIN_ADDR(hWaveIn);
	if (lpDesc == NULL) return MMSYSERR_NOMEM;
	lpDesc->hWave = hWaveIn;
	lpDesc->lpFormat = lpFormat;
	lpDesc->dwCallBack = dwCallback;
	lpDesc->dwInstance = dwInstance;
	while(uDeviceID < MAXWAVEDRIVERS) {
		dwRet = widMessage(uDeviceID, WIDM_OPEN, 
			lpDesc->dwInstance, (DWORD)lpDesc, 0L);
		if (dwRet == MMSYSERR_NOERROR) break;
		if (!bMapperFlg) break;
		uDeviceID++;
		dprintf_mmsys(stddeb, "waveInOpen	// WAVE_MAPPER mode ! try next driver...\n");
	}
	lpDesc->uDeviceID = uDeviceID;
	if (dwFlags & WAVE_FORMAT_QUERY) {
		dprintf_mmsys(stddeb, "waveInOpen	// End of WAVE_FORMAT_QUERY !\n");
		dwRet = waveInClose16(hWaveIn);
	}
	return dwRet;
}

/**************************************************************************
 * 				waveInClose			[WINMM.145]
 */
UINT32 WINAPI waveInClose32(HWAVEIN32 hWaveIn)
{
	return waveInClose16(hWaveIn);
}
/**************************************************************************
 * 				waveInClose			[MMSYSTEM.505]
 */
UINT16 WINAPI waveInClose16(HWAVEIN16 hWaveIn)
{
	LPWAVEOPENDESC	lpDesc;

	dprintf_mmsys(stddeb, "waveInClose(%04X)\n", hWaveIn);
	lpDesc = (LPWAVEOPENDESC) USER_HEAP_LIN_ADDR(hWaveIn);
	if (lpDesc == NULL) return MMSYSERR_INVALHANDLE;
	return widMessage(lpDesc->uDeviceID, WIDM_CLOSE, lpDesc->dwInstance, 0L, 0L);
}

/**************************************************************************
 * 				waveInPrepareHeader		[WINMM.155]
 */
UINT32 WINAPI waveInPrepareHeader32(HWAVEIN32 hWaveIn,
                                  WAVEHDR * lpWaveInHdr, UINT32 uSize)
{
	LPWAVEOPENDESC	lpDesc;

	dprintf_mmsys(stddeb, "waveInPrepareHeader(%04X, %p, %u);\n", 
					hWaveIn, lpWaveInHdr, uSize);
	lpDesc = (LPWAVEOPENDESC) USER_HEAP_LIN_ADDR(hWaveIn);
	if (lpDesc == NULL) return MMSYSERR_INVALHANDLE;
	if (lpWaveInHdr == NULL) return MMSYSERR_INVALHANDLE;
	lpWaveInHdr = lpWaveInHdr;
	lpWaveInHdr->lpNext = NULL;
    	lpWaveInHdr->dwBytesRecorded = 0;
	dprintf_mmsys(stddeb, "waveInPrepareHeader // lpData=%p size=%lu \n", 
		lpWaveInHdr->lpData, lpWaveInHdr->dwBufferLength);
	return widMessage(lpDesc->uDeviceID,WIDM_PREPARE,lpDesc->dwInstance, 
			  (DWORD)lpWaveInHdr, uSize);
}
/**************************************************************************
 * 				waveInPrepareHeader		[MMSYSTEM.506]
 */
UINT16 WINAPI waveInPrepareHeader16(HWAVEIN16 hWaveIn,
                                  WAVEHDR * lpWaveInHdr, UINT16 uSize)
{
	LPWAVEOPENDESC	lpDesc;
	LPBYTE		saveddata = lpWaveInHdr->lpData;
	UINT16		ret;

	dprintf_mmsys(stddeb, "waveInPrepareHeader(%04X, %p, %u);\n", 
					hWaveIn, lpWaveInHdr, uSize);
	lpDesc = (LPWAVEOPENDESC) USER_HEAP_LIN_ADDR(hWaveIn);
	if (lpDesc == NULL) return MMSYSERR_INVALHANDLE;
	if (lpWaveInHdr == NULL) return MMSYSERR_INVALHANDLE;
	lpWaveInHdr = lpWaveInHdr;
	lpWaveInHdr->lpNext = NULL;
    	lpWaveInHdr->dwBytesRecorded = 0;

	dprintf_mmsys(stddeb, "waveInPrepareHeader // lpData=%p size=%lu \n", 
		lpWaveInHdr->lpData, lpWaveInHdr->dwBufferLength);
	lpWaveInHdr->lpData = PTR_SEG_TO_LIN(lpWaveInHdr->lpData);
	ret = widMessage(lpDesc->uDeviceID,WIDM_PREPARE,lpDesc->dwInstance, 
			  (DWORD)lpWaveInHdr,uSize);
	lpWaveInHdr->lpData = saveddata;
	return ret;
}


/**************************************************************************
 * 				waveInUnprepareHeader	[WINMM.159]
 */
UINT32 WINAPI waveInUnprepareHeader32(HWAVEIN32 hWaveIn,
                                      WAVEHDR * lpWaveInHdr, UINT32 uSize)
{
	LPWAVEOPENDESC	lpDesc;

	dprintf_mmsys(stddeb, "waveInUnprepareHeader(%04X, %p, %u);\n", 
						hWaveIn, lpWaveInHdr, uSize);
	lpDesc = (LPWAVEOPENDESC) USER_HEAP_LIN_ADDR(hWaveIn);
	if (lpDesc == NULL) return MMSYSERR_INVALHANDLE;
	if (lpWaveInHdr == NULL) return MMSYSERR_INVALHANDLE;
	/*USER_HEAP_FREE(HIWORD((DWORD)lpWaveInHdr->lpData)); FIXME */
	lpWaveInHdr->lpData = NULL;
	lpWaveInHdr->lpNext = NULL;
	return widMessage(lpDesc->uDeviceID,WIDM_UNPREPARE,lpDesc->dwInstance, 
			  (DWORD)lpWaveInHdr, uSize);
}
/**************************************************************************
 * 				waveInUnprepareHeader	[MMSYSTEM.507]
 */
UINT16 WINAPI waveInUnprepareHeader16(HWAVEIN16 hWaveIn,
                                      WAVEHDR * lpWaveInHdr, UINT16 uSize)
{
	LPWAVEOPENDESC	lpDesc;

	dprintf_mmsys(stddeb, "waveInUnprepareHeader(%04X, %p, %u);\n", 
						hWaveIn, lpWaveInHdr, uSize);
	lpDesc = (LPWAVEOPENDESC) USER_HEAP_LIN_ADDR(hWaveIn);
	if (lpDesc == NULL) return MMSYSERR_INVALHANDLE;
	if (lpWaveInHdr == NULL) return MMSYSERR_INVALHANDLE;
	/*USER_HEAP_FREE(HIWORD((DWORD)lpWaveInHdr->lpData)); FIXME */
	lpWaveInHdr->lpData = NULL;
	lpWaveInHdr->lpNext = NULL;
	return widMessage(lpDesc->uDeviceID,WIDM_UNPREPARE,lpDesc->dwInstance, 
			  (DWORD)lpWaveInHdr, uSize);
}

/**************************************************************************
 * 				waveInAddBuffer		[WINMM.144]
 */
UINT32 WINAPI waveInAddBuffer32(HWAVEIN32 hWaveIn,
                                WAVEHDR * lpWaveInHdr, UINT32 uSize)
{
	LPWAVEOPENDESC	lpDesc;

	dprintf_mmsys(stddeb, "waveInAddBuffer(%04X, %p, %u);\n", hWaveIn, lpWaveInHdr, uSize);
	lpDesc = (LPWAVEOPENDESC) USER_HEAP_LIN_ADDR(hWaveIn);
	if (lpDesc == NULL) return MMSYSERR_INVALHANDLE;
	if (lpWaveInHdr == NULL) return MMSYSERR_INVALHANDLE;
	lpWaveInHdr->lpNext = NULL;
	lpWaveInHdr->dwBytesRecorded = 0;
	dprintf_mmsys(stddeb, "waveInAddBuffer // lpData=%p size=%lu \n", 
		lpWaveInHdr->lpData, lpWaveInHdr->dwBufferLength);
	return widMessage(lpDesc->uDeviceID, WIDM_ADDBUFFER, lpDesc->dwInstance,
								(DWORD)lpWaveInHdr, uSize);
	
}

/**************************************************************************
 * 				waveInAddBuffer		[MMSYSTEM.508]
 */
UINT16 WINAPI waveInAddBuffer16(HWAVEIN16 hWaveIn,
                                WAVEHDR * lpWaveInHdr, UINT16 uSize)
{
	LPWAVEOPENDESC	lpDesc;
	UINT16		ret;

	dprintf_mmsys(stddeb, "waveInAddBuffer(%04X, %p, %u);\n", hWaveIn, lpWaveInHdr, uSize);
	lpDesc = (LPWAVEOPENDESC) USER_HEAP_LIN_ADDR(hWaveIn);
	if (lpDesc == NULL) return MMSYSERR_INVALHANDLE;
	if (lpWaveInHdr == NULL) return MMSYSERR_INVALHANDLE;
	lpWaveInHdr->lpNext = NULL;
	lpWaveInHdr->dwBytesRecorded = 0;
	lpWaveInHdr->lpData = PTR_SEG_TO_LIN(lpWaveInHdr->lpData);
	dprintf_mmsys(stddeb, "waveInAddBuffer // lpData=%p size=%lu \n", 
		lpWaveInHdr->lpData, lpWaveInHdr->dwBufferLength);
	ret = widMessage(lpDesc->uDeviceID, WIDM_ADDBUFFER, lpDesc->dwInstance,
			  (DWORD)lpWaveInHdr, uSize);
	/*lpWaveInHdr->lpData = saveddata;*/
	return ret;
}

/**************************************************************************
 * 				waveInStart			[WINMM.157]
 */
UINT32 WINAPI waveInStart32(HWAVEIN32 hWaveIn)
{
	return waveInStart16(hWaveIn);
}

/**************************************************************************
 * 				waveInStart			[MMSYSTEM.509]
 */
UINT16 WINAPI waveInStart16(HWAVEIN16 hWaveIn)
{
	LPWAVEOPENDESC	lpDesc;

	dprintf_mmsys(stddeb, "waveInStart(%04X)\n", hWaveIn);
	lpDesc = (LPWAVEOPENDESC) USER_HEAP_LIN_ADDR(hWaveIn);
	if (lpDesc == NULL) return MMSYSERR_INVALHANDLE;
	return widMessage(lpDesc->uDeviceID,WIDM_START,lpDesc->dwInstance,0,0);
}

/**************************************************************************
 * 				waveInStop			[WINMM.158]
 */
UINT32 WINAPI waveInStop32(HWAVEIN32 hWaveIn)
{
	return waveInStop16(hWaveIn);
}

/**************************************************************************
 * 				waveInStop			[MMSYSTEM.510]
 */
UINT16 WINAPI waveInStop16(HWAVEIN16 hWaveIn)
{
	LPWAVEOPENDESC	lpDesc;

	dprintf_mmsys(stddeb, "waveInStop(%04X)\n", hWaveIn);
	lpDesc = (LPWAVEOPENDESC) USER_HEAP_LIN_ADDR(hWaveIn);
	if (lpDesc == NULL) return MMSYSERR_INVALHANDLE;
	return widMessage(lpDesc->uDeviceID, WIDM_STOP, lpDesc->dwInstance, 0L, 0L);
}

/**************************************************************************
 * 				waveInReset			[WINMM.156]
 */
UINT32 WINAPI waveInReset32(HWAVEIN32 hWaveIn)
{
	return waveInReset16(hWaveIn);
}

/**************************************************************************
 * 				waveInReset			[MMSYSTEM.511]
 */
UINT16 WINAPI waveInReset16(HWAVEIN16 hWaveIn)
{
	LPWAVEOPENDESC	lpDesc;

	dprintf_mmsys(stddeb, "waveInReset(%04X)\n", hWaveIn);
	lpDesc = (LPWAVEOPENDESC) USER_HEAP_LIN_ADDR(hWaveIn);
	if (lpDesc == NULL) return MMSYSERR_INVALHANDLE;
	return widMessage(lpDesc->uDeviceID,WIDM_RESET,lpDesc->dwInstance,0,0);
}

/**************************************************************************
 * 				waveInGetPosition	[WINMM.152]
 */
UINT32 WINAPI waveInGetPosition32(HWAVEIN32 hWaveIn, LPMMTIME32 lpTime,
                                  UINT32 uSize)
{
	MMTIME16 mmt16;
	UINT32	ret = waveInGetPosition16(hWaveIn,&mmt16,uSize);

	MMSYSTEM_MMTIME16to32(lpTime,&mmt16);
	return ret;
}

/**************************************************************************
 * 				waveInGetPosition	[MMSYSTEM.512]
 */
UINT16 WINAPI waveInGetPosition16(HWAVEIN16 hWaveIn,LPMMTIME16 lpTime,
                                  UINT16 uSize)
{
	LPWAVEOPENDESC	lpDesc;

	dprintf_mmsys(stddeb, "waveInGetPosition(%04X, %p, %u);\n", hWaveIn, lpTime, uSize);
	lpDesc = (LPWAVEOPENDESC) USER_HEAP_LIN_ADDR(hWaveIn);
	if (lpDesc == NULL) return MMSYSERR_INVALHANDLE;
	return widMessage(lpDesc->uDeviceID, WIDM_GETPOS, lpDesc->dwInstance,
			  (DWORD)lpTime, (DWORD)uSize);
}

/**************************************************************************
 * 				waveInGetID			[WINMM.150]
 */
UINT32 WINAPI waveInGetID32(HWAVEIN32 hWaveIn, UINT32 * lpuDeviceID)
{
	LPWAVEOPENDESC	lpDesc;

	dprintf_mmsys(stddeb, "waveInGetID\n");
	if (lpuDeviceID == NULL) return MMSYSERR_INVALHANDLE;
	lpDesc = (LPWAVEOPENDESC) USER_HEAP_LIN_ADDR(hWaveIn);
	if (lpDesc == NULL) return MMSYSERR_INVALHANDLE;
	*lpuDeviceID = lpDesc->uDeviceID;
	return 0;
}


/**************************************************************************
 * 				waveInGetID			[MMSYSTEM.513]
 */
UINT16 WINAPI waveInGetID16(HWAVEIN16 hWaveIn, UINT16 * lpuDeviceID)
{
	LPWAVEOPENDESC	lpDesc;

	dprintf_mmsys(stddeb, "waveInGetID\n");
	if (lpuDeviceID == NULL) return MMSYSERR_INVALHANDLE;
	lpDesc = (LPWAVEOPENDESC) USER_HEAP_LIN_ADDR(hWaveIn);
	if (lpDesc == NULL) return MMSYSERR_INVALHANDLE;
	*lpuDeviceID = lpDesc->uDeviceID;
	return 0;
}

/**************************************************************************
 * 				waveInMessage 		[WINMM.153]
 */
DWORD WINAPI waveInMessage32(HWAVEIN32 hWaveIn, UINT32 uMessage,
                             DWORD dwParam1, DWORD dwParam2)
{
	LPWAVEOPENDESC	lpDesc;

	fprintf(stderr, "waveInMessage32(%04X, %04X, %08lX, %08lX),FIXME!\n", 
			hWaveIn, uMessage, dwParam1, dwParam2);
	lpDesc = (LPWAVEOPENDESC) USER_HEAP_LIN_ADDR(hWaveIn);
	if (lpDesc == NULL) return MMSYSERR_INVALHANDLE;
	switch (uMessage) {
	case WIDM_OPEN:
		fprintf(stderr,"waveInMessage32: cannot handle WIDM_OPEN, please report.\n");
		break;
	case WIDM_GETNUMDEVS:
	case WIDM_GETPOS:
	case WIDM_CLOSE:
	case WIDM_STOP :
	case WIDM_RESET:
	case WIDM_START:
	case WIDM_PREPARE:
	case WIDM_UNPREPARE:
	case WIDM_ADDBUFFER:
	case WIDM_PAUSE:
		/* no argument conversion needed */
		break;
	case WIDM_GETDEVCAPS:
		/*FIXME: ANSI/UNICODE */
		return waveInGetDevCaps32A(hWaveIn,(LPWAVEINCAPS32A)dwParam1,dwParam2);
	default:
		fprintf(stderr,"unhandled waveInMessage32(%04x,%04x,%08lx,%08lx)\n",hWaveIn,uMessage,dwParam1,dwParam2);
		break;
	}
	return widMessage(lpDesc->uDeviceID, uMessage, lpDesc->dwInstance, dwParam1, dwParam2);
}

/**************************************************************************
 * 				waveInMessage 		[MMSYSTEM.514]
 */
DWORD WINAPI waveInMessage16(HWAVEIN16 hWaveIn, UINT16 uMessage,
                             DWORD dwParam1, DWORD dwParam2)
{
	LPWAVEOPENDESC	lpDesc;

	fprintf(stderr, "waveInMessage(%04X, %04X, %08lX, %08lX),FIXME!\n", 
			hWaveIn, uMessage, dwParam1, dwParam2);
	lpDesc = (LPWAVEOPENDESC) USER_HEAP_LIN_ADDR(hWaveIn);
	if (lpDesc == NULL) return MMSYSERR_INVALHANDLE;
	switch (uMessage) {
	case WIDM_OPEN:
		fprintf(stderr,"waveInMessage16: cannot handle WIDM_OPEN, please report.\n");
		break;
	case WIDM_GETNUMDEVS:
	case WIDM_CLOSE:
	case WIDM_STOP :
	case WIDM_RESET:
	case WIDM_START:
	case WIDM_PAUSE:
		/* no argument conversion needed */
		break;
	case WIDM_GETDEVCAPS:
		return waveInGetDevCaps16(hWaveIn,(LPWAVEINCAPS16)PTR_SEG_TO_LIN(dwParam1),dwParam2);
	case WIDM_GETPOS:
		return waveInGetPosition16(hWaveIn,(LPMMTIME16)PTR_SEG_TO_LIN(dwParam1),dwParam2);
	case WIDM_PREPARE:
		return waveInPrepareHeader16(hWaveIn,(LPWAVEHDR)PTR_SEG_TO_LIN(dwParam1),dwParam2);
	case WIDM_UNPREPARE:
		return waveInUnprepareHeader16(hWaveIn,(LPWAVEHDR)PTR_SEG_TO_LIN(dwParam1),dwParam2);
	case WIDM_ADDBUFFER:
		return waveInAddBuffer16(hWaveIn,(LPWAVEHDR)PTR_SEG_TO_LIN(dwParam1),dwParam2);
	default:
		fprintf(stderr,"unhandled waveInMessage16(%04x,%04x,%08lx,%08lx)\n",hWaveIn,uMessage,dwParam1,dwParam2);
		break;
	}
	return widMessage(lpDesc->uDeviceID, uMessage, lpDesc->dwInstance, dwParam1, dwParam2);
}

/**************************************************************************
* 				DrvOpen	       		[MMSYSTEM.1100]
*/
HDRVR16 WINAPI DrvOpen(LPSTR lpDriverName, LPSTR lpSectionName, LPARAM lParam)
{
	dprintf_mmsys(stddeb, "DrvOpen('%s', '%s', %08lX);\n",
		lpDriverName, lpSectionName, lParam);
	return OpenDriver(lpDriverName, lpSectionName, lParam);
}


/**************************************************************************
* 				DrvClose       		[MMSYSTEM.1101]
*/
LRESULT WINAPI DrvClose(HDRVR16 hDrvr, LPARAM lParam1, LPARAM lParam2)
{
	dprintf_mmsys(stddeb, "DrvClose(%04X, %08lX, %08lX);\n", hDrvr, lParam1, lParam2);
	return CloseDriver(hDrvr, lParam1, lParam2);
}


/**************************************************************************
* 				DrvSendMessage		[MMSYSTEM.1102]
*/
LRESULT WINAPI DrvSendMessage(HDRVR16 hDriver, WORD msg, LPARAM lParam1,
                              LPARAM lParam2)
{
	DWORD 	dwDriverID = 0;
	dprintf_mmsys(stddeb, "DrvSendMessage(%04X, %04X, %08lX, %08lX);\n",
					hDriver, msg, lParam1, lParam2);
	return CDAUDIO_DriverProc(dwDriverID, hDriver, msg, lParam1, lParam2);
}

/**************************************************************************
* 				DrvGetModuleHandle	[MMSYSTEM.1103]
*/
HANDLE16 WINAPI DrvGetModuleHandle(HDRVR16 hDrvr)
{
	dprintf_mmsys(stddeb, "DrvGetModuleHandle(%04X);\n", hDrvr);
        return 0;
}


/**************************************************************************
 * 				DrvDefDriverProc	[MMSYSTEM.1104]
 */
LRESULT WINAPI DrvDefDriverProc(DWORD dwDriverID, HDRVR16 hDriv, WORD wMsg, 
                                DWORD dwParam1, DWORD dwParam2)
{
	return DefDriverProc(dwDriverID, hDriv, wMsg, dwParam1, dwParam2);
}

/**************************************************************************
 * 				mmThreadCreate		[MMSYSTEM.1120]
 */
LRESULT WINAPI mmThreadCreate16(LPVOID x1, LPWORD x2, DWORD x3, DWORD x4) {
	fprintf(stderr,"mmThreadCreate16(%p,%p,%08lx,%08lx),stub!\n",
		x1,x2,x3,x4
	);
	*x2 = 0xbabe;
	return 0;
}

/**************************************************************************
 * 				mmThreadGetTask		[MMSYSTEM.1125]
 */
LRESULT WINAPI mmThreadGetTask16(WORD hnd) {
	fprintf(stderr,"mmThreadGetTask16(%04x),stub!\n",hnd);
	return GetCurrentTask();
}

/**************************************************************************
 * 				mmThreadSignal		[MMSYSTEM.1121]
 */
LRESULT WINAPI mmThreadSignal16(WORD hnd) {
	fprintf(stderr,"mmThreadSignal16(%04x), stub!\n",hnd);
	return 0;
}

/**************************************************************************
 * 				mmTaskCreate		[MMSYSTEM.900]
 */
LRESULT WINAPI mmTaskCreate16(LPWORD lphnd,DWORD x1,DWORD x2) {
	fprintf(stderr,"mmTaskCreate16(%p,%08lx,%08lx),stub!\n",lphnd,x1,x2);
	*lphnd = 0xcafe;
	return 0;
}

/**************************************************************************
 * 				mmTaskSignal		[MMSYSTEM.903]
 */
LRESULT WINAPI mmTaskSignal16(HTASK16 ht) {
	fprintf(stderr,"mmTaskSignal(%04x),stub!\n",ht);
	return PostAppMessage16(ht,0x400,0,0);
}

/**************************************************************************
 * 				mciDriverYield		[MMSYSTEM.710]
 */
LRESULT WINAPI mciDriverYield16(HANDLE16 hnd) {
	fprintf(stderr,"mciDriverYield16(%04x),stub!\n",hnd);
	return 0;
}

