| /* -*- tab-width: 8; c-basic-offset: 4 -*- */ |
| |
| /* |
| * Sample MIDI Wine Driver for Linux |
| * |
| * Copyright 1994 Martin Ayotte |
| */ |
| |
| /* |
| * Eric POUECH : |
| * 98/7 changes for making this MIDI driver work on OSS |
| * current support is limited to MIDI ports of OSS systems |
| * 98/9 rewriting MCI code for MIDI |
| */ |
| |
| #include <errno.h> |
| #include <string.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <unistd.h> |
| #include <fcntl.h> |
| #include <sys/ioctl.h> |
| #include "windows.h" |
| #include "ldt.h" |
| #include "multimedia.h" |
| #include "user.h" |
| #include "driver.h" |
| #include "mmsystem.h" |
| #include "xmalloc.h" |
| #include "debug.h" |
| #include "callback.h" |
| #include "options.h" |
| |
| typedef struct { |
| #ifndef HAVE_OSS |
| int unixdev; |
| #endif |
| int state; |
| DWORD bufsize; |
| LPMIDIOPENDESC midiDesc; |
| WORD wFlags; |
| LPMIDIHDR lpQueueHdr; |
| DWORD dwTotalPlayed; |
| #ifdef HAVE_OSS |
| unsigned char incoming[3]; |
| unsigned char incPrev; |
| char incLen; |
| DWORD startTime; |
| #endif |
| } WINE_MIDIIN; |
| |
| typedef struct { |
| #ifndef HAVE_OSS |
| int unixdev; |
| #endif |
| int state; |
| DWORD bufsize; |
| LPMIDIOPENDESC midiDesc; |
| WORD wFlags; |
| LPMIDIHDR lpQueueHdr; |
| DWORD dwTotalPlayed; |
| #ifdef HAVE_OSS |
| void* lpExtra; /* according to port type (MIDI, FM...), extra data when needed */ |
| #endif |
| } WINE_MIDIOUT; |
| |
| typedef struct { |
| DWORD dwFirst; /* offset in file of track */ |
| DWORD dwLast; /* number of bytes in file of track */ |
| DWORD dwIndex; /* current index in file (dwFirst <= dwIndex < dwLast) */ |
| DWORD dwLength; /* number of pulses in this track */ |
| DWORD dwEventPulse; /* current pulse # (event) pointed by dwIndex */ |
| DWORD dwEventData; /* current data (event) pointed by dwIndex */ |
| WORD wEventLength; /* current length (event) pointed by dwIndex */ |
| WORD wStatus : 1, /* 1 : playing, 0 : done */ |
| wTrackNr : 7, |
| wLastCommand : 8; |
| } MCI_MIDITRACK; |
| |
| typedef struct { |
| int nUseCount; /* Incremented for each shared open */ |
| BOOL16 fShareable; /* TRUE if first open was shareable */ |
| WORD wNotifyDeviceID; /* MCI device ID with a pending notification */ |
| HANDLE16 hCallback; /* Callback handle for pending notification */ |
| HMMIO32 hFile; /* mmio file handle open as Element */ |
| WORD wFormat; |
| WORD nTracks; |
| WORD nDivision; |
| DWORD dwTempo; |
| MCI_OPEN_PARMS16 openParms; |
| WORD dwStatus; |
| MCI_MIDITRACK* tracks; |
| DWORD dwPulse; |
| DWORD dwPositionMS; |
| DWORD dwStartTicks; |
| HLOCAL16 hMidiHdr; |
| } WINE_MCIMIDI; |
| |
| static WINE_MIDIIN MidiInDev [MAX_MIDIINDRV ]; |
| static WINE_MIDIOUT MidiOutDev[MAX_MIDIOUTDRV]; |
| static WINE_MCIMIDI MCIMidiDev[MAX_MCIMIDIDRV]; |
| |
| /* this is the total number of MIDI out devices found */ |
| int MODM_NUMDEVS = 0; |
| /* this is the number of FM synthetizers (index from 0 to |
| NUMFMSYNTHDEVS - 1) */ |
| int MODM_NUMFMSYNTHDEVS = 0; |
| /* this is the number of Midi ports (index from NUMFMSYNTHDEVS to |
| NUMFMSYNTHDEVS + NUMMIDIDEVS - 1) */ |
| int MODM_NUMMIDIDEVS = 0; |
| |
| /* this is the total number of MIDI out devices found */ |
| int MIDM_NUMDEVS = 0; |
| |
| #ifdef HAVE_OSS |
| static int midiSeqFD = -1; |
| static int numOpenMidiSeq = 0; |
| static UINT32 midiInTimerID = 0; |
| static int numStartedMidiIn = 0; |
| #endif /* HAVE_OSS */ |
| |
| /* this structure holds pointers with information for each MIDI |
| * out device found. |
| */ |
| LPMIDIOUTCAPS16 midiOutDevices[MAX_MIDIOUTDRV]; |
| |
| /* this structure holds pointers with information for each MIDI |
| * in device found. |
| */ |
| LPMIDIINCAPS16 midiInDevices [MAX_MIDIINDRV]; |
| |
| /* |
| * FIXME : all tests on device ID for midXXX and modYYY are made against |
| * MAX_MIDIxxDRV (when they are made) but should be done against the actual |
| * number of midi devices found... |
| * check also when HAVE_OSS is defined that midiDesc is not NULL |
| */ |
| |
| /************************************************************************** |
| * MIDI_NotifyClient [internal] |
| */ |
| static DWORD MIDI_NotifyClient(UINT16 wDevID, WORD wMsg, |
| DWORD dwParam1, DWORD dwParam2) |
| { |
| DWORD dwCallBack; |
| UINT16 uFlags; |
| HANDLE16 hDev; |
| DWORD dwInstance; |
| |
| TRACE(midi,"wDevID = %04X wMsg = %d dwParm1 = %04lX dwParam2 = %04lX\n", |
| wDevID, wMsg, dwParam1, dwParam2); |
| |
| switch (wMsg) { |
| case MOM_OPEN: |
| case MOM_CLOSE: |
| case MOM_DONE: |
| if (wDevID > MAX_MIDIOUTDRV) |
| return MCIERR_INTERNAL; |
| |
| dwCallBack = MidiOutDev[wDevID].midiDesc->dwCallback; |
| uFlags = MidiOutDev[wDevID].wFlags; |
| hDev = MidiOutDev[wDevID].midiDesc->hMidi; |
| dwInstance = MidiOutDev[wDevID].midiDesc->dwInstance; |
| break; |
| |
| case MIM_OPEN: |
| case MIM_CLOSE: |
| case MIM_DATA: |
| case MIM_ERROR: |
| if (wDevID > MAX_MIDIINDRV) |
| return MCIERR_INTERNAL; |
| |
| dwCallBack = MidiInDev[wDevID].midiDesc->dwCallback; |
| uFlags = MidiInDev[wDevID].wFlags; |
| hDev = MidiInDev[wDevID].midiDesc->hMidi; |
| dwInstance = MidiInDev[wDevID].midiDesc->dwInstance; |
| break; |
| default: |
| WARN(midi, "Unsupported MSW-MIDI message %u\n", wMsg); |
| return MCIERR_INTERNAL; |
| } |
| |
| return DriverCallback(dwCallBack, uFlags, hDev, wMsg, dwInstance, dwParam1, dwParam2) ? |
| 0 : MCIERR_INTERNAL; |
| } |
| |
| /************************************************************************** |
| * MIDI_ReadByte [internal] |
| */ |
| static DWORD MIDI_ReadByte(UINT16 wDevID, BYTE *lpbyt) |
| { |
| if (lpbyt != NULL) { |
| if (mmioRead32(MCIMidiDev[wDevID].hFile, (HPSTR)lpbyt, |
| (long) sizeof(BYTE)) == (long) sizeof(BYTE)) { |
| return 0; |
| } |
| } |
| WARN(midi, "error reading wDevID=%04X\n", wDevID); |
| return MCIERR_INTERNAL; |
| |
| } |
| |
| /************************************************************************** |
| * MIDI_ReadWord [internal] |
| */ |
| static DWORD MIDI_ReadWord(UINT16 wDevID, LPWORD lpw) |
| { |
| BYTE hibyte, lobyte; |
| |
| if (lpw != NULL) { |
| if (MIDI_ReadByte(wDevID, &hibyte) == 0) { |
| if (MIDI_ReadByte(wDevID, &lobyte) == 0) { |
| *lpw = ((WORD)hibyte << 8) + lobyte; |
| return 0; |
| } |
| } |
| } |
| WARN(midi, "error reading wDevID=%04X\n", wDevID); |
| return MCIERR_INTERNAL; |
| } |
| |
| /************************************************************************** |
| * MIDI_ReadLong [internal] |
| */ |
| static DWORD MIDI_ReadLong(UINT16 wDevID, LPDWORD lpdw) |
| { |
| WORD hiword, loword; |
| |
| if (lpdw != NULL) { |
| if (MIDI_ReadWord(wDevID, &hiword) == 0) { |
| if (MIDI_ReadWord(wDevID, &loword) == 0) { |
| *lpdw = MAKELONG(loword, hiword); |
| return 0; |
| } |
| } |
| } |
| WARN(midi, "error reading wDevID=%04X\n", wDevID); |
| return MCIERR_INTERNAL; |
| } |
| |
| /************************************************************************** |
| * MIDI_ReadVaryLen [internal] |
| */ |
| static WORD MIDI_ReadVaryLen(UINT16 wDevID, LPDWORD lpdw) |
| { |
| BYTE byte; |
| DWORD value = 0; |
| WORD ret = 0; |
| |
| if (lpdw == NULL) |
| return MCIERR_INTERNAL; |
| |
| do { |
| if (MIDI_ReadByte(wDevID, &byte) != 0) { |
| WARN(midi, "error reading wDevID=%04X\n", wDevID); |
| return 0; |
| } |
| value = (value << 7) + (byte & 0x7F); |
| ret++; |
| } while (byte & 0x80); |
| *lpdw = value; |
| /* |
| TRACE(midi, "val=%08lX \n", value); |
| */ |
| return ret; |
| } |
| |
| /************************************************************************** |
| * MIDI_ReadNextEvent [internal] |
| */ |
| static DWORD MIDI_ReadNextEvent(UINT16 wDevID, MCI_MIDITRACK* mmt) |
| { |
| BYTE b1, b2 = 0, b3; |
| WORD hw = 0; |
| DWORD evtPulse; |
| DWORD evtLength; |
| DWORD tmp; |
| |
| if (mmioSeek32(MCIMidiDev[wDevID].hFile, mmt->dwIndex, SEEK_SET) != mmt->dwIndex) { |
| WARN(midi, "can't seek at %08lX \n", mmt->dwIndex); |
| return MCIERR_INTERNAL; |
| } |
| evtLength = MIDI_ReadVaryLen(wDevID, &evtPulse) + 1; /* > 0 */ |
| MIDI_ReadByte(wDevID, &b1); |
| switch (b1) { |
| case 0xF0: |
| case 0xF7: |
| evtLength += MIDI_ReadVaryLen(wDevID, &tmp); |
| evtLength += tmp; |
| break; |
| case 0xFF: |
| MIDI_ReadByte(wDevID, &b2); evtLength++; |
| |
| evtLength += MIDI_ReadVaryLen(wDevID, &tmp); |
| if (evtLength >= 0x10000u) { |
| /* this limitation shouldn't be a problem */ |
| WARN(midi, "Ouch !! Implementation limitation to 64k bytes for a MIDI event is overflowed\n"); |
| hw = 0xFFFF; |
| } else { |
| hw = LOWORD(evtLength); |
| } |
| evtLength += tmp; |
| break; |
| default: |
| if (b1 & 0x80) { // use running status ? |
| mmt->wLastCommand = b1; |
| MIDI_ReadByte(wDevID, &b2); evtLength++; |
| } else { |
| b2 = b1; |
| b1 = mmt->wLastCommand; |
| } |
| switch ((b1 >> 4) & 0x07) { |
| case 0: case 1: case 2: case 3: case 6: |
| MIDI_ReadByte(wDevID, &b3); evtLength++; |
| hw = b3; |
| break; |
| case 4: case 5: |
| break; |
| case 7: |
| WARN(midi, "Strange indeed b1=0x%02x\n", b1); |
| } |
| break; |
| } |
| if (mmt->dwIndex + evtLength > mmt->dwLast) |
| return MCIERR_INTERNAL; |
| |
| mmt->dwEventPulse += evtPulse; |
| mmt->dwEventData = (hw << 16) + (b2 << 8) + b1; |
| mmt->wEventLength = evtLength; |
| |
| /* |
| TRACE(midi, "[%u] => pulse=%08lx(%08lx), data=%08lx, length=%u\n", |
| mmt->wTrackNr, mmt->dwEventPulse, evtPulse, |
| mmt->dwEventData, mmt->wEventLength); |
| */ |
| return 0; |
| } |
| |
| /************************************************************************** |
| * MIDI_ReadMTrk [internal] |
| */ |
| static DWORD MIDI_ReadMTrk(UINT16 wDevID, MCI_MIDITRACK* mmt) |
| { |
| DWORD toberead; |
| FOURCC fourcc; |
| |
| if (mmioRead32(MCIMidiDev[wDevID].hFile, (HPSTR)&fourcc, |
| (long) sizeof(FOURCC)) != (long) sizeof(FOURCC)) { |
| return MCIERR_INTERNAL; |
| } |
| |
| if (fourcc != mmioFOURCC('M', 'T', 'r', 'k')) { |
| WARN(midi, "cannot synchronize on MTrk !\n"); |
| return MCIERR_INTERNAL; |
| } |
| |
| if (MIDI_ReadLong(wDevID, &toberead) != 0) { |
| return MCIERR_INTERNAL; |
| } |
| mmt->dwFirst = mmioSeek32(MCIMidiDev[wDevID].hFile, 0, SEEK_CUR); /* >= 0 */ |
| mmt->dwLast = mmt->dwFirst + toberead; |
| |
| /* compute # of pulses in this track */ |
| mmt->dwIndex = mmt->dwFirst; |
| mmt->dwEventPulse = 0; |
| while (MIDI_ReadNextEvent(wDevID, mmt) == 0 && |
| LOWORD(mmt->dwEventData) != 0x2FFF) { |
| mmt->dwIndex += mmt->wEventLength; |
| } |
| mmt->dwLength = mmt->dwEventPulse; |
| |
| TRACE(midi, "Track %u has %lu bytes and %lu pulses\n", |
| mmt->wTrackNr, toberead, mmt->dwLength); |
| |
| /* reset track data */ |
| mmt->wStatus = 1; /* ok, playing */ |
| mmt->dwIndex = mmt->dwFirst; |
| mmt->dwEventPulse = 0; |
| |
| if (mmioSeek32(MCIMidiDev[wDevID].hFile, 0, SEEK_CUR) != mmt->dwLast) { |
| WARN(midi, "Ouch, out of sync seek=%lu track=%lu\n", |
| mmioSeek32(MCIMidiDev[wDevID].hFile, 0, SEEK_CUR), mmt->dwLast); |
| /* position at end of this track, to be ready to read next track */ |
| mmioSeek32(MCIMidiDev[wDevID].hFile, mmt->dwLast, SEEK_SET); |
| } |
| |
| return 0; |
| } |
| |
| /************************************************************************** |
| * MIDI_ReadMThd [internal] |
| */ |
| static DWORD MIDI_ReadMThd(UINT16 wDevID, DWORD dwOffset) |
| { |
| DWORD toberead; |
| FOURCC fourcc; |
| WORD nt; |
| |
| TRACE(midi, "(%04X, %08lX);\n", wDevID, dwOffset); |
| if (mmioSeek32(MCIMidiDev[wDevID].hFile, dwOffset, SEEK_SET) != dwOffset) { |
| WARN(midi, "can't seek at %08lX begin of 'MThd' \n", dwOffset); |
| return MCIERR_INTERNAL; |
| } |
| if (mmioRead32(MCIMidiDev[wDevID].hFile, (HPSTR)&fourcc, |
| (long) sizeof(FOURCC)) != (long) sizeof(FOURCC)) |
| return MCIERR_INTERNAL; |
| |
| if (fourcc != mmioFOURCC('M', 'T', 'h', 'd')) { |
| WARN(midi, "cannot synchronize on MThd !\n"); |
| return MCIERR_INTERNAL; |
| } |
| |
| if (MIDI_ReadLong(wDevID, &toberead) != 0 || toberead < 3 * sizeof(WORD)) |
| return MCIERR_INTERNAL; |
| if (MIDI_ReadWord(wDevID, &MCIMidiDev[wDevID].wFormat) != 0) |
| return MCIERR_INTERNAL; |
| if (MIDI_ReadWord(wDevID, &MCIMidiDev[wDevID].nTracks) != 0) |
| return MCIERR_INTERNAL; |
| if (MIDI_ReadWord(wDevID, &MCIMidiDev[wDevID].nDivision) != 0) |
| return MCIERR_INTERNAL; |
| |
| TRACE(midi, "toberead=0x%08lX, wFormat=0x%04X nTracks=0x%04X nDivision=0x%04X\n", |
| toberead, MCIMidiDev[wDevID].wFormat, |
| MCIMidiDev[wDevID].nTracks, |
| MCIMidiDev[wDevID].nDivision); |
| |
| if (MCIMidiDev[wDevID].nDivision > 0x8000) { |
| WARN(midi, "Handling SMPTE time in MIDI files is not (yet) supported\n" |
| "Please report with MIDI file !\n"); |
| return MCIERR_INTERNAL; |
| } |
| |
| switch (MCIMidiDev[wDevID].wFormat) { |
| case 0: |
| if (MCIMidiDev[wDevID].nTracks != 1) { |
| WARN(midi, "Got type 0 file whose number of track is not 1. Setting it to 1\n"); |
| MCIMidiDev[wDevID].nTracks = 1; |
| } |
| break; |
| case 1: |
| case 2: |
| break; |
| default: |
| WARN(midi, "Handling MIDI files which format = %d is not (yet) supported\n" |
| "Please report with MIDI file !\n", MCIMidiDev[wDevID].wFormat); |
| return MCIERR_INTERNAL; |
| } |
| |
| if (MCIMidiDev[wDevID].nTracks & 0x8000) { |
| /* this shouldn't be a problem... */ |
| WARN(midi, "Ouch !! Implementation limitation to 32k tracks per MIDI file is overflowed\n"); |
| MCIMidiDev[wDevID].nTracks = 0x7FFF; |
| } |
| MCIMidiDev[wDevID].tracks = xmalloc(sizeof(MCI_MIDITRACK) * MCIMidiDev[wDevID].nTracks); |
| |
| toberead -= 3 * sizeof(WORD); |
| if (toberead > 0) { |
| TRACE(midi, "Size of MThd > 6, skipping %ld extra bytes\n", toberead); |
| mmioSeek32(MCIMidiDev[wDevID].hFile, toberead, SEEK_CUR); |
| } |
| |
| for (nt = 0; nt < MCIMidiDev[wDevID].nTracks; nt++) { |
| MCIMidiDev[wDevID].tracks[nt].wTrackNr = nt; |
| if (MIDI_ReadMTrk(wDevID, &MCIMidiDev[wDevID].tracks[nt]) != 0) { |
| WARN(midi, "can't read 'MTrk' header \n"); |
| return MCIERR_INTERNAL; |
| } |
| } |
| |
| return 0; |
| } |
| |
| /************************************************************************** |
| * MIDI_mciOpen [internal] |
| */ |
| static DWORD MIDI_mciOpen(UINT16 wDevID, DWORD dwFlags, void* lp, BOOL32 is32) |
| { |
| MIDIOPENDESC MidiDesc; |
| DWORD dwRet; |
| DWORD dwDeviceID; |
| |
| TRACE(midi, "(%08lX, %p)\n", dwFlags, lp); |
| |
| if (lp == NULL) return MCIERR_INTERNAL; |
| |
| if (MCIMidiDev[wDevID].nUseCount > 0) { |
| /* The driver already open on this channel */ |
| /* If the driver was opened shareable before and this open specifies */ |
| /* shareable then increment the use count */ |
| if (MCIMidiDev[wDevID].fShareable && (dwFlags & MCI_OPEN_SHAREABLE)) |
| ++MCIMidiDev[wDevID].nUseCount; |
| else |
| return MCIERR_MUST_USE_SHAREABLE; |
| } else { |
| MCIMidiDev[wDevID].nUseCount = 1; |
| MCIMidiDev[wDevID].fShareable = dwFlags & MCI_OPEN_SHAREABLE; |
| MCIMidiDev[wDevID].hMidiHdr = USER_HEAP_ALLOC(sizeof(MIDIHDR)); |
| } |
| |
| if (is32) dwDeviceID = ((LPMCI_OPEN_PARMS32A)lp)->wDeviceID; |
| else dwDeviceID = ((LPMCI_OPEN_PARMS16)lp)->wDeviceID; |
| |
| TRACE(midi, "wDevID=%04X (lpParams->wDeviceID=%08lX)\n", wDevID, dwDeviceID); |
| /* lpParms->wDeviceID = wDevID;*/ |
| TRACE(midi, "before OPEN_ELEMENT\n"); |
| if (dwFlags & MCI_OPEN_ELEMENT) { |
| LPSTR lpstrElementName; |
| |
| if (is32) lpstrElementName = ((LPMCI_OPEN_PARMS32A)lp)->lpstrElementName; |
| else lpstrElementName = (LPSTR)PTR_SEG_TO_LIN(((LPMCI_OPEN_PARMS16)lp)->lpstrElementName); |
| |
| TRACE(midi, "MCI_OPEN_ELEMENT '%s' !\n", lpstrElementName); |
| if (strlen(lpstrElementName) > 0) { |
| MCIMidiDev[wDevID].hFile = mmioOpen32A(lpstrElementName, NULL, |
| MMIO_ALLOCBUF | MMIO_READWRITE | MMIO_EXCLUSIVE); |
| if (MCIMidiDev[wDevID].hFile == 0) { |
| WARN(midi, "can't find file='%s' !\n", lpstrElementName); |
| return MCIERR_FILE_NOT_FOUND; |
| } |
| } else |
| MCIMidiDev[wDevID].hFile = 0; |
| } |
| TRACE(midi, "hFile=%u\n", MCIMidiDev[wDevID].hFile); |
| |
| /* should be of same size in all cases */ |
| memcpy(&MCIMidiDev[wDevID].openParms, lp, sizeof(MCI_OPEN_PARMS16)); |
| |
| MCIMidiDev[wDevID].wNotifyDeviceID = dwDeviceID; |
| MCIMidiDev[wDevID].dwStatus = MCI_MODE_STOP; |
| MidiDesc.hMidi = 0; |
| |
| if (MCIMidiDev[wDevID].hFile != 0) { |
| MMCKINFO ckMainRIFF; |
| DWORD dwOffset = 0; |
| |
| if (mmioDescend(MCIMidiDev[wDevID].hFile, &ckMainRIFF, NULL, 0) != 0) { |
| return MCIERR_INTERNAL; |
| } |
| TRACE(midi,"ParentChunk ckid=%.4s fccType=%.4s cksize=%08lX \n", |
| (LPSTR)&ckMainRIFF.ckid, (LPSTR)&ckMainRIFF.fccType, |
| ckMainRIFF.cksize); |
| |
| if (ckMainRIFF.ckid == mmioFOURCC('R', 'M', 'I', 'D')) { |
| TRACE(midi, "is a 'RMID' file \n"); |
| dwOffset = ckMainRIFF.dwDataOffset; |
| FIXME(midi, "Setting #tracks for RMID to 1: is this correct ?\n"); |
| MCIMidiDev[wDevID].nTracks = 1; |
| } |
| if (MIDI_ReadMThd(wDevID, dwOffset) != 0) { |
| WARN(midi, "can't read 'MThd' header \n"); |
| return MCIERR_INTERNAL; |
| } |
| |
| TRACE(midi, "Chunk Found ckid=%.4s fccType=%.4s cksize=%08lX \n", |
| (LPSTR)&ckMainRIFF.ckid, (LPSTR)&ckMainRIFF.fccType, |
| ckMainRIFF.cksize); |
| } |
| |
| dwRet = modMessage(wDevID, MODM_OPEN, 0, (DWORD)&MidiDesc, CALLBACK_NULL); |
| /* dwRet = midMessage(wDevID, MIDM_OPEN, 0, (DWORD)&MidiDesc, CALLBACK_NULL);*/ |
| |
| return 0; |
| } |
| |
| static DWORD MIDI_ConvertPulseToMS(UINT16 wDevID, DWORD pulse) |
| { |
| DWORD ret = (DWORD)((double)pulse * ((double)MCIMidiDev[wDevID].dwTempo / 1000) / |
| (double)MCIMidiDev[wDevID].nDivision); |
| |
| /* |
| TRACE(midi, "pulse=%lu tempo=%lu division=%u => ms=%lu\n", |
| pulse, MCIMidiDev[wDevID].dwTempo, MCIMidiDev[wDevID].nDivision, ret); |
| */ |
| return ret; |
| } |
| |
| /************************************************************************** |
| * MIDI_mciStop [internal] |
| */ |
| static DWORD MIDI_mciStop(UINT16 wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms) |
| { |
| TRACE(midi, "(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms); |
| |
| if (wDevID >= MAX_MCIMIDIDRV || MCIMidiDev[wDevID].nUseCount == 0) { |
| WARN(midi, "Invalid wDevID=%u\n", wDevID); |
| return MCIERR_INVALID_DEVICE_ID; |
| } |
| |
| MCIMidiDev[wDevID].dwStatus = MCI_MODE_STOP; |
| TRACE(midi, "MCIMidiDev[wDevID].dwStatus=%d\n", MCIMidiDev[wDevID].dwStatus); |
| if (lpParms && (dwFlags & MCI_NOTIFY)) { |
| TRACE(midi, "MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback); |
| mciDriverNotify((HWND16)LOWORD(lpParms->dwCallback), |
| MCIMidiDev[wDevID].wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL); |
| } |
| return 0; |
| } |
| |
| /************************************************************************** |
| * MIDI_mciClose [internal] |
| */ |
| static DWORD MIDI_mciClose(UINT16 wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms) |
| { |
| DWORD dwRet; |
| |
| TRACE(midi, "(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms); |
| |
| if (wDevID >= MAX_MCIMIDIDRV || MCIMidiDev[wDevID].nUseCount == 0) { |
| WARN(midi, "Invalid wDevID=%u\n", wDevID); |
| return MCIERR_INVALID_DEVICE_ID; |
| } |
| |
| if (MCIMidiDev[wDevID].dwStatus != MCI_MODE_STOP) { |
| MIDI_mciStop(wDevID, MCI_WAIT, lpParms); |
| } |
| |
| MCIMidiDev[wDevID].dwStatus = MCI_MODE_STOP; |
| MCIMidiDev[wDevID].nUseCount--; |
| if (MCIMidiDev[wDevID].nUseCount == 0) { |
| if (MCIMidiDev[wDevID].hFile != 0) { |
| mmioClose32(MCIMidiDev[wDevID].hFile, 0); |
| MCIMidiDev[wDevID].hFile = 0; |
| TRACE(midi, "hFile closed !\n"); |
| } |
| USER_HEAP_FREE(MCIMidiDev[wDevID].hMidiHdr); |
| free(MCIMidiDev[wDevID].tracks); |
| dwRet = modMessage(wDevID, MODM_CLOSE, 0, 0L, 0L); |
| if (dwRet != MMSYSERR_NOERROR) return MCIERR_INTERNAL; |
| /* |
| dwRet = midMessage(wDevID, MIDM_CLOSE, 0, 0L, 0L); |
| if (dwRet != MMSYSERR_NOERROR) return MCIERR_INTERNAL; |
| */ |
| } |
| if (lpParms && (dwFlags & MCI_NOTIFY)) { |
| TRACE(midi, "MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback); |
| mciDriverNotify((HWND16)LOWORD(lpParms->dwCallback), |
| MCIMidiDev[wDevID].wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL); |
| } |
| return 0; |
| } |
| |
| /************************************************************************** |
| * MIDI_mciPlay [internal] |
| */ |
| static DWORD MIDI_mciPlay(UINT16 wDevID, DWORD dwFlags, LPMCI_PLAY_PARMS lpParms) |
| { |
| DWORD start = 0, end = 0xFFFFFFFFul; |
| DWORD dwRet; |
| WORD nt, cnt; |
| |
| TRACE(midi, "(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms); |
| |
| if (wDevID >= MAX_MCIMIDIDRV || MCIMidiDev[wDevID].nUseCount == 0) { |
| WARN(midi, "Invalid wDevID=%u\n", wDevID); |
| return MCIERR_INVALID_DEVICE_ID; |
| } |
| |
| if (MCIMidiDev[wDevID].hFile == 0) { |
| WARN(midi, "Cannot play : can't find file='%08lx' !\n", |
| (DWORD)MCIMidiDev[wDevID].openParms.lpstrElementName); |
| return MCIERR_FILE_NOT_FOUND; |
| } |
| |
| if (MCIMidiDev[wDevID].dwStatus != MCI_MODE_STOP) { |
| WARN(midi, "Cannot play : device is not stopped !\n"); |
| return MCIERR_INTERNAL; |
| } |
| |
| if (lpParms && (dwFlags & MCI_FROM)) { |
| start = lpParms->dwFrom; |
| FIXME(midi, "MCI_FROM=%lu \n", start); |
| } |
| if (lpParms && (dwFlags & MCI_TO)) { |
| end = lpParms->dwTo; |
| FIXME(midi, "MCI_TO=%lu \n", end); |
| } |
| |
| if (!(dwFlags & MCI_WAIT)) { |
| /** FIXME: I'm not 100% sure that wNotifyDeviceID is the right value in all cases ??? */ |
| return mciSendCommandAsync32(MCIMidiDev[wDevID].wNotifyDeviceID, MCI_PLAY, dwFlags, (DWORD)lpParms); |
| } |
| |
| /* init tracks */ |
| for (nt = 0; nt < MCIMidiDev[wDevID].nTracks; nt++) { |
| MCI_MIDITRACK* mmt = &MCIMidiDev[wDevID].tracks[nt]; |
| |
| mmt->wStatus = 1; /* ok, playing */ |
| mmt->dwIndex = mmt->dwFirst; |
| if (MCIMidiDev[wDevID].wFormat == 2 && nt > 0) { |
| mmt->dwEventPulse = MCIMidiDev[wDevID].tracks[nt - 1].dwLength; |
| } else { |
| mmt->dwEventPulse = 0; |
| } |
| MIDI_ReadNextEvent(wDevID, mmt); /* FIXME == 0 */ |
| } |
| |
| MCIMidiDev[wDevID].dwPulse = 0; |
| MCIMidiDev[wDevID].dwPositionMS = 0; |
| MCIMidiDev[wDevID].dwStartTicks = GetTickCount(); |
| MCIMidiDev[wDevID].dwTempo = 500000; |
| MCIMidiDev[wDevID].dwStatus = MCI_MODE_PLAY; |
| |
| while (MCIMidiDev[wDevID].dwStatus != MCI_MODE_STOP) { |
| MCI_MIDITRACK* mmt; |
| DWORD hiPulse; |
| |
| TRACE(midi, "MCIMidiDev[wDevID].dwStatus=%p %d\n", |
| &MCIMidiDev[wDevID].dwStatus, MCIMidiDev[wDevID].dwStatus); |
| |
| while (MCIMidiDev[wDevID].dwStatus == MCI_MODE_PAUSE); |
| |
| /* find first event */ |
| hiPulse = 0xFFFFFFFFul; |
| cnt = 0xFFFFu; |
| for (nt = 0; nt < MCIMidiDev[wDevID].nTracks; nt++) { |
| mmt = &MCIMidiDev[wDevID].tracks[nt]; |
| |
| if (mmt->wStatus == 0) |
| continue; |
| if (mmt->dwEventPulse < hiPulse) { |
| hiPulse = mmt->dwEventPulse; |
| cnt = nt; |
| } |
| } |
| if (cnt == 0xFFFFu) /* no more event on tracks */ |
| break; |
| |
| mmt = &MCIMidiDev[wDevID].tracks[cnt]; |
| if (hiPulse > MCIMidiDev[wDevID].dwPulse) { |
| DWORD togo = MCIMidiDev[wDevID].dwStartTicks + |
| MIDI_ConvertPulseToMS(wDevID, hiPulse); |
| DWORD tc = GetTickCount(); |
| |
| TRACE(midi, "Pulses hi=0x%08lx <> cur=0x%08lx\n", hiPulse, MCIMidiDev[wDevID].dwPulse); |
| TRACE(midi, "Wait til %08lx -> %08lx ms\n", |
| tc - MCIMidiDev[wDevID].dwStartTicks, |
| togo - MCIMidiDev[wDevID].dwStartTicks); |
| if (tc < togo) Sleep(togo - tc); |
| MCIMidiDev[wDevID].dwPositionMS += MIDI_ConvertPulseToMS(wDevID, hiPulse - MCIMidiDev[wDevID].dwPulse); |
| MCIMidiDev[wDevID].dwPulse = hiPulse; |
| } |
| |
| switch (LOBYTE(LOWORD(mmt->dwEventData))) { |
| case 0xF0: |
| case 0xF7: /* sysex events */ |
| FIXME(midi, "Not handling SysEx events (yet)\n"); |
| break; |
| case 0xFF: |
| /* position after meta data header */ |
| mmioSeek32(MCIMidiDev[wDevID].hFile, mmt->dwIndex + HIWORD(mmt->dwEventData), SEEK_SET); |
| switch (HIBYTE(LOWORD(mmt->dwEventData))) { |
| case 0x00: /* 16-bit sequence number */ |
| if (TRACE_ON(midi)) { |
| WORD twd; |
| |
| MIDI_ReadWord(wDevID, &twd); /* == 0 */ |
| TRACE(midi, "Got sequence number %u\n", twd); |
| } |
| break; |
| case 0x01: /* any text */ |
| case 0x02: /* Copyright Message text */ |
| case 0x03: /* Sequence/Track Name text */ |
| case 0x04: /* Instrument Name text */ |
| case 0x05: /* Lyric text */ |
| case 0x06: /* Marker text */ |
| case 0x07: /* Cue-point text */ |
| if (TRACE_ON(midi)) { |
| char buf[1024]; |
| WORD len = mmt->wEventLength - HIWORD(mmt->dwEventData); |
| static char* info[8] = {"", "Text", "Copyright", "Seq/Trk name", |
| "Instrument", "Lyric", "Marker", "Cue-point"}; |
| WORD idx = HIBYTE(LOWORD(mmt->dwEventData)); |
| |
| if (len >= sizeof(buf)) { |
| WARN(midi, "Buffer for text is too small (%d bytes, when %u are needed)\n", sizeof(buf) - 1, len); |
| len = sizeof(buf) - 1; |
| } |
| if (mmioRead32(MCIMidiDev[wDevID].hFile, (HPSTR)buf, len) == len) { |
| buf[len] = 0; /* end string in case */ |
| TRACE(midi, "%s => \"%s\"\n", (idx < 8 ) ? info[idx] : "", buf); |
| } else { |
| WARN(midi, "Couldn't read data for %s\n", (idx < 8 ) ? info[idx] : ""); |
| } |
| } |
| break; |
| case 0x20: |
| /* MIDI channel |
| * cc |
| */ |
| if (TRACE_ON(midi)) { |
| BYTE bt; |
| |
| MIDI_ReadByte(wDevID, &bt); /* == 0 */ |
| FIXME(midi, "NIY: MIDI channel %u\n", bt); |
| |
| } else { |
| FIXME(midi, "NIY: MIDI channel\n"); |
| } |
| break; |
| case 0x21: |
| /* MIDI port |
| * pp |
| */ |
| if (TRACE_ON(midi)) { |
| BYTE bt; |
| |
| MIDI_ReadByte(wDevID, &bt); /* == 0 */ |
| FIXME(midi, "NIY: MIDI port %u\n", bt); |
| } else { |
| FIXME(midi, "NIY: MIDI port\n"); |
| } |
| break; |
| case 0x2F: /* end of track */ |
| mmt->wStatus = 0; |
| break; |
| case 0x51:/* set tempo */ |
| /* Tempo is expressed in µ-seconds per midi quarter note |
| * for format 1 MIDI files, this can only be present on track #0 |
| */ |
| if (mmt->wTrackNr != 0 && MCIMidiDev[wDevID].wFormat == 1) { |
| WARN(midi, "For format #1 MIDI files, tempo can only be changed on track #0 (%u)\n", mmt->wTrackNr); |
| } else { |
| BYTE tbt; |
| |
| MIDI_ReadByte(wDevID, &tbt); MCIMidiDev[wDevID].dwTempo = ((DWORD)tbt) << 16; |
| MIDI_ReadByte(wDevID, &tbt); MCIMidiDev[wDevID].dwTempo |= ((DWORD)tbt) << 8; |
| MIDI_ReadByte(wDevID, &tbt); MCIMidiDev[wDevID].dwTempo |= ((DWORD)tbt) << 0; |
| TRACE(midi, "Setting tempo to %ld (BPM=%ld)\n", MCIMidiDev[wDevID].dwTempo, 60000000l / MCIMidiDev[wDevID].dwTempo); |
| } |
| |
| break; |
| case 0x54: /* (hour) (min) (second) (frame) (fractional-frame) - SMPTE track start */ |
| FIXME(midi, "NIY: SMPTE track start\n"); |
| break; |
| case 0x58: |
| if (TRACE_ON(midi)) { |
| BYTE num, den, cpmc, _32npqn; |
| |
| MIDI_ReadByte(wDevID, &num); |
| MIDI_ReadByte(wDevID, &den); /* to notate e.g. 6/8 */ |
| MIDI_ReadByte(wDevID, &cpmc); /* number of MIDI clocks per metronome click */ |
| MIDI_ReadByte(wDevID, &_32npqn); /* number of notated 32nd notes per MIDI quarter note */ |
| |
| TRACE(midi, "%u/%u, clock per metronome click=%u, 32nd notes by 1/4 note=%u\n", num, 1 << den, cpmc, _32npqn); |
| } |
| break; |
| case 0x59: |
| if (TRACE_ON(midi)) { |
| BYTE sf, mm; |
| |
| MIDI_ReadByte(wDevID, &sf); MIDI_ReadByte(wDevID, &mm); |
| |
| if (sf >= 0x80) TRACE(midi, "%d flats\n", -(char)sf); |
| else if (sf > 0) TRACE(midi, "%d sharps\n", (char)sf); |
| else TRACE(midi, "Key of C\n"); |
| TRACE(midi, "Mode: %s\n", (mm = 0) ? "major" : "minor"); |
| } |
| break; |
| default: |
| WARN(midi, "Unknown MIDI meta event %02x. Skipping...\n", HIBYTE(LOWORD(mmt->dwEventData))); |
| break; |
| } |
| break; |
| default: |
| dwRet = modMessage(wDevID, MODM_DATA, 0, mmt->dwEventData, 0); |
| } |
| mmt->dwIndex += mmt->wEventLength; |
| if (mmt->dwIndex < mmt->dwFirst || mmt->dwIndex >= mmt->dwLast) { |
| mmt->wStatus = 0; |
| } |
| if (mmt->wStatus) { |
| MIDI_ReadNextEvent(wDevID, mmt); |
| } |
| } |
| |
| /* stop all notes */ |
| #ifdef HAVE_OSS |
| modMessage(wDevID, MODM_DATA, 0, (MIDI_CTL_CHANGE << 8) | 0x78, 0); |
| #endif |
| MCIMidiDev[wDevID].dwStatus = MCI_MODE_STOP; |
| if (lpParms && (dwFlags & MCI_NOTIFY)) { |
| TRACE(midi, "MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback); |
| mciDriverNotify((HWND16)LOWORD(lpParms->dwCallback), |
| MCIMidiDev[wDevID].wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL); |
| } |
| return 0; |
| } |
| |
| /************************************************************************** |
| * MIDI_mciRecord [internal] |
| */ |
| static DWORD MIDI_mciRecord(UINT16 wDevID, DWORD dwFlags, LPMCI_RECORD_PARMS lpParms) |
| { |
| int start, end; |
| LPMIDIHDR lpMidiHdr; |
| DWORD dwRet; |
| |
| TRACE(midi, "(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms); |
| |
| if (wDevID >= MAX_MCIMIDIDRV || MCIMidiDev[wDevID].nUseCount == 0) { |
| WARN(midi, "Invalid wDevID=%u\n", wDevID); |
| return MCIERR_INVALID_DEVICE_ID; |
| } |
| |
| if (MCIMidiDev[wDevID].hFile == 0) { |
| WARN(midi, "can't find file='%08lx' !\n", |
| (DWORD)MCIMidiDev[wDevID].openParms.lpstrElementName); |
| return MCIERR_FILE_NOT_FOUND; |
| } |
| start = 1; end = 99999; |
| if (lpParms && (dwFlags & MCI_FROM)) { |
| start = lpParms->dwFrom; |
| TRACE(midi, "MCI_FROM=%d \n", start); |
| } |
| if (lpParms && (dwFlags & MCI_TO)) { |
| end = lpParms->dwTo; |
| TRACE(midi, "MCI_TO=%d \n", end); |
| } |
| lpMidiHdr = USER_HEAP_LIN_ADDR(MCIMidiDev[wDevID].hMidiHdr); |
| lpMidiHdr->lpData = (LPSTR) xmalloc(1200); |
| lpMidiHdr->dwBufferLength = 1024; |
| lpMidiHdr->dwUser = 0L; |
| lpMidiHdr->dwFlags = 0L; |
| dwRet = midMessage(wDevID, MIDM_PREPARE, 0, (DWORD)lpMidiHdr, sizeof(MIDIHDR)); |
| TRACE(midi, "after MIDM_PREPARE \n"); |
| MCIMidiDev[wDevID].dwStatus = MCI_MODE_RECORD; |
| while (MCIMidiDev[wDevID].dwStatus != MCI_MODE_STOP) { |
| TRACE(midi, "MCIMidiDev[wDevID].dwStatus=%p %d\n", |
| &MCIMidiDev[wDevID].dwStatus, MCIMidiDev[wDevID].dwStatus); |
| lpMidiHdr->dwBytesRecorded = 0; |
| dwRet = midMessage(wDevID, MIDM_START, 0, 0L, 0L); |
| TRACE(midi, "after MIDM_START lpMidiHdr=%p dwBytesRecorded=%lu\n", |
| lpMidiHdr, lpMidiHdr->dwBytesRecorded); |
| if (lpMidiHdr->dwBytesRecorded == 0) break; |
| } |
| TRACE(midi, "before MIDM_UNPREPARE \n"); |
| dwRet = midMessage(wDevID, MIDM_UNPREPARE, 0, (DWORD)lpMidiHdr, sizeof(MIDIHDR)); |
| TRACE(midi, "after MIDM_UNPREPARE \n"); |
| if (lpMidiHdr->lpData != NULL) { |
| free(lpMidiHdr->lpData); |
| lpMidiHdr->lpData = NULL; |
| } |
| MCIMidiDev[wDevID].dwStatus = MCI_MODE_STOP; |
| if (lpParms && (dwFlags & MCI_NOTIFY)) { |
| TRACE(midi, "MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback); |
| mciDriverNotify((HWND16)LOWORD(lpParms->dwCallback), |
| MCIMidiDev[wDevID].wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL); |
| } |
| return 0; |
| } |
| |
| /************************************************************************** |
| * MIDI_mciPause [internal] |
| */ |
| static DWORD MIDI_mciPause(UINT16 wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms) |
| { |
| TRACE(midi, "(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms); |
| |
| if (wDevID >= MAX_MCIMIDIDRV || MCIMidiDev[wDevID].nUseCount == 0) { |
| WARN(midi, "Invalid wDevID=%u\n", wDevID); |
| return MCIERR_INVALID_DEVICE_ID; |
| } |
| |
| if (MCIMidiDev[wDevID].dwStatus == MCI_MODE_PLAY) { |
| MCIMidiDev[wDevID].dwStatus = MCI_MODE_PAUSE; |
| } |
| if (lpParms && (dwFlags & MCI_NOTIFY)) { |
| TRACE(midi, "MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback); |
| mciDriverNotify((HWND16)LOWORD(lpParms->dwCallback), |
| MCIMidiDev[wDevID].wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL); |
| } |
| |
| return 0; |
| } |
| |
| /************************************************************************** |
| * MIDI_mciResume [internal] |
| */ |
| static DWORD MIDI_mciResume(UINT16 wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms) |
| { |
| TRACE(midi, "(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms); |
| |
| if (wDevID >= MAX_MCIMIDIDRV || MCIMidiDev[wDevID].nUseCount == 0) { |
| WARN(midi, "Invalid wDevID=%u\n", wDevID); |
| return MCIERR_INVALID_DEVICE_ID; |
| } |
| |
| if (MCIMidiDev[wDevID].dwStatus == MCI_MODE_PAUSE) { |
| MCIMidiDev[wDevID].dwStatus = MCI_MODE_PLAY; |
| } |
| if (lpParms && (dwFlags & MCI_NOTIFY)) { |
| TRACE(midi, "MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback); |
| mciDriverNotify((HWND16)LOWORD(lpParms->dwCallback), |
| MCIMidiDev[wDevID].wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL); |
| } |
| return 0; |
| } |
| |
| /************************************************************************** |
| * MIDI_mciSet [internal] |
| */ |
| static DWORD MIDI_mciSet(UINT16 wDevID, DWORD dwFlags, LPMCI_SET_PARMS lpParms) |
| { |
| TRACE(midi, "(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms); |
| |
| if (lpParms == NULL) return MCIERR_INTERNAL; |
| |
| if (wDevID >= MAX_MCIMIDIDRV || MCIMidiDev[wDevID].nUseCount == 0) { |
| WARN(midi, "Invalid wDevID=%u\n", wDevID); |
| return MCIERR_INVALID_DEVICE_ID; |
| } |
| |
| TRACE(midi, "dwTimeFormat=%08lX\n", lpParms->dwTimeFormat); |
| TRACE(midi, "dwAudio=%08lX\n", lpParms->dwAudio); |
| |
| if (dwFlags & MCI_SET_TIME_FORMAT) { |
| switch (lpParms->dwTimeFormat) { |
| case MCI_FORMAT_MILLISECONDS: |
| TRACE(midi, "MCI_FORMAT_MILLISECONDS !\n"); |
| break; |
| case MCI_FORMAT_BYTES: |
| TRACE(midi, "MCI_FORMAT_BYTES !\n"); |
| break; |
| case MCI_FORMAT_SAMPLES: |
| TRACE(midi, "MCI_FORMAT_SAMPLES !\n"); |
| break; |
| default: |
| WARN(midi, "bad time format !\n"); |
| return MCIERR_BAD_TIME_FORMAT; |
| } |
| } |
| if (dwFlags & MCI_SET_VIDEO) { |
| TRACE(midi, "No support for video !\n"); |
| return MCIERR_UNSUPPORTED_FUNCTION; |
| } |
| if (dwFlags & MCI_SET_DOOR_OPEN) { |
| TRACE(midi, "No support for door open !\n"); |
| return MCIERR_UNSUPPORTED_FUNCTION; |
| } |
| if (dwFlags & MCI_SET_DOOR_CLOSED) { |
| TRACE(midi, "No support for door close !\n"); |
| return MCIERR_UNSUPPORTED_FUNCTION; |
| } |
| |
| if (dwFlags && MCI_SET_ON) { |
| TRACE(midi, "MCI_SET_ON !\n"); |
| if (dwFlags & MCI_SET_AUDIO) { |
| TRACE(midi, "MCI_SET_AUDIO !\n"); |
| } |
| if (dwFlags && MCI_SET_AUDIO_LEFT) |
| TRACE(midi, "MCI_SET_AUDIO_LEFT !\n"); |
| if (dwFlags && MCI_SET_AUDIO_RIGHT) |
| TRACE(midi, "MCI_SET_AUDIO_RIGHT !\n"); |
| } |
| if (dwFlags & MCI_SET_OFF) { |
| TRACE(midi, "MCI_SET_OFF !\n"); |
| if (dwFlags & MCI_SET_AUDIO) { |
| TRACE(midi, "MCI_SET_AUDIO !\n"); |
| } |
| if (dwFlags && MCI_SET_AUDIO_LEFT) |
| TRACE(midi, "MCI_SET_AUDIO_LEFT !\n"); |
| if (dwFlags && MCI_SET_AUDIO_RIGHT) |
| TRACE(midi, "MCI_SET_AUDIO_RIGHT !\n"); |
| } |
| if (dwFlags & MCI_SEQ_SET_MASTER) |
| TRACE(midi, "MCI_SEQ_SET_MASTER !\n"); |
| if (dwFlags & MCI_SEQ_SET_SLAVE) |
| TRACE(midi, "MCI_SEQ_SET_SLAVE !\n"); |
| if (dwFlags & MCI_SEQ_SET_OFFSET) |
| TRACE(midi, "MCI_SEQ_SET_OFFSET !\n"); |
| if (dwFlags & MCI_SEQ_SET_PORT) |
| TRACE(midi, "MCI_SEQ_SET_PORT !\n"); |
| if (dwFlags & MCI_SEQ_SET_TEMPO) |
| TRACE(midi, "MCI_SEQ_SET_TEMPO !\n"); |
| return 0; |
| } |
| |
| /************************************************************************** |
| * MIDI_mciStatus [internal] |
| */ |
| static DWORD MIDI_mciStatus(UINT16 wDevID, DWORD dwFlags, LPMCI_STATUS_PARMS lpParms) |
| { |
| TRACE(midi, "(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms); |
| |
| if (lpParms == NULL) return MCIERR_INTERNAL; |
| |
| if (wDevID >= MAX_MCIMIDIDRV || MCIMidiDev[wDevID].nUseCount == 0) { |
| WARN(midi, "Invalid wDevID=%u\n", wDevID); |
| return MCIERR_INVALID_DEVICE_ID; |
| } |
| |
| if (dwFlags & MCI_STATUS_ITEM) { |
| switch (lpParms->dwItem) { |
| case MCI_STATUS_CURRENT_TRACK: |
| TRACE(midi, "MCI_STATUS_CURRENT_TRACK !\n"); |
| lpParms->dwReturn = 1; |
| break; |
| case MCI_STATUS_LENGTH: |
| TRACE(midi, "MCI_STATUS_LENGTH !\n"); |
| { |
| WORD nt; |
| DWORD ret = 0; |
| |
| for (nt = 0; nt < MCIMidiDev[wDevID].nTracks; nt++) { |
| if (MCIMidiDev[wDevID].wFormat == 2) { |
| ret += MCIMidiDev[wDevID].tracks[nt].dwLength; |
| } else { |
| if (MCIMidiDev[wDevID].tracks[nt].dwLength > ret) |
| ret = MCIMidiDev[wDevID].tracks[nt].dwLength; |
| } |
| } |
| lpParms->dwReturn = MIDI_ConvertPulseToMS(wDevID, ret); |
| } |
| break; |
| case MCI_STATUS_MODE: |
| TRACE(midi, "MCI_STATUS_MODE !\n"); |
| lpParms->dwReturn = MCIMidiDev[wDevID].dwStatus; |
| break; |
| case MCI_STATUS_MEDIA_PRESENT: |
| TRACE(midi, "MCI_STATUS_MEDIA_PRESENT !\n"); |
| lpParms->dwReturn = TRUE; |
| break; |
| case MCI_STATUS_NUMBER_OF_TRACKS: |
| TRACE(midi, "MCI_STATUS_NUMBER_OF_TRACKS !\n"); |
| lpParms->dwReturn = 1; /* FIXME: except in format 2 */ |
| break; |
| case MCI_STATUS_POSITION: |
| TRACE(midi, "MCI_STATUS_POSITION !\n"); |
| if (dwFlags & MCI_STATUS_START) { |
| lpParms->dwReturn = 0; |
| } else { |
| lpParms->dwReturn = MCIMidiDev[wDevID].dwPositionMS; |
| } |
| break; |
| case MCI_STATUS_READY: |
| TRACE(midi, "MCI_STATUS_READY !\n"); |
| lpParms->dwReturn = TRUE; |
| break; |
| case MCI_STATUS_TIME_FORMAT: |
| TRACE(midi, "MCI_STATUS_TIME_FORMAT !\n"); |
| lpParms->dwReturn = MCI_FORMAT_MILLISECONDS; |
| break; |
| case MCI_SEQ_STATUS_DIVTYPE: |
| TRACE(midi, "MCI_SEQ_STATUS_DIVTYPE !\n"); |
| lpParms->dwReturn = MCI_SEQ_DIV_PPQN; |
| break; |
| case MCI_SEQ_STATUS_MASTER: |
| TRACE(midi, "MCI_SEQ_STATUS_MASTER !\n"); |
| lpParms->dwReturn = 0; |
| break; |
| case MCI_SEQ_STATUS_SLAVE: |
| TRACE(midi, "MCI_SEQ_STATUS_SLAVE !\n"); |
| lpParms->dwReturn = 0; |
| break; |
| case MCI_SEQ_STATUS_OFFSET: |
| TRACE(midi, "MCI_SEQ_STATUS_OFFSET !\n"); |
| lpParms->dwReturn = 0; |
| break; |
| case MCI_SEQ_STATUS_PORT: |
| TRACE(midi, "MCI_SEQ_STATUS_PORT !\n"); |
| lpParms->dwReturn = 0; |
| break; |
| case MCI_SEQ_STATUS_TEMPO: |
| TRACE(midi, "MCI_SEQ_STATUS_TEMPO !\n"); |
| lpParms->dwReturn = MCIMidiDev[wDevID].dwTempo; |
| break; |
| default: |
| WARN(midi, "unknowm command %08lX !\n", lpParms->dwItem); |
| return MCIERR_UNRECOGNIZED_COMMAND; |
| } |
| } else { |
| WARN(midi, "No Status-Item!\n"); |
| return MCIERR_UNRECOGNIZED_COMMAND; |
| } |
| if (dwFlags & MCI_NOTIFY) { |
| TRACE(midi, "MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback); |
| mciDriverNotify((HWND16)LOWORD(lpParms->dwCallback), |
| MCIMidiDev[wDevID].wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL); |
| } |
| return 0; |
| } |
| |
| /************************************************************************** |
| * MIDI_mciGetDevCaps [internal] |
| */ |
| static DWORD MIDI_mciGetDevCaps(UINT16 wDevID, DWORD dwFlags, |
| LPMCI_GETDEVCAPS_PARMS lpParms) |
| { |
| TRACE(midi, "(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms); |
| |
| if (lpParms == NULL) return MCIERR_INTERNAL; |
| |
| if (wDevID >= MAX_MCIMIDIDRV || MCIMidiDev[wDevID].nUseCount == 0) { |
| WARN(midi, "Invalid wDevID=%u\n", wDevID); |
| return MCIERR_INVALID_DEVICE_ID; |
| } |
| |
| if (dwFlags & MCI_GETDEVCAPS_ITEM) { |
| switch (lpParms->dwItem) { |
| case MCI_GETDEVCAPS_CAN_RECORD: |
| TRACE(midi, "MCI_GETDEVCAPS_CAN_RECORD !\n"); |
| lpParms->dwReturn = TRUE; |
| break; |
| case MCI_GETDEVCAPS_HAS_AUDIO: |
| TRACE(midi, "MCI_GETDEVCAPS_HAS_AUDIO !\n"); |
| lpParms->dwReturn = TRUE; |
| break; |
| case MCI_GETDEVCAPS_HAS_VIDEO: |
| TRACE(midi, "MCI_GETDEVCAPS_HAS_VIDEO !\n"); |
| lpParms->dwReturn = FALSE; |
| break; |
| case MCI_GETDEVCAPS_DEVICE_TYPE: |
| TRACE(midi, "MCI_GETDEVCAPS_DEVICE_TYPE !\n"); |
| lpParms->dwReturn = MCI_DEVTYPE_SEQUENCER; |
| break; |
| case MCI_GETDEVCAPS_USES_FILES: |
| TRACE(midi, "MCI_GETDEVCAPS_USES_FILES !\n"); |
| lpParms->dwReturn = TRUE; |
| break; |
| case MCI_GETDEVCAPS_COMPOUND_DEVICE: |
| TRACE(midi, "MCI_GETDEVCAPS_COMPOUND_DEVICE !\n"); |
| lpParms->dwReturn = TRUE; |
| break; |
| case MCI_GETDEVCAPS_CAN_EJECT: |
| TRACE(midi, "MCI_GETDEVCAPS_CAN_EJECT !\n"); |
| lpParms->dwReturn = FALSE; |
| break; |
| case MCI_GETDEVCAPS_CAN_PLAY: |
| TRACE(midi, "MCI_GETDEVCAPS_CAN_PLAY !\n"); |
| lpParms->dwReturn = TRUE; |
| break; |
| case MCI_GETDEVCAPS_CAN_SAVE: |
| TRACE(midi, "MCI_GETDEVCAPS_CAN_SAVE !\n"); |
| lpParms->dwReturn = FALSE; |
| break; |
| default: |
| TRACE(midi, "Unknown capability (%08lx) !\n", lpParms->dwItem); |
| return MCIERR_UNRECOGNIZED_COMMAND; |
| } |
| } else { |
| TRACE(midi, "No GetDevCaps-Item !\n"); |
| return MCIERR_UNRECOGNIZED_COMMAND; |
| } |
| return 0; |
| } |
| |
| /************************************************************************** |
| * MIDI_mciInfo [internal] |
| */ |
| static DWORD MIDI_mciInfo(UINT16 wDevID, DWORD dwFlags, LPMCI_INFO_PARMS16 lpParms) |
| { |
| TRACE(midi, "(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms); |
| |
| if (lpParms == NULL) return MCIERR_INTERNAL; |
| |
| if (wDevID >= MAX_MCIMIDIDRV || MCIMidiDev[wDevID].nUseCount == 0) { |
| WARN(midi, "Invalid wDevID=%u\n", wDevID); |
| return MCIERR_INVALID_DEVICE_ID; |
| } |
| |
| lpParms->lpstrReturn = NULL; |
| |
| switch (dwFlags) { |
| case MCI_INFO_PRODUCT: |
| lpParms->lpstrReturn = "Linux Sound System 0.5"; |
| break; |
| case MCI_INFO_FILE: |
| lpParms->lpstrReturn = "FileName"; |
| break; |
| default: |
| return MCIERR_UNRECOGNIZED_COMMAND; |
| } |
| lpParms->dwRetSize = (lpParms->lpstrReturn != NULL) ? strlen(lpParms->lpstrReturn) : 0; |
| return 0; |
| } |
| |
| /*-----------------------------------------------------------------------*/ |
| |
| #ifdef HAVE_OSS |
| /************************************************************************** |
| * midiOpenSeq [internal] |
| */ |
| static int midiOpenSeq(void) |
| { |
| if (numOpenMidiSeq == 0) { |
| midiSeqFD = open(MIDI_SEQ, O_RDWR, 0); |
| if (midiSeqFD == -1) { |
| ERR(midi, "can't open '%s' ! (%d)\n", MIDI_SEQ, errno); |
| return -1; |
| } |
| if (fcntl(midiSeqFD, F_SETFL, O_NONBLOCK) < 0) { |
| WARN(midi, "can't set sequencer fd to non blocking (%d)\n", errno); |
| close(midiSeqFD); |
| midiSeqFD = -1; |
| return -1; |
| } |
| ioctl(midiSeqFD, SNDCTL_SEQ_RESET); |
| } |
| numOpenMidiSeq++; |
| return 0; |
| } |
| |
| /************************************************************************** |
| * midiCloseSeq [internal] |
| */ |
| static int midiCloseSeq(void) |
| { |
| if (--numOpenMidiSeq == 0) { |
| close(midiSeqFD); |
| midiSeqFD = -1; |
| } |
| return 0; |
| } |
| |
| /* FIXME: this is a bad idea, it's even not static... */ |
| SEQ_DEFINEBUF(1024); |
| |
| /* FIXME: this is not reentrant, not static - because of global variable |
| * _seqbuf and al. */ |
| /************************************************************************** |
| * seqbuf_dump [internal] |
| */ |
| void seqbuf_dump(void) |
| { |
| if (_seqbufptr) { |
| if (write(midiSeqFD, _seqbuf, _seqbufptr) == -1) { |
| WARN(midi, "Can't write data to sequencer (%d/%d)!\n", |
| midiSeqFD, errno); |
| } |
| /* FIXME: |
| * in any case buffer is lost so that if many errors occur the buffer |
| * will not overrun |
| */ |
| _seqbufptr = 0; |
| } |
| } |
| #endif /* HAVE_OSS */ |
| |
| #ifdef HAVE_OSS |
| |
| static void midReceiveChar(WORD wDevID, unsigned char value, DWORD dwTime) |
| { |
| DWORD toSend = 0; |
| |
| TRACE(midi, "Adding %02xh to %d[%d]\n", value, wDevID, MidiInDev[wDevID].incLen); |
| |
| if (wDevID >= MAX_MIDIINDRV) { |
| WARN(midi, "bad devID\n"); |
| return; |
| } |
| if (MidiInDev[wDevID].state == 0) { |
| TRACE(midi, "input not started, thrown away\n"); |
| return; |
| } |
| |
| if (MidiInDev[wDevID].state & 2) { /* system exclusive */ |
| LPMIDIHDR ptr = MidiInDev[wDevID].lpQueueHdr; |
| WORD sbfb = FALSE; |
| |
| if (ptr) { |
| ((LPSTR)(ptr->reserved))[ptr->dwBytesRecorded++] = value; |
| if (ptr->dwBytesRecorded == ptr->dwBufferLength) { |
| sbfb = TRUE; |
| } |
| } |
| if (value == 0xF7) { /* then end */ |
| MidiInDev[wDevID].state &= ~2; |
| sbfb = TRUE; |
| } |
| if (sbfb && ptr != NULL) { |
| ptr = MidiInDev[wDevID].lpQueueHdr; |
| ptr->dwFlags &= ~MHDR_INQUEUE; |
| ptr->dwFlags |= MHDR_DONE; |
| MidiInDev[wDevID].lpQueueHdr = (LPMIDIHDR)ptr->lpNext; |
| if (MIDI_NotifyClient(wDevID, MIM_LONGDATA, (DWORD)ptr, dwTime) != MMSYSERR_NOERROR) { |
| WARN(midi, "Couldn't notify client\n"); |
| } |
| } |
| return; |
| } |
| |
| #define IS_CMD(_x) (((_x) & 0x80) == 0x80) |
| #define IS_SYS_CMD(_x) (((_x) & 0xF0) == 0xF0) |
| |
| if (!IS_CMD(value) && MidiInDev[wDevID].incLen == 0) { /* try to reuse old cmd */ |
| if (IS_CMD(MidiInDev[wDevID].incPrev) && !IS_SYS_CMD(MidiInDev[wDevID].incPrev)) { |
| MidiInDev[wDevID].incoming[0] = MidiInDev[wDevID].incPrev; |
| MidiInDev[wDevID].incLen = 1; |
| TRACE(midi, "Reusing old command %02xh\n", MidiInDev[wDevID].incPrev); |
| } else { |
| FIXME(midi, "error for midi-in, should generate MIM_ERROR notification:" |
| " prev=%02Xh, incLen=%02Xh\n", |
| MidiInDev[wDevID].incPrev, MidiInDev[wDevID].incLen); |
| return; |
| } |
| } |
| MidiInDev[wDevID].incoming[(int)(MidiInDev[wDevID].incLen++)] = value; |
| if (MidiInDev[wDevID].incLen == 1 && !IS_SYS_CMD(MidiInDev[wDevID].incoming[0])) { |
| /* store new cmd, just in case */ |
| MidiInDev[wDevID].incPrev = MidiInDev[wDevID].incoming[0]; |
| } |
| |
| #undef IS_CMD(_x) |
| #undef IS_SYS_CMD(_x) |
| |
| switch (MidiInDev[wDevID].incoming[0] & 0xF0) { |
| case MIDI_NOTEOFF: |
| case MIDI_NOTEON: |
| case MIDI_KEY_PRESSURE: |
| case MIDI_CTL_CHANGE: |
| case MIDI_PITCH_BEND: |
| if (MidiInDev[wDevID].incLen == 3) { |
| toSend = (MidiInDev[wDevID].incoming[2] << 16) | |
| (MidiInDev[wDevID].incoming[1] << 8) | |
| (MidiInDev[wDevID].incoming[0] << 0); |
| } |
| break; |
| case MIDI_PGM_CHANGE: |
| case MIDI_CHN_PRESSURE: |
| if (MidiInDev[wDevID].incLen == 2) { |
| toSend = (MidiInDev[wDevID].incoming[1] << 8) | |
| (MidiInDev[wDevID].incoming[0] << 0); |
| } |
| break; |
| case MIDI_SYSTEM_PREFIX: |
| if (MidiInDev[wDevID].incoming[0] == 0xF0) { |
| MidiInDev[wDevID].state |= 2; |
| MidiInDev[wDevID].incLen = 0; |
| } else { |
| if (MidiInDev[wDevID].incLen == 1) { |
| toSend = (MidiInDev[wDevID].incoming[0] << 0); |
| } |
| } |
| break; |
| default: |
| WARN(midi, "This shouldn't happen (%02X)\n", MidiInDev[wDevID].incoming[0]); |
| } |
| if (toSend != 0) { |
| TRACE(midi, "Sending event %08lx\n", toSend); |
| MidiInDev[wDevID].incLen = 0; |
| dwTime -= MidiInDev[wDevID].startTime; |
| if (MIDI_NotifyClient(wDevID, MIM_DATA, toSend, dwTime) != MMSYSERR_NOERROR) { |
| WARN(midi, "Couldn't notify client\n"); |
| } |
| } |
| } |
| |
| static VOID WINAPI midTimeCallback(HWND32 hwnd, UINT32 msg, UINT32 id, DWORD dwTime) |
| { |
| unsigned char buffer[256]; |
| int len, idx; |
| |
| TRACE(midi, "(%04X, %d, %d, %lu)\n", hwnd, msg, id, dwTime); |
| |
| len = read(midiSeqFD, buffer, sizeof(buffer)); |
| |
| if ((len % 4) != 0) { |
| WARN(midi, "bad length %d (%d)\n", len, errno); |
| return; |
| } |
| |
| for (idx = 0; idx < len; ) { |
| if (buffer[idx] & 0x80) { |
| TRACE(midi, |
| "reading<8> %02x %02x %02x %02x %02x %02x %02x %02x\n", |
| buffer[idx + 0], buffer[idx + 1], |
| buffer[idx + 2], buffer[idx + 3], |
| buffer[idx + 4], buffer[idx + 5], |
| buffer[idx + 6], buffer[idx + 7]); |
| idx += 8; |
| } else { |
| switch (buffer[idx + 0]) { |
| case SEQ_WAIT: |
| case SEQ_ECHO: |
| break; |
| case SEQ_MIDIPUTC: |
| midReceiveChar(buffer[idx + 2], buffer[idx + 1], dwTime); |
| break; |
| default: |
| TRACE(midi, "Unsupported event %d\n", buffer[idx + 0]); |
| break; |
| } |
| idx += 4; |
| } |
| } |
| } |
| #endif /* HAVE_OSS */ |
| |
| /************************************************************************** |
| * midGetDevCaps [internal] |
| */ |
| static DWORD midGetDevCaps(WORD wDevID, LPMIDIINCAPS16 lpCaps, DWORD dwSize) |
| { |
| LPMIDIINCAPS16 tmplpCaps; |
| |
| TRACE(midi, "(%04X, %p, %08lX);\n", wDevID, lpCaps, dwSize); |
| |
| if (wDevID >= MIDM_NUMDEVS) { |
| return MMSYSERR_BADDEVICEID; |
| } |
| if (lpCaps == NULL) { |
| return MMSYSERR_INVALPARAM; |
| } |
| |
| tmplpCaps = midiInDevices[wDevID]; |
| lpCaps->wMid = tmplpCaps->wMid; |
| lpCaps->wPid = tmplpCaps->wPid; |
| lpCaps->vDriverVersion = tmplpCaps->vDriverVersion; |
| strcpy(lpCaps->szPname, tmplpCaps->szPname); |
| if (dwSize == sizeof(MIDIINCAPS16)) { |
| /* we should run win 95, so make use of dwSupport */ |
| lpCaps->dwSupport = tmplpCaps->dwSupport; |
| } else if (dwSize != sizeof(MIDIINCAPS16) - sizeof(DWORD)) { |
| TRACE(midi, "bad size for lpCaps\n"); |
| return MMSYSERR_INVALPARAM; |
| } |
| |
| return MMSYSERR_NOERROR; |
| } |
| |
| /************************************************************************** |
| * midOpen [internal] |
| */ |
| static DWORD midOpen(WORD wDevID, LPMIDIOPENDESC lpDesc, DWORD dwFlags) |
| { |
| TRACE(midi, "(%04X, %p, %08lX);\n", wDevID, lpDesc, dwFlags); |
| |
| if (lpDesc == NULL) { |
| WARN(midi, "Invalid Parameter !\n"); |
| return MMSYSERR_INVALPARAM; |
| } |
| /* FIXME : |
| * how to check that content of lpDesc is correct ? |
| */ |
| if (wDevID >= MAX_MIDIINDRV) { |
| WARN(midi,"wDevID too large (%u) !\n", wDevID); |
| return MMSYSERR_BADDEVICEID; |
| } |
| if (MidiInDev[wDevID].midiDesc != 0) { |
| WARN(midi, "device already open !\n"); |
| return MMSYSERR_ALLOCATED; |
| } |
| if ((dwFlags & ~CALLBACK_TYPEMASK) != 0) { |
| FIXME(midi, "No support for MIDI_IO_STATUS in dwFlags\n"); |
| return MMSYSERR_INVALFLAG; |
| } |
| |
| #ifdef HAVE_OSS |
| if (midiOpenSeq() < 0) { |
| return MMSYSERR_ERROR; |
| } |
| |
| if (numStartedMidiIn++ == 0) { |
| midiInTimerID = SetTimer32(0, 0, 250, midTimeCallback); |
| if (!midiInTimerID) { |
| numStartedMidiIn = 0; |
| WARN(midi, "Couldn't start timer for midi-in\n"); |
| midiCloseSeq(); |
| return MMSYSERR_ERROR; |
| } |
| TRACE(midi, "Starting timer (%u) for midi-in\n", midiInTimerID); |
| } |
| #else /* HAVE_OSS */ |
| { |
| int midi = open(MIDI_DEV, O_WRONLY, 0); |
| |
| MidiInDev[wDevID].unixdev = 0; |
| if (midi == -1) { |
| WARN(midi,"can't open '%s' (%d)!\n", MIDI_DEV, errno); |
| return MMSYSERR_ALLOCATED; |
| } |
| MidiInDev[wDevID].unixdev = midi; |
| } |
| #endif /* HAVE_OSS */ |
| |
| MidiInDev[wDevID].wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK); |
| switch (MidiInDev[wDevID].wFlags) { |
| case DCB_NULL: |
| TRACE(midi,"CALLBACK_NULL !\n"); |
| break; |
| case DCB_WINDOW: |
| TRACE(midi, "CALLBACK_WINDOW !\n"); |
| break; |
| case DCB_TASK: |
| TRACE(midi, "CALLBACK_TASK !\n"); |
| break; |
| case DCB_FUNCTION: |
| TRACE(midi, "CALLBACK_FUNCTION (%08lX)!\n", lpDesc->dwCallback); |
| break; |
| } |
| MidiInDev[wDevID].lpQueueHdr = NULL; |
| MidiInDev[wDevID].dwTotalPlayed = 0; |
| MidiInDev[wDevID].bufsize = 0x3FFF; |
| MidiInDev[wDevID].midiDesc = lpDesc; |
| MidiInDev[wDevID].state = 0; |
| #ifdef HAVE_OSS |
| MidiInDev[wDevID].incLen = 0; |
| MidiInDev[wDevID].startTime = 0; |
| #endif /* HAVE_OSS */ |
| if (MIDI_NotifyClient(wDevID, MIM_OPEN, 0L, 0L) != MMSYSERR_NOERROR) { |
| WARN(midi,"can't notify client !\n"); |
| return MMSYSERR_INVALPARAM; |
| } |
| return MMSYSERR_NOERROR; |
| } |
| |
| /************************************************************************** |
| * midClose [internal] |
| */ |
| static DWORD midClose(WORD wDevID) |
| { |
| int ret = MMSYSERR_NOERROR; |
| |
| TRACE(midi, "(%04X);\n", wDevID); |
| |
| if (wDevID >= MAX_MIDIINDRV) { |
| WARN(midi,"wDevID too bif (%u) !\n", wDevID); |
| return MMSYSERR_BADDEVICEID; |
| } |
| if (MidiInDev[wDevID].midiDesc == 0) { |
| WARN(midi, "device not opened !\n"); |
| return MMSYSERR_ERROR; |
| } |
| if (MidiInDev[wDevID].lpQueueHdr != 0) { |
| return MIDIERR_STILLPLAYING; |
| } |
| |
| #ifdef HAVE_OSS |
| if (midiSeqFD == -1) { |
| WARN(midi,"ooops !\n"); |
| return MMSYSERR_ERROR; |
| } |
| if (--numStartedMidiIn == 0) { |
| TRACE(midi, "Stopping timer for midi-in\n"); |
| if (!KillTimer32(0, midiInTimerID)) { |
| WARN(midi, "Couldn't stop timer for midi-in\n"); |
| } |
| midiInTimerID = 0; |
| } |
| midiCloseSeq(); |
| #else /* HAVE_OSS */ |
| if (MidiInDev[wDevID].unixdev == 0) { |
| WARN(midi,"ooops !\n"); |
| return MMSYSERR_ERROR; |
| } |
| close(MidiInDev[wDevID].unixdev); |
| MidiInDev[wDevID].unixdev = 0; |
| #endif /* HAVE_OSS */ |
| MidiInDev[wDevID].bufsize = 0; |
| if (MIDI_NotifyClient(wDevID, MIM_CLOSE, 0L, 0L) != MMSYSERR_NOERROR) { |
| WARN(midi,"can't notify client !\n"); |
| ret = MMSYSERR_INVALPARAM; |
| } |
| MidiInDev[wDevID].midiDesc = 0; |
| return ret; |
| } |
| |
| /************************************************************************** |
| * midAddBuffer [internal] |
| */ |
| static DWORD midAddBuffer(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize) |
| { |
| TRACE(midi, "(%04X, %p, %08lX);\n", wDevID, lpMidiHdr, dwSize); |
| |
| if (lpMidiHdr == NULL) return MMSYSERR_INVALPARAM; |
| if (sizeof(MIDIHDR) != dwSize) return MMSYSERR_INVALPARAM; |
| if (lpMidiHdr->dwBufferLength == 0) return MMSYSERR_INVALPARAM; |
| if (lpMidiHdr->dwFlags & MHDR_INQUEUE) return MIDIERR_STILLPLAYING; |
| if (!(lpMidiHdr->dwFlags & MHDR_PREPARED)) return MIDIERR_UNPREPARED; |
| |
| if (MidiInDev[wDevID].lpQueueHdr == 0) { |
| MidiInDev[wDevID].lpQueueHdr = lpMidiHdr; |
| } else { |
| LPMIDIHDR ptr; |
| |
| for (ptr = MidiInDev[wDevID].lpQueueHdr; |
| ptr->lpNext != 0; |
| ptr = (LPMIDIHDR)ptr->lpNext); |
| ptr->lpNext = (struct midihdr_tag*)lpMidiHdr; |
| } |
| return MMSYSERR_NOERROR; |
| } |
| |
| /************************************************************************** |
| * midPrepare [internal] |
| */ |
| static DWORD midPrepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize) |
| { |
| TRACE(midi, "(%04X, %p, %08lX);\n", wDevID, lpMidiHdr, dwSize); |
| |
| if (dwSize != sizeof(MIDIHDR) || lpMidiHdr == 0 || |
| lpMidiHdr->lpData == 0 || lpMidiHdr->dwFlags != 0 || |
| lpMidiHdr->dwBufferLength >= 0x10000ul) |
| return MMSYSERR_INVALPARAM; |
| |
| lpMidiHdr->lpNext = 0; |
| lpMidiHdr->dwFlags |= MHDR_PREPARED; |
| lpMidiHdr->dwBytesRecorded = 0; |
| |
| return MMSYSERR_NOERROR; |
| } |
| |
| /************************************************************************** |
| * midUnprepare [internal] |
| */ |
| static DWORD midUnprepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize) |
| { |
| TRACE(midi, "(%04X, %p, %08lX);\n", wDevID, lpMidiHdr, dwSize); |
| |
| if (dwSize != sizeof(MIDIHDR) || lpMidiHdr == 0 || |
| lpMidiHdr->lpData == 0 || lpMidiHdr->dwBufferLength >= 0x10000ul) |
| return MMSYSERR_INVALPARAM; |
| |
| if (!(lpMidiHdr->dwFlags & MHDR_PREPARED)) return MIDIERR_UNPREPARED; |
| if (lpMidiHdr->dwFlags & MHDR_INQUEUE) return MIDIERR_STILLPLAYING; |
| |
| lpMidiHdr->dwFlags &= ~MHDR_PREPARED; |
| |
| return MMSYSERR_NOERROR; |
| } |
| |
| /************************************************************************** |
| * midReset [internal] |
| */ |
| static DWORD midReset(WORD wDevID) |
| { |
| DWORD dwTime = GetTickCount(); |
| |
| TRACE(midi, "(%04X);\n", wDevID); |
| |
| while (MidiInDev[wDevID].lpQueueHdr) { |
| MidiInDev[wDevID].lpQueueHdr->dwFlags &= ~MHDR_INQUEUE; |
| MidiInDev[wDevID].lpQueueHdr->dwFlags |= MHDR_DONE; |
| if (MIDI_NotifyClient(wDevID, MIM_LONGDATA, |
| (DWORD)MidiInDev[wDevID].lpQueueHdr, dwTime) != MMSYSERR_NOERROR) { |
| WARN(midi, "Couldn't notify client\n"); |
| } |
| MidiInDev[wDevID].lpQueueHdr = (LPMIDIHDR)MidiInDev[wDevID].lpQueueHdr->lpNext; |
| } |
| |
| return MMSYSERR_NOERROR; |
| } |
| |
| |
| /************************************************************************** |
| * midStart [internal] |
| */ |
| static DWORD midStart(WORD wDevID) |
| { |
| TRACE(midi, "(%04X);\n", wDevID); |
| |
| /* FIXME : should test value of wDevID */ |
| |
| #ifdef HAVE_OSS |
| MidiInDev[wDevID].state = 1; |
| MidiInDev[wDevID].startTime = GetTickCount(); |
| return MMSYSERR_NOERROR; |
| #else |
| return MMSYSERR_NOTENABLED; |
| #endif /* HAVE_OSS */ |
| } |
| |
| /************************************************************************** |
| * midStop [internal] |
| */ |
| static DWORD midStop(WORD wDevID) |
| { |
| TRACE(midi, "(%04X);\n", wDevID); |
| |
| /* FIXME : should test value of wDevID */ |
| |
| #ifdef HAVE_OSS |
| MidiInDev[wDevID].state = 0; |
| return MMSYSERR_NOERROR; |
| #else /* HAVE_OSS */ |
| return MMSYSERR_NOTENABLED; |
| #endif /* HAVE_OSS */ |
| } |
| |
| /************************************************************************** |
| * midMessage [sample driver] |
| */ |
| DWORD WINAPI midMessage(WORD wDevID, WORD wMsg, DWORD dwUser, |
| DWORD dwParam1, DWORD dwParam2) |
| { |
| TRACE(midi, "(%04X, %04X, %08lX, %08lX, %08lX);\n", |
| wDevID, wMsg, dwUser, dwParam1, dwParam2); |
| switch (wMsg) { |
| case MIDM_OPEN: |
| return midOpen(wDevID,(LPMIDIOPENDESC)dwParam1, dwParam2); |
| case MIDM_CLOSE: |
| return midClose(wDevID); |
| case MIDM_ADDBUFFER: |
| return midAddBuffer(wDevID,(LPMIDIHDR)dwParam1, dwParam2); |
| case MIDM_PREPARE: |
| return midPrepare(wDevID,(LPMIDIHDR)dwParam1, dwParam2); |
| case MIDM_UNPREPARE: |
| return midUnprepare(wDevID,(LPMIDIHDR)dwParam1, dwParam2); |
| case MIDM_GETDEVCAPS: |
| return midGetDevCaps(wDevID,(LPMIDIINCAPS16)dwParam1,dwParam2); |
| case MIDM_GETNUMDEVS: |
| return MIDM_NUMDEVS; |
| case MIDM_RESET: |
| return midReset(wDevID); |
| case MIDM_START: |
| return midStart(wDevID); |
| case MIDM_STOP: |
| return midStop(wDevID); |
| default: |
| TRACE(midi, "Unsupported message\n"); |
| } |
| return MMSYSERR_NOTSUPPORTED; |
| } |
| |
| /*-----------------------------------------------------------------------*/ |
| |
| #ifdef HAVE_OSS |
| |
| typedef struct sVoice { |
| int note; /* 0 means not used */ |
| int channel; |
| unsigned cntMark : 30, |
| status : 2; |
| #define sVS_UNUSED 0 |
| #define sVS_PLAYING 1 |
| #define sVS_SUSTAINED 2 |
| } sVoice; |
| |
| typedef struct sChannel { |
| int program; |
| |
| int bender; |
| int benderRange; |
| /* controlers */ |
| int bank; /* CTL_BANK_SELECT */ |
| int volume; /* CTL_MAIN_VOLUME */ |
| int balance; /* CTL_BALANCE */ |
| int expression; /* CTL_EXPRESSION */ |
| int sustain; /* CTL_SUSTAIN */ |
| |
| unsigned char nrgPmtMSB; /* Non register Parameters */ |
| unsigned char nrgPmtLSB; |
| unsigned char regPmtMSB; /* Non register Parameters */ |
| unsigned char regPmtLSB; |
| } sChannel; |
| |
| typedef struct sFMextra { |
| unsigned counter; |
| int drumSetMask; |
| sChannel channel[16]; /* MIDI has only 16 channels */ |
| sVoice voice[1]; /* dyn allocated according to sound card */ |
| /* do not append fields below voice[1] since the size of this structure |
| * depends on the number of available voices on the FM synth... |
| */ |
| } sFMextra; |
| |
| extern unsigned char midiFMInstrumentPatches[16 * 128]; |
| extern unsigned char midiFMDrumsPatches [16 * 128]; |
| |
| /************************************************************************** |
| * modFMLoad [internal] |
| */ |
| static int modFMLoad(int dev) |
| { |
| int i; |
| struct sbi_instrument sbi; |
| |
| sbi.device = dev; |
| sbi.key = FM_PATCH; |
| |
| memset(sbi.operators + 16, 0, 16); |
| for (i = 0; i < 128; i++) { |
| sbi.channel = i; |
| memcpy(sbi.operators, midiFMInstrumentPatches + i * 16, 16); |
| |
| if (write(midiSeqFD, (char*)&sbi, sizeof(sbi)) == -1) { |
| WARN(midi, "Couldn't write patch for instrument %d (%d)!\n", sbi.channel, errno); |
| return -1; |
| } |
| } |
| for (i = 0; i < 128; i++) { |
| sbi.channel = 128 + i; |
| memcpy(sbi.operators, midiFMDrumsPatches + i * 16, 16); |
| |
| if (write(midiSeqFD, (char*)&sbi, sizeof(sbi)) == -1) { |
| WARN(midi, "Couldn't write patch for drum %d (%d)!\n", sbi.channel, errno); |
| return -1; |
| } |
| } |
| return 0; |
| } |
| |
| /************************************************************************** |
| * modFMReset [internal] |
| */ |
| static void modFMReset(WORD wDevID) |
| { |
| sFMextra* extra = (sFMextra*)MidiOutDev[wDevID].lpExtra; |
| sVoice* voice = extra->voice; |
| sChannel* channel = extra->channel; |
| int i; |
| |
| for (i = 0; i < midiOutDevices[wDevID]->wVoices; i++) { |
| if (voice[i].status != sVS_UNUSED) { |
| SEQ_STOP_NOTE(wDevID, i, voice[i].note, 64); |
| } |
| SEQ_KEY_PRESSURE(wDevID, i, 127, 0); |
| SEQ_CONTROL(wDevID, i, SEQ_VOLMODE, VOL_METHOD_LINEAR); |
| voice[i].note = 0; |
| voice[i].channel = -1; |
| voice[i].cntMark = 0; |
| voice[i].status = sVS_UNUSED; |
| } |
| for (i = 0; i < 16; i++) { |
| channel[i].program = 0; |
| channel[i].bender = 8192; |
| channel[i].benderRange = 2; |
| channel[i].bank = 0; |
| channel[i].volume = 127; |
| channel[i].balance = 64; |
| channel[i].expression = 0; |
| channel[i].sustain = 0; |
| } |
| extra->counter = 0; |
| extra->drumSetMask = 1 << 9; /* channel 10 is normally drums, sometimes 16 also */ |
| SEQ_DUMPBUF(); |
| } |
| |
| #define IS_DRUM_CHANNEL(_xtra, _chn) ((_xtra)->drumSetMask & (1 << (_chn))) |
| |
| #endif /* HAVE_OSS */ |
| |
| /************************************************************************** |
| * modGetDevCaps [internal] |
| */ |
| static DWORD modGetDevCaps(WORD wDevID, LPMIDIOUTCAPS16 lpCaps, |
| DWORD dwSize) |
| { |
| TRACE(midi, "(%04X, %p, %08lX);\n", wDevID, lpCaps, dwSize); |
| if (wDevID == (WORD) MIDI_MAPPER) { |
| lpCaps->wMid = 0x00FF; /* Manufac ID */ |
| lpCaps->wPid = 0x0001; /* Product ID */ |
| lpCaps->vDriverVersion = 0x001; /* Product Version */ |
| strcpy(lpCaps->szPname, "MIDI Mapper (not functional yet)"); |
| /* FIXME Does it make any difference ? */ |
| lpCaps->wTechnology = MOD_FMSYNTH; |
| lpCaps->wVoices = 14; /* FIXME */ |
| lpCaps->wNotes = 14; /* FIXME */ |
| /* FIXME Does it make any difference ? */ |
| lpCaps->dwSupport = MIDICAPS_VOLUME|MIDICAPS_LRVOLUME; |
| } else { |
| LPMIDIOUTCAPS16 tmplpCaps; |
| |
| if (wDevID >= MODM_NUMDEVS) { |
| TRACE(midi, "MAX_MIDIOUTDRV reached !\n"); |
| return MMSYSERR_BADDEVICEID; |
| } |
| /* FIXME There is a way to do it so easily, but I'm too |
| * sleepy to think and I want to test |
| */ |
| |
| tmplpCaps = midiOutDevices[wDevID]; |
| lpCaps->wMid = tmplpCaps->wMid; |
| lpCaps->wPid = tmplpCaps->wPid; |
| lpCaps->vDriverVersion = tmplpCaps->vDriverVersion; |
| strcpy(lpCaps->szPname, tmplpCaps->szPname); |
| lpCaps->wTechnology = tmplpCaps->wTechnology; |
| lpCaps->wVoices = tmplpCaps->wVoices; |
| lpCaps->wNotes = tmplpCaps->wNotes; |
| lpCaps->dwSupport = tmplpCaps->dwSupport; |
| } |
| return MMSYSERR_NOERROR; |
| } |
| |
| /************************************************************************** |
| * modOpen [internal] |
| */ |
| static DWORD modOpen(WORD wDevID, LPMIDIOPENDESC lpDesc, DWORD dwFlags) |
| { |
| TRACE(midi, "(%04X, %p, %08lX);\n", wDevID, lpDesc, dwFlags); |
| if (lpDesc == NULL) { |
| WARN(midi, "Invalid Parameter !\n"); |
| return MMSYSERR_INVALPARAM; |
| } |
| if (wDevID >= MAX_MIDIOUTDRV) { |
| TRACE(midi,"MAX_MIDIOUTDRV reached !\n"); |
| return MMSYSERR_BADDEVICEID; |
| } |
| if (MidiOutDev[wDevID].midiDesc != 0) { |
| WARN(midi, "device already open !\n"); |
| return MMSYSERR_ALLOCATED; |
| } |
| if ((dwFlags & ~CALLBACK_TYPEMASK) != 0) { |
| WARN(midi, "bad dwFlags\n"); |
| return MMSYSERR_INVALFLAG; |
| } |
| |
| #ifdef HAVE_OSS |
| MidiOutDev[wDevID].lpExtra = 0; |
| |
| switch (midiOutDevices[wDevID]->wTechnology) { |
| case MOD_FMSYNTH: |
| { |
| void* extra = xmalloc(sizeof(struct sFMextra) + |
| sizeof(struct sVoice) * |
| (midiOutDevices[wDevID]->wVoices - 1)); |
| |
| if (extra == 0) { |
| WARN(midi, "can't alloc extra data !\n"); |
| return MMSYSERR_NOMEM; |
| } |
| MidiOutDev[wDevID].lpExtra = extra; |
| if (midiOpenSeq() < 0) { |
| MidiOutDev[wDevID].lpExtra = 0; |
| free(extra); |
| return MMSYSERR_ERROR; |
| } |
| if (modFMLoad(wDevID) < 0) { |
| midiCloseSeq(); |
| MidiOutDev[wDevID].lpExtra = 0; |
| free(extra); |
| return MMSYSERR_ERROR; |
| } |
| modFMReset(wDevID); |
| } |
| break; |
| case MOD_MIDIPORT: |
| if (midiOpenSeq() < 0) { |
| return MMSYSERR_ALLOCATED; |
| } |
| break; |
| default: |
| WARN(midi,"Technology not supported (yet) %d !\n", |
| midiOutDevices[wDevID]->wTechnology); |
| return MMSYSERR_NOTENABLED; |
| } |
| #else /* HAVE_OSS */ |
| { |
| int midi = open (MIDI_DEV, O_WRONLY, 0); |
| MidiOutDev[wDevID].unixdev = 0; |
| if (midi == -1) { |
| WARN(midi, "can't open !\n"); |
| return MMSYSERR_ALLOCATED; |
| } |
| MidiOutDev[wDevID].unixdev = midi; |
| } |
| #endif /* HAVE_OSS */ |
| |
| MidiOutDev[wDevID].wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK); |
| switch (MidiOutDev[wDevID].wFlags) { |
| case DCB_NULL: |
| TRACE(midi,"CALLBACK_NULL !\n"); |
| break; |
| case DCB_WINDOW: |
| TRACE(midi, "CALLBACK_WINDOW !\n"); |
| break; |
| case DCB_TASK: |
| TRACE(midi, "CALLBACK_TASK !\n"); |
| break; |
| case DCB_FUNCTION: |
| TRACE(midi, "CALLBACK_FUNCTION !\n"); |
| break; |
| } |
| MidiOutDev[wDevID].lpQueueHdr = NULL; |
| MidiOutDev[wDevID].dwTotalPlayed = 0; |
| MidiOutDev[wDevID].bufsize = 0x3FFF; |
| MidiOutDev[wDevID].midiDesc = lpDesc; |
| |
| if (MIDI_NotifyClient(wDevID, MOM_OPEN, 0L, 0L) != MMSYSERR_NOERROR) { |
| WARN(midi,"can't notify client !\n"); |
| return MMSYSERR_INVALPARAM; |
| } |
| TRACE(midi, "Succesful !\n"); |
| return MMSYSERR_NOERROR; |
| } |
| |
| |
| /************************************************************************** |
| * modClose [internal] |
| */ |
| static DWORD modClose(WORD wDevID) |
| { |
| int ret = MMSYSERR_NOERROR; |
| |
| TRACE(midi, "(%04X);\n", wDevID); |
| |
| if (MidiOutDev[wDevID].midiDesc == 0) { |
| WARN(midi, "device not opened !\n"); |
| return MMSYSERR_ERROR; |
| } |
| /* FIXME: should test that no pending buffer is still in the queue for |
| * playing */ |
| |
| #ifdef HAVE_OSS |
| if (midiSeqFD == -1) { |
| WARN(midi,"can't close !\n"); |
| return MMSYSERR_ERROR; |
| } |
| |
| switch (midiOutDevices[wDevID]->wTechnology) { |
| case MOD_FMSYNTH: |
| case MOD_MIDIPORT: |
| midiCloseSeq(); |
| break; |
| default: |
| WARN(midi,"Technology not supported (yet) %d !\n", |
| midiOutDevices[wDevID]->wTechnology); |
| return MMSYSERR_NOTENABLED; |
| } |
| |
| if (MidiOutDev[wDevID].lpExtra != 0) { |
| free(MidiOutDev[wDevID].lpExtra); |
| MidiOutDev[wDevID].lpExtra = 0; |
| } |
| #else |
| if (MidiOutDev[wDevID].unixdev == 0) { |
| WARN(midi,"can't close !\n"); |
| return MMSYSERR_NOTENABLED; |
| } |
| close(MidiOutDev[wDevID].unixdev); |
| MidiOutDev[wDevID].unixdev = 0; |
| #endif /* HAVE_OSS */ |
| |
| MidiOutDev[wDevID].bufsize = 0; |
| if (MIDI_NotifyClient(wDevID, MOM_CLOSE, 0L, 0L) != MMSYSERR_NOERROR) { |
| WARN(midi,"can't notify client !\n"); |
| ret = MMSYSERR_INVALPARAM; |
| } |
| MidiOutDev[wDevID].midiDesc = 0; |
| return ret; |
| } |
| |
| /************************************************************************** |
| * modData [internal] |
| */ |
| static DWORD modData(WORD wDevID, DWORD dwParam) |
| { |
| WORD evt = LOBYTE(LOWORD(dwParam)); |
| WORD d1 = HIBYTE(LOWORD(dwParam)); |
| WORD d2 = LOBYTE(HIWORD(dwParam)); |
| |
| TRACE(midi, "(%04X, %08lX);\n", wDevID, dwParam); |
| |
| #ifdef HAVE_OSS |
| if (midiSeqFD == -1) { |
| WARN(midi,"can't play !\n"); |
| return MIDIERR_NODEVICE; |
| } |
| switch (midiOutDevices[wDevID]->wTechnology) { |
| case MOD_FMSYNTH: |
| /* FIXME: |
| * - chorus depth controller is not used |
| */ |
| { |
| sFMextra* extra = (sFMextra*)MidiOutDev[wDevID].lpExtra; |
| sVoice* voice = extra->voice; |
| sChannel* channel = extra->channel; |
| int chn = (evt & 0x0F); |
| int i, nv; |
| |
| switch (evt & 0xF0) { |
| case MIDI_NOTEOFF: |
| for (i = 0; i < midiOutDevices[wDevID]->wVoices; i++) { |
| /* don't stop sustained notes */ |
| if (voice[i].status == sVS_PLAYING && voice[i].channel == chn && voice[i].note == d1) { |
| voice[i].status = sVS_UNUSED; |
| SEQ_STOP_NOTE(wDevID, i, d1, d2); |
| } |
| } |
| break; |
| case MIDI_NOTEON: |
| if (d2 == 0) { /* note off if velocity == 0 */ |
| for (i = 0; i < midiOutDevices[wDevID]->wVoices; i++) { |
| /* don't stop sustained notes */ |
| if (voice[i].status == sVS_PLAYING && voice[i].channel == chn && voice[i].note == d1) { |
| voice[i].status = sVS_UNUSED; |
| SEQ_STOP_NOTE(wDevID, i, d1, 64); |
| } |
| } |
| break; |
| } |
| /* finding out in this order : |
| * - an empty voice |
| * - if replaying the same note on the same channel |
| * - the older voice (LRU) |
| */ |
| for (i = nv = 0; i < midiOutDevices[wDevID]->wVoices; i++) { |
| if (voice[i].status == sVS_UNUSED || |
| (voice[i].note == d1 && voice[i].channel == chn)) { |
| nv = i; |
| break; |
| } |
| if (voice[i].cntMark < voice[0].cntMark) { |
| nv = i; |
| } |
| } |
| TRACE(midi, |
| "playing on voice=%d, pgm=%d, pan=0x%02X, vol=0x%02X, " |
| "bender=0x%02X, note=0x%02X, vel=0x%02X\n", |
| nv, channel[chn].program, |
| channel[chn].balance, |
| channel[chn].volume, |
| channel[chn].bender, d1, d2); |
| |
| SEQ_SET_PATCH(wDevID, nv, IS_DRUM_CHANNEL(extra, chn) ? |
| (128 + d1) : channel[chn].program); |
| SEQ_BENDER_RANGE(wDevID, nv, channel[chn].benderRange * 100); |
| SEQ_BENDER(wDevID, nv, channel[chn].bender); |
| SEQ_CONTROL(wDevID, nv, CTL_PAN, channel[chn].balance); |
| SEQ_CONTROL(wDevID, nv, CTL_EXPRESSION, channel[chn].expression); |
| #if 0 |
| /* FIXME: does not really seem to work on my SB card and |
| * screws everything up... so lay it down |
| */ |
| SEQ_CONTROL(wDevID, nv, CTL_MAIN_VOLUME, channel[chn].volume); |
| #endif |
| SEQ_START_NOTE(wDevID, nv, d1, d2); |
| voice[nv].status = channel[chn].sustain ? sVS_SUSTAINED : sVS_PLAYING; |
| voice[nv].note = d1; |
| voice[nv].channel = chn; |
| voice[nv].cntMark = extra->counter++; |
| break; |
| case MIDI_KEY_PRESSURE: |
| for (i = 0; i < midiOutDevices[wDevID]->wVoices; i++) { |
| if (voice[i].status != sVS_UNUSED && voice[i].channel == chn && voice[i].note == d1) { |
| SEQ_KEY_PRESSURE(wDevID, i, d1, d2); |
| } |
| } |
| break; |
| case MIDI_CTL_CHANGE: |
| switch (d1) { |
| case CTL_BANK_SELECT: channel[chn].bank = d2; break; |
| case CTL_MAIN_VOLUME: channel[chn].volume = d2; break; |
| case CTL_PAN: channel[chn].balance = d2; break; |
| case CTL_EXPRESSION: channel[chn].expression = d2; break; |
| case CTL_SUSTAIN: channel[chn].sustain = d2; |
| if (d2) { |
| for (i = 0; i < midiOutDevices[wDevID]->wVoices; i++) { |
| if (voice[i].status == sVS_PLAYING && voice[i].channel == chn) { |
| voice[i].status = sVS_SUSTAINED; |
| } |
| } |
| } else { |
| for (i = 0; i < midiOutDevices[wDevID]->wVoices; i++) { |
| if (voice[i].status == sVS_SUSTAINED && voice[i].channel == chn) { |
| voice[i].status = sVS_UNUSED; |
| SEQ_STOP_NOTE(wDevID, i, voice[i].note, 64); |
| } |
| } |
| } |
| break; |
| case CTL_NONREG_PARM_NUM_LSB: channel[chn].nrgPmtLSB = d2; break; |
| case CTL_NONREG_PARM_NUM_MSB: channel[chn].nrgPmtMSB = d2; break; |
| case CTL_REGIST_PARM_NUM_LSB: channel[chn].regPmtLSB = d2; break; |
| case CTL_REGIST_PARM_NUM_MSB: channel[chn].regPmtMSB = d2; break; |
| |
| case CTL_DATA_ENTRY: |
| switch ((channel[chn].regPmtMSB << 8) | channel[chn].regPmtLSB) { |
| case 0x0000: |
| if (channel[chn].benderRange != d2) { |
| channel[chn].benderRange = d2; |
| for (i = 0; i < midiOutDevices[wDevID]->wVoices; i++) { |
| if (voice[i].channel == chn) { |
| SEQ_BENDER_RANGE(wDevID, i, channel[chn].benderRange); |
| } |
| } |
| } |
| break; |
| |
| case 0x7F7F: |
| channel[chn].benderRange = 2; |
| for (i = 0; i < midiOutDevices[wDevID]->wVoices; i++) { |
| if (voice[i].channel == chn) { |
| SEQ_BENDER_RANGE(wDevID, i, channel[chn].benderRange); |
| } |
| } |
| break; |
| default: |
| TRACE(midi, "Data entry: regPmt=0x%02x%02x, nrgPmt=0x%02x%02x with %x\n", |
| channel[chn].regPmtMSB, channel[chn].regPmtLSB, |
| channel[chn].nrgPmtMSB, channel[chn].nrgPmtLSB, |
| d2); |
| break; |
| } |
| break; |
| |
| case 0x78: /* all sounds off */ |
| for (i = 0; i < midiOutDevices[wDevID]->wVoices; i++) { |
| if (voice[i].status != sVS_UNUSED && voice[i].channel == chn) { |
| voice[i].status = sVS_UNUSED; |
| SEQ_STOP_NOTE(wDevID, i, voice[i].note, 0); |
| } |
| } |
| break; |
| case 0x7B: /* all notes off */ |
| for (i = 0; i < midiOutDevices[wDevID]->wVoices; i++) { |
| if (voice[i].status == sVS_PLAYING && voice[i].channel == chn) { |
| voice[i].status = sVS_UNUSED; |
| SEQ_STOP_NOTE(wDevID, i, voice[i].note, 0); |
| } |
| } |
| break; |
| default: |
| TRACE(midi, "Dropping MIDI control event 0x%02x(%02x) on channel %d\n", |
| d1, d2, chn); |
| break; |
| } |
| break; |
| case MIDI_PGM_CHANGE: |
| channel[chn].program = d1; |
| break; |
| case MIDI_CHN_PRESSURE: |
| for (i = 0; i < midiOutDevices[wDevID]->wVoices; i++) { |
| if (voice[i].status != sVS_UNUSED && voice[i].channel == chn) { |
| SEQ_KEY_PRESSURE(wDevID, i, voice[i].note, d1); |
| } |
| } |
| break; |
| case MIDI_PITCH_BEND: |
| channel[chn].bender = (d2 << 7) + d1; |
| for (i = 0; i < midiOutDevices[wDevID]->wVoices; i++) { |
| if (voice[i].channel == chn) { |
| SEQ_BENDER(wDevID, i, channel[chn].bender); |
| } |
| } |
| break; |
| case MIDI_SYSTEM_PREFIX: |
| switch (evt & 0x0F) { |
| case 0x0F: /* Reset */ |
| modFMReset(wDevID); |
| break; |
| default: |
| WARN(midi, |
| "Unsupported (yet) system event %02x\n", |
| evt & 0x0F); |
| } |
| break; |
| default: |
| WARN(midi, "Internal error, shouldn't happen (event=%08x)\n", evt & 0xF0); |
| return MMSYSERR_NOTENABLED; |
| } |
| } |
| break; |
| case MOD_MIDIPORT: |
| { |
| int dev = wDevID - MODM_NUMFMSYNTHDEVS; |
| if (dev < 0) { |
| WARN(midi, "Internal error on devID (%u) !\n", wDevID); |
| return MIDIERR_NODEVICE; |
| } |
| |
| switch (evt & 0xF0) { |
| case MIDI_NOTEOFF: |
| case MIDI_NOTEON: |
| case MIDI_KEY_PRESSURE: |
| case MIDI_CTL_CHANGE: |
| case MIDI_PITCH_BEND: |
| SEQ_MIDIOUT(dev, evt); |
| SEQ_MIDIOUT(dev, d1); |
| SEQ_MIDIOUT(dev, d2); |
| break; |
| case MIDI_PGM_CHANGE: |
| case MIDI_CHN_PRESSURE: |
| SEQ_MIDIOUT(dev, evt); |
| SEQ_MIDIOUT(dev, d1); |
| break; |
| case MIDI_SYSTEM_PREFIX: |
| switch (evt & 0x0F) { |
| case 0x00: /* System Exclusive, don't do it on modData, |
| * should require modLongData*/ |
| case 0x01: /* Undefined */ |
| case 0x04: /* Undefined. */ |
| case 0x05: /* Undefined. */ |
| case 0x07: /* End of Exclusive. */ |
| case 0x09: /* Undefined. */ |
| case 0x0D: /* Undefined. */ |
| break; |
| case 0x06: /* Tune Request */ |
| case 0x08: /* Timing Clock. */ |
| case 0x0A: /* Start. */ |
| case 0x0B: /* Continue */ |
| case 0x0C: /* Stop */ |
| case 0x0E: /* Active Sensing. */ |
| SEQ_MIDIOUT(dev, evt); |
| break; |
| case 0x0F: /* Reset */ |
| /* SEQ_MIDIOUT(dev, evt); |
| this other way may be better */ |
| SEQ_MIDIOUT(dev, MIDI_SYSTEM_PREFIX); |
| SEQ_MIDIOUT(dev, 0x7e); |
| SEQ_MIDIOUT(dev, 0x7f); |
| SEQ_MIDIOUT(dev, 0x09); |
| SEQ_MIDIOUT(dev, 0x01); |
| SEQ_MIDIOUT(dev, 0xf7); |
| break; |
| case 0x03: /* Song Select. */ |
| SEQ_MIDIOUT(dev, evt); |
| SEQ_MIDIOUT(dev, d1); |
| case 0x02: /* Song Position Pointer. */ |
| SEQ_MIDIOUT(dev, evt); |
| SEQ_MIDIOUT(dev, d1); |
| SEQ_MIDIOUT(dev, d2); |
| } |
| break; |
| } |
| } |
| break; |
| default: |
| WARN(midi, "Technology not supported (yet) %d !\n", |
| midiOutDevices[wDevID]->wTechnology); |
| return MMSYSERR_NOTENABLED; |
| } |
| |
| SEQ_DUMPBUF(); |
| #else |
| if (MidiOutDev[wDevID].unixdev == 0) { |
| WARN(midi,"can't play !\n"); |
| return MIDIERR_NODEVICE; |
| } |
| { |
| WORD event = LOWORD(dwParam); |
| if (write (MidiOutDev[wDevID].unixdev, |
| &event, sizeof(event)) != sizeof(WORD)) { |
| WARN(midi, "error writting unixdev !\n"); |
| } |
| } |
| #endif /* HAVE_OSS */ |
| return MMSYSERR_NOERROR; |
| } |
| |
| /************************************************************************** |
| * modLongData [internal] |
| */ |
| static DWORD modLongData(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize) |
| { |
| int count; |
| |
| TRACE(midi, "(%04X, %p, %08lX);\n", wDevID, lpMidiHdr, dwSize); |
| |
| #ifdef HAVE_OSS |
| if (midiSeqFD == -1) { |
| WARN(midi,"can't play !\n"); |
| return MIDIERR_NODEVICE; |
| } |
| #else /* HAVE_OSS */ |
| if (MidiOutDev[wDevID].unixdev == 0) { |
| WARN(midi,"can't play !\n"); |
| return MIDIERR_NODEVICE; |
| } |
| #endif /* HAVE_OSS */ |
| |
| if (lpMidiHdr->lpData == NULL) |
| return MIDIERR_UNPREPARED; |
| if (!(lpMidiHdr->dwFlags & MHDR_PREPARED)) |
| return MIDIERR_UNPREPARED; |
| if (lpMidiHdr->dwFlags & MHDR_INQUEUE) |
| return MIDIERR_STILLPLAYING; |
| lpMidiHdr->dwFlags &= ~MHDR_DONE; |
| lpMidiHdr->dwFlags |= MHDR_INQUEUE; |
| |
| TRACE(midi, "dwBytesRecorded %lu !\n", lpMidiHdr->dwBytesRecorded); |
| TRACE(midi, " %02X %02X %02X %02X\n", |
| ((LPBYTE)(lpMidiHdr->reserved))[0], |
| ((LPBYTE)(lpMidiHdr->reserved))[1], |
| ((LPBYTE)(lpMidiHdr->reserved))[2], |
| ((LPBYTE)(lpMidiHdr->reserved))[3]); |
| #ifdef HAVE_OSS |
| switch (midiOutDevices[wDevID]->wTechnology) { |
| case MOD_FMSYNTH: |
| /* FIXME: I don't think there is much to do here */ |
| break; |
| case MOD_MIDIPORT: |
| if (((LPBYTE)lpMidiHdr->reserved)[0] != 0xF0) { |
| /* Send end of System Exclusive */ |
| SEQ_MIDIOUT(wDevID - MODM_NUMFMSYNTHDEVS, 0xF0); |
| TRACE(midi, "Adding missing 0xF0 marker at the begining of " |
| "system exclusive byte stream\n"); |
| } |
| for (count = 0; count < lpMidiHdr->dwBytesRecorded; count++) { |
| SEQ_MIDIOUT(wDevID - MODM_NUMFMSYNTHDEVS, |
| ((LPBYTE)lpMidiHdr->reserved)[count]); |
| } |
| if (((LPBYTE)lpMidiHdr->reserved)[count - 1] != 0xF7) { |
| /* Send end of System Exclusive */ |
| SEQ_MIDIOUT(wDevID - MODM_NUMFMSYNTHDEVS, 0xF7); |
| TRACE(midi, "Adding missing 0xF7 marker at the end of " |
| "system exclusive byte stream\n"); |
| } |
| SEQ_DUMPBUF(); |
| break; |
| default: |
| WARN(midi, "Technology not supported (yet) %d !\n", |
| midiOutDevices[wDevID]->wTechnology); |
| return MMSYSERR_NOTENABLED; |
| } |
| #else /* HAVE_OSS */ |
| { |
| LPWORD ptr = (LPWORD)lpMidiHdr->reserved; |
| int en; |
| |
| for (count = 0; count < lpMidiHdr->dwBytesRecorded; count++) { |
| if (write(MidiOutDev[wDevID].unixdev, |
| ptr, sizeof(WORD)) != sizeof(WORD)) |
| break; |
| ptr++; |
| } |
| |
| en = errno; |
| TRACE(midi, "after write count = %d\n",count); |
| if (count != lpMidiHdr->dwBytesRecorded) { |
| WARN(midi, "error writting unixdev #%d ! (%d != %ld)\n", |
| MidiOutDev[wDevID].unixdev, count, |
| lpMidiHdr->dwBytesRecorded); |
| TRACE(midi, "\terrno = %d error = %s\n",en,strerror(en)); |
| return MMSYSERR_NOTENABLED; |
| } |
| } |
| #endif /* HAVE_OSS */ |
| |
| lpMidiHdr->dwFlags &= ~MHDR_INQUEUE; |
| lpMidiHdr->dwFlags |= MHDR_DONE; |
| if (MIDI_NotifyClient(wDevID, MOM_DONE, (DWORD)lpMidiHdr, 0L) != MMSYSERR_NOERROR) { |
| WARN(midi,"can't notify client !\n"); |
| return MMSYSERR_INVALPARAM; |
| } |
| return MMSYSERR_NOERROR; |
| } |
| |
| /************************************************************************** |
| * modPrepare [internal] |
| */ |
| static DWORD modPrepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize) |
| { |
| TRACE(midi, "(%04X, %p, %08lX);\n", wDevID, lpMidiHdr, dwSize); |
| |
| #ifdef HAVE_OSS |
| if (midiSeqFD == -1) { |
| WARN(midi,"can't prepare !\n"); |
| return MMSYSERR_NOTENABLED; |
| } |
| #else /* HAVE_OSS */ |
| if (MidiOutDev[wDevID].unixdev == 0) { |
| WARN(midi,"can't prepare !\n"); |
| return MMSYSERR_NOTENABLED; |
| } |
| #endif /* HAVE_OSS */ |
| if (dwSize != sizeof(MIDIHDR) || lpMidiHdr == 0 || |
| lpMidiHdr->lpData == 0 || lpMidiHdr->dwFlags != 0 || |
| lpMidiHdr->dwBufferLength >= 0x10000ul) |
| return MMSYSERR_INVALPARAM; |
| |
| lpMidiHdr->lpNext = 0; |
| lpMidiHdr->dwFlags |= MHDR_PREPARED; |
| lpMidiHdr->dwFlags &= ~MHDR_DONE; |
| return MMSYSERR_NOERROR; |
| } |
| |
| /************************************************************************** |
| * modUnprepare [internal] |
| */ |
| static DWORD modUnprepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize) |
| { |
| TRACE(midi, "(%04X, %p, %08lX);\n", wDevID, lpMidiHdr, dwSize); |
| |
| #ifdef HAVE_OSS |
| if (midiSeqFD == -1) { |
| WARN(midi,"can't unprepare !\n"); |
| return MMSYSERR_NOTENABLED; |
| } |
| #else /* HAVE_OSS */ |
| if (MidiOutDev[wDevID].unixdev == 0) { |
| WARN(midi,"can't unprepare !\n"); |
| return MMSYSERR_NOTENABLED; |
| } |
| #endif /* HAVE_OSS */ |
| if (dwSize != sizeof(MIDIHDR) || lpMidiHdr == 0) |
| return MMSYSERR_INVALPARAM; |
| if (lpMidiHdr->dwFlags & MHDR_INQUEUE) |
| return MIDIERR_STILLPLAYING; |
| lpMidiHdr->dwFlags &= ~MHDR_PREPARED; |
| return MMSYSERR_NOERROR; |
| } |
| |
| /************************************************************************** |
| * modReset [internal] |
| */ |
| static DWORD modReset(WORD wDevID) |
| { |
| TRACE(midi, "(%04X);\n", wDevID); |
| /* FIXME: this function should : |
| * turn off every note, remove sustain on all channels |
| * remove any pending buffers |
| */ |
| return MMSYSERR_NOTENABLED; |
| } |
| |
| /************************************************************************** |
| * modMessage [sample driver] |
| */ |
| DWORD WINAPI modMessage(WORD wDevID, WORD wMsg, DWORD dwUser, |
| DWORD dwParam1, DWORD dwParam2) |
| { |
| TRACE(midi, "(%04X, %04X, %08lX, %08lX, %08lX);\n", |
| wDevID, wMsg, dwUser, dwParam1, dwParam2); |
| |
| switch (wMsg) { |
| case MODM_OPEN: |
| return modOpen(wDevID, (LPMIDIOPENDESC)dwParam1, dwParam2); |
| case MODM_CLOSE: |
| return modClose(wDevID); |
| case MODM_DATA: |
| return modData(wDevID, dwParam1); |
| case MODM_LONGDATA: |
| return modLongData(wDevID, (LPMIDIHDR)dwParam1, dwParam2); |
| case MODM_PREPARE: |
| return modPrepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2); |
| case MODM_UNPREPARE: |
| return modUnprepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2); |
| case MODM_GETDEVCAPS: |
| return modGetDevCaps(wDevID,(LPMIDIOUTCAPS16)dwParam1,dwParam2); |
| case MODM_GETNUMDEVS: |
| return MODM_NUMDEVS; |
| case MODM_GETVOLUME: |
| return 0; |
| case MODM_SETVOLUME: |
| return 0; |
| case MODM_RESET: |
| return modReset(wDevID); |
| default: |
| TRACE(midi, "Unsupported message\n"); |
| } |
| return MMSYSERR_NOTSUPPORTED; |
| } |
| |
| /************************************************************************** |
| * MIDI_DriverProc16 [sample driver] |
| */ |
| LONG MIDI_DriverProc16(DWORD dwDevID, HDRVR16 hDriv, WORD wMsg, |
| DWORD dwParam1, DWORD dwParam2) |
| { |
| switch (wMsg) { |
| case DRV_LOAD: return 1; |
| case DRV_FREE: return 1; |
| case DRV_OPEN: return 1; |
| case DRV_CLOSE: return 1; |
| case DRV_ENABLE: return 1; |
| case DRV_DISABLE: return 1; |
| case DRV_QUERYCONFIGURE: return 1; |
| case DRV_CONFIGURE: MessageBox16(0, "Sample Midi Linux Driver !", "MMLinux Driver", MB_OK); return 1; |
| case DRV_INSTALL: return DRVCNF_RESTART; |
| case DRV_REMOVE: return DRVCNF_RESTART; |
| case MCI_OPEN_DRIVER: |
| case MCI_OPEN: return MIDI_mciOpen(dwDevID, dwParam1, PTR_SEG_TO_LIN(dwParam2), FALSE); |
| case MCI_CLOSE_DRIVER: |
| case MCI_CLOSE: return MIDI_mciClose(dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)PTR_SEG_TO_LIN(dwParam2)); |
| case MCI_PLAY: return MIDI_mciPlay(dwDevID, dwParam1, (LPMCI_PLAY_PARMS)PTR_SEG_TO_LIN(dwParam2)); |
| case MCI_RECORD: return MIDI_mciRecord(dwDevID, dwParam1, (LPMCI_RECORD_PARMS)PTR_SEG_TO_LIN(dwParam2)); |
| case MCI_STOP: return MIDI_mciStop(dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)PTR_SEG_TO_LIN(dwParam2)); |
| case MCI_SET: return MIDI_mciSet(dwDevID, dwParam1, (LPMCI_SET_PARMS)PTR_SEG_TO_LIN(dwParam2)); |
| case MCI_PAUSE: return MIDI_mciPause(dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)PTR_SEG_TO_LIN(dwParam2)); |
| case MCI_RESUME: return MIDI_mciResume(dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)PTR_SEG_TO_LIN(dwParam2)); |
| case MCI_STATUS: return MIDI_mciStatus(dwDevID, dwParam1, (LPMCI_STATUS_PARMS)PTR_SEG_TO_LIN(dwParam2)); |
| case MCI_GETDEVCAPS: return MIDI_mciGetDevCaps(dwDevID, dwParam1, (LPMCI_GETDEVCAPS_PARMS)PTR_SEG_TO_LIN(dwParam2)); |
| case MCI_INFO: return MIDI_mciInfo(dwDevID, dwParam1, (LPMCI_INFO_PARMS16)PTR_SEG_TO_LIN(dwParam2)); |
| default: return DefDriverProc16(dwDevID, hDriv, wMsg, dwParam1, dwParam2); |
| } |
| } |
| |
| /************************************************************************** |
| * MIDI_DriverProc32 [sample driver] |
| */ |
| LONG MIDI_DriverProc32(DWORD dwDevID, HDRVR16 hDriv, DWORD wMsg, |
| DWORD dwParam1, DWORD dwParam2) |
| { |
| switch (wMsg) { |
| case DRV_LOAD: return 1; |
| case DRV_FREE: return 1; |
| case DRV_OPEN: return 1; |
| case DRV_CLOSE: return 1; |
| case DRV_ENABLE: return 1; |
| case DRV_DISABLE: return 1; |
| case DRV_QUERYCONFIGURE: return 1; |
| case DRV_CONFIGURE: MessageBox16(0, "Sample Midi Linux Driver !", "MMLinux Driver", MB_OK); return 1; |
| case DRV_INSTALL: return DRVCNF_RESTART; |
| case DRV_REMOVE: return DRVCNF_RESTART; |
| case MCI_OPEN_DRIVER: |
| case MCI_OPEN: return MIDI_mciOpen(dwDevID, dwParam1, (void*)dwParam2, TRUE); |
| case MCI_CLOSE_DRIVER: |
| case MCI_CLOSE: return MIDI_mciClose(dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)dwParam2); |
| case MCI_PLAY: return MIDI_mciPlay(dwDevID, dwParam1, (LPMCI_PLAY_PARMS)dwParam2); |
| case MCI_RECORD: return MIDI_mciRecord(dwDevID, dwParam1, (LPMCI_RECORD_PARMS)dwParam2); |
| case MCI_STOP: return MIDI_mciStop(dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)dwParam2); |
| case MCI_SET: return MIDI_mciSet(dwDevID, dwParam1, (LPMCI_SET_PARMS)dwParam2); |
| case MCI_PAUSE: return MIDI_mciPause(dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)dwParam2); |
| case MCI_RESUME: return MIDI_mciResume(dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)dwParam2); |
| case MCI_STATUS: return MIDI_mciStatus(dwDevID, dwParam1, (LPMCI_STATUS_PARMS)dwParam2); |
| case MCI_GETDEVCAPS: return MIDI_mciGetDevCaps(dwDevID, dwParam1, (LPMCI_GETDEVCAPS_PARMS)dwParam2); |
| case MCI_INFO: return MIDI_mciInfo(dwDevID, dwParam1, (LPMCI_INFO_PARMS16)dwParam2); |
| default: return DefDriverProc32(dwDevID, hDriv, wMsg, dwParam1, dwParam2); |
| } |
| } |
| /*-----------------------------------------------------------------------*/ |