677 lines
25 KiB
C
677 lines
25 KiB
C
|
|
||
|
/*---------------------------------------------------------------------------*/
|
||
|
/*
|
||
|
Copyright (c) 1985-1998 Microsoft Corporation
|
||
|
|
||
|
Title: mcicmds.c - Multimedia Systems Media Control Interface
|
||
|
Contains specific mci command implementations
|
||
|
|
||
|
Version: 1.00
|
||
|
|
||
|
Date: 7-MAR-1991
|
||
|
|
||
|
Author: Greg Simons
|
||
|
|
||
|
------------------------------------------------------------------------------
|
||
|
|
||
|
Change log:
|
||
|
|
||
|
DATE REV DESCRIPTION
|
||
|
----------- ----- -----------------------------------------------------------
|
||
|
7-MAR-1991 GregSi Original
|
||
|
*/
|
||
|
/*---------------------------------------------------------------------------*/
|
||
|
#define UNICODE
|
||
|
|
||
|
//MMSYSTEM
|
||
|
#define MMNOSOUND - Sound support
|
||
|
#define MMNOWAVE - Waveform support
|
||
|
#define MMNOAUX - Auxiliary output support
|
||
|
#define MMNOJOY - Joystick support
|
||
|
|
||
|
//MMDDK
|
||
|
#define NOWAVEDEV - Waveform support
|
||
|
#define NOAUXDEV - Auxiliary output support
|
||
|
#define NOJOYDEV - Joystick support
|
||
|
|
||
|
#include <windows.h>
|
||
|
#include <mmsystem.h>
|
||
|
#include <mmddk.h>
|
||
|
#include <string.h>
|
||
|
#include <wchar.h>
|
||
|
#include "mmsys.h"
|
||
|
#include "list.h"
|
||
|
#include "mciseq.h"
|
||
|
|
||
|
#ifdef WIN16
|
||
|
#define SZCODE char _based(_segname("_CODE"))
|
||
|
#else
|
||
|
#define WSZCODE WCHAR
|
||
|
#endif
|
||
|
|
||
|
PRIVATE WSZCODE aszAppName[] = L"mciseq.drv";
|
||
|
PRIVATE WSZCODE aszSystemIni[] = L"system.ini";
|
||
|
PRIVATE WSZCODE aszTrue[] = L"true";
|
||
|
PRIVATE WSZCODE aszNull[] = L"";
|
||
|
PRIVATE WSZCODE aszDisableWarning[] = L"disablewarning";
|
||
|
|
||
|
/*---------------------------------------------------------------------------*/
|
||
|
PUBLIC DWORD NEAR PASCAL msOpen(
|
||
|
pSeqStreamType FAR *lppStream,
|
||
|
MCIDEVICEID wDeviceID,
|
||
|
DWORD dwFlags,
|
||
|
LPMCI_OPEN_PARMS lpOpen)
|
||
|
{
|
||
|
DWORD dwReturn; // to be returned from this function
|
||
|
LPCWSTR lpstrFileName;
|
||
|
LPMMIOPROC pIOProc;
|
||
|
pSeqStreamType pStream;
|
||
|
|
||
|
if ((dwFlags & (MCI_OPEN_ELEMENT | MCI_OPEN_ELEMENT_ID)) == (MCI_OPEN_ELEMENT | MCI_OPEN_ELEMENT_ID))
|
||
|
return MCIERR_FLAGS_NOT_COMPATIBLE;
|
||
|
if (!(dwFlags & (MCI_OPEN_ELEMENT | MCI_OPEN_ELEMENT_ID)))
|
||
|
return 0L;
|
||
|
if (dwFlags & MCI_OPEN_SHAREABLE)
|
||
|
return MCIERR_UNSUPPORTED_FUNCTION;
|
||
|
if (dwFlags & MCI_OPEN_ELEMENT)
|
||
|
pIOProc = NULL;
|
||
|
else
|
||
|
pIOProc = *(LPMMIOPROC *)(lpOpen + 1);
|
||
|
// open the "stream" (opens file, allocs data, creates streaming task,
|
||
|
// and calls mmseq (sequencer) to have it allocate sequence structure)
|
||
|
lpstrFileName = lpOpen->lpstrElementName;
|
||
|
// Does not support 'new'
|
||
|
if (lpstrFileName != NULL && *lpstrFileName == '\0')
|
||
|
return MCIERR_FILENAME_REQUIRED;
|
||
|
dwReturn = msOpenStream(&pStream, lpstrFileName, pIOProc);
|
||
|
if (!dwReturn) {
|
||
|
MIDISEQINFO seqInfo;
|
||
|
|
||
|
pStream->wDeviceID = wDeviceID;
|
||
|
pStream->wNotifyMsg = 0;
|
||
|
// set up to remember when seq calls back
|
||
|
// tell sequencer to prepare to play (actually parses whole file
|
||
|
// and creates tempo map [which allocs memory])
|
||
|
if (midiSeqMessage(pStream->hSeq, SEQ_SETUPTOPLAY, 0L, 0L) == MIDISEQERR_NOMEM)
|
||
|
return MCIERR_OUT_OF_MEMORY;
|
||
|
// get back file division type (ppqn, smpte, etc...)
|
||
|
midiSeqMessage(pStream->hSeq, SEQ_GETINFO, (DWORD_PTR)(LPMIDISEQINFO)&seqInfo, 0L);
|
||
|
pStream->fileDivType = seqInfo.wDivType;
|
||
|
// set stream display type default based on file div type
|
||
|
switch (pStream->fileDivType) {
|
||
|
case SEQ_DIV_PPQN:
|
||
|
pStream->userDisplayType = MCI_SEQ_FORMAT_SONGPTR;
|
||
|
break;
|
||
|
case SEQ_DIV_SMPTE_24:
|
||
|
pStream->userDisplayType = MCI_FORMAT_SMPTE_24;
|
||
|
break;
|
||
|
case SEQ_DIV_SMPTE_25:
|
||
|
pStream->userDisplayType = MCI_FORMAT_SMPTE_25;
|
||
|
break;
|
||
|
case SEQ_DIV_SMPTE_30:
|
||
|
pStream->userDisplayType = MCI_FORMAT_SMPTE_30;
|
||
|
break;
|
||
|
case SEQ_DIV_SMPTE_30DROP:
|
||
|
pStream->userDisplayType = MCI_FORMAT_SMPTE_30DROP;
|
||
|
break;
|
||
|
}
|
||
|
//Force a wait until stream is initialized.
|
||
|
do {
|
||
|
Yield();
|
||
|
midiSeqMessage(pStream->hSeq, SEQ_GETINFO, (DWORD_PTR)(LPMIDISEQINFO)&seqInfo, 0L);
|
||
|
} while ((!seqInfo.bReadyToPlay) && (seqInfo.tempoMapExists) && (seqInfo.bLegalFile));
|
||
|
// (if tempo map deleted, this is a sign that a tempo map alloc failed)
|
||
|
// important: check to see if tempo map allocation failed
|
||
|
// if so, close sequence and return failure
|
||
|
mciSetDriverData(wDeviceID, (DWORD_PTR)pStream);
|
||
|
// MCI bookkeeping -- must come before close below
|
||
|
if (!seqInfo.tempoMapExists) {
|
||
|
dwReturn = MCIERR_OUT_OF_MEMORY;
|
||
|
mciDriverEntry(wDeviceID, MCI_CLOSE_DRIVER, dwFlags & ~MCI_NOTIFY, (DWORD_PTR)lpOpen);
|
||
|
} else if (!seqInfo.bLegalFile) {
|
||
|
dwReturn = MCIERR_INVALID_FILE;
|
||
|
mciDriverEntry(wDeviceID, MCI_CLOSE_DRIVER, dwFlags & ~MCI_NOTIFY, (DWORD_PTR)lpOpen);
|
||
|
} else
|
||
|
*lppStream = pStream;
|
||
|
}
|
||
|
return dwReturn;
|
||
|
}
|
||
|
|
||
|
/*---------------------------------------------------------------------------*/
|
||
|
PUBLIC DWORD NEAR PASCAL msClose(
|
||
|
pSeqStreamType pStream,
|
||
|
MCIDEVICEID wDeviceID,
|
||
|
DWORD dwFlags)
|
||
|
{
|
||
|
dprintf2(("close"));
|
||
|
if (pStream) {
|
||
|
midiSeqMessage(pStream->hSeq, SEQ_STOP, 0L, 0L);
|
||
|
dprintf4(("calling endfilestream"));
|
||
|
midiSeqMessage(pStream->hSeq, SEQ_SETPORTOFF, FALSE, 0L);
|
||
|
dprintf4(("closed port"));
|
||
|
Notify(pStream, (dwFlags == MCI_NOTIFY) ? MCI_NOTIFY_SUPERSEDED : MCI_NOTIFY_ABORTED);
|
||
|
EndFileStream(pStream);
|
||
|
dprintf4(("back from endfilestream"));
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*---------------------------------------------------------------------------*/
|
||
|
PRIVATE DWORD PASCAL NEAR OpenMidiPort(
|
||
|
pSeqStreamType pStream)
|
||
|
{
|
||
|
if ((pStream->wPortNum == MCI_SEQ_NONE) || (midiSeqMessage(pStream->hSeq, SEQ_QUERYHMIDI, 0L, 0L)))
|
||
|
return 0;
|
||
|
|
||
|
if (!midiOutGetNumDevs()) {
|
||
|
dprintf1(("OpenMidiPort - no MIDI ports present"));
|
||
|
return MCIERR_SEQ_NOMIDIPRESENT; // No midi ports present
|
||
|
}
|
||
|
|
||
|
switch ((UINT)midiSeqMessage(pStream->hSeq, SEQ_SETPORT, (DWORD)pStream->wPortNum, 0L)) {
|
||
|
case 0:
|
||
|
return 0;
|
||
|
case MMSYSERR_ALLOCATED:
|
||
|
return MCIERR_SEQ_PORT_INUSE;
|
||
|
case MMSYSERR_BADDEVICEID:
|
||
|
return MCIERR_SEQ_PORT_NONEXISTENT;
|
||
|
case MIDIERR_NODEVICE:
|
||
|
return MIDIERR_NODEVICE;
|
||
|
default:
|
||
|
return MCIERR_SEQ_NOMIDIPRESENT;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*---------------------------------------------------------------------------*/
|
||
|
PUBLIC DWORD NEAR PASCAL msPlay(pSeqStreamType pStream, MCIDEVICEID wDeviceID,
|
||
|
DWORD dwFlags, LPMCI_PLAY_PARMS lpPlay)
|
||
|
{
|
||
|
MIDISEQINFO seqInfo;
|
||
|
DWORD dwPlayFrom;
|
||
|
DWORD dwPlayTo;
|
||
|
DWORD dwReturn;
|
||
|
|
||
|
if (0 != (dwReturn = OpenMidiPort(pStream)))
|
||
|
return dwReturn;
|
||
|
|
||
|
// get info to aid in possible time format conversions (from & to)
|
||
|
midiSeqMessage(pStream->hSeq, SEQ_GETINFO, (DWORD_PTR)(LPMIDISEQINFO)&seqInfo, 0L);
|
||
|
// convert "to," if any
|
||
|
if (MCI_TO & dwFlags) {
|
||
|
// is the user typing in what he believes to be the end?
|
||
|
if (lpPlay->dwTo == CnvtTimeFromSeq(pStream, seqInfo.dwLength, &seqInfo))
|
||
|
dwPlayTo = seqInfo.dwLength; // if so, let him have it
|
||
|
else
|
||
|
dwPlayTo = CnvtTimeToSeq(pStream, lpPlay->dwTo, &seqInfo);
|
||
|
} else
|
||
|
dwPlayTo = PLAYTOEND; // has no effect
|
||
|
// convert "from," if any
|
||
|
if (MCI_FROM & dwFlags)
|
||
|
dwPlayFrom = CnvtTimeToSeq(pStream, lpPlay->dwFrom, &seqInfo);
|
||
|
else
|
||
|
dwPlayFrom = 0;
|
||
|
// complain if input out of range
|
||
|
// "to" in range [0..length]
|
||
|
if ((MCI_TO & dwFlags) && (!RangeCheck(pStream, lpPlay->dwTo)))
|
||
|
dwReturn = MCIERR_OUTOFRANGE;
|
||
|
// "from" in range [0..length]
|
||
|
else if ((MCI_FROM & dwFlags) && (!RangeCheck(pStream, lpPlay->dwFrom)))
|
||
|
dwReturn = MCIERR_OUTOFRANGE;
|
||
|
// from before to (can't play backwards!)
|
||
|
else if ((MCI_FROM & dwFlags) && (MCI_TO & dwFlags) && (dwPlayFrom > dwPlayTo))
|
||
|
dwReturn = MCIERR_OUTOFRANGE;
|
||
|
// if from not specified, current pos implied -- don't play backwards
|
||
|
else if ((!(MCI_FROM & dwFlags)) && (MCI_TO & dwFlags) && (dwPlayTo < seqInfo.dwCurrentTick))
|
||
|
dwReturn = MCIERR_OUTOFRANGE;
|
||
|
else if (MCI_FROM & dwFlags)
|
||
|
if (midiSeqMessage(pStream->hSeq, SEQ_SYNCSEEKTICKS, dwPlayFrom, 0) != MIDISEQERR_NOERROR)
|
||
|
dwReturn = MCIERR_DEVICE_NOT_READY;
|
||
|
|
||
|
if (!dwReturn) {
|
||
|
if (0 != (dwReturn = (DWORD)midiSeqMessage(pStream->hSeq, SEQ_PLAY, dwPlayTo, 0L))) {
|
||
|
// handle possible timer error (possibly due to timer.dll not loading)
|
||
|
if (dwReturn == MIDISEQERR_TIMER)
|
||
|
dwReturn = MCIERR_SEQ_TIMER;
|
||
|
} else if ((dwFlags & MCI_WAIT)) {
|
||
|
// handle "Play Wait"
|
||
|
if (pStream->hNotifyCB)
|
||
|
if (pStream->wNotifyMsg == MCI_PLAY) {
|
||
|
if (dwPlayTo == PLAYTOEND)
|
||
|
dwPlayTo = seqInfo.dwLength;
|
||
|
Notify(pStream, ((dwFlags & MCI_FROM) || (pStream->dwNotifyOldTo != dwPlayTo)) ? MCI_NOTIFY_ABORTED : MCI_NOTIFY_SUPERSEDED);
|
||
|
} else if (pStream->wNotifyMsg == MCI_SEEK)
|
||
|
Notify(pStream, MCI_NOTIFY_SUPERSEDED);
|
||
|
do {
|
||
|
Yield();
|
||
|
midiSeqMessage(pStream->hSeq, SEQ_GETINFO, (DWORD_PTR)(LPMIDISEQINFO)&seqInfo, 0L);
|
||
|
} while ((seqInfo.bPlaying) && (!mciDriverYield(wDeviceID)));
|
||
|
}
|
||
|
}
|
||
|
if (dwReturn && !seqInfo.bPlaying)
|
||
|
midiSeqMessage(pStream->hSeq, SEQ_SETPORTOFF, FALSE, 0L);
|
||
|
return dwReturn;
|
||
|
}
|
||
|
|
||
|
/*---------------------------------------------------------------------------*/
|
||
|
PUBLIC DWORD NEAR PASCAL msSeek(
|
||
|
pSeqStreamType pStream,
|
||
|
MCIDEVICEID wDeviceID,
|
||
|
DWORD dwParam1,
|
||
|
LPMCI_SEEK_PARMS lpSeek)
|
||
|
{
|
||
|
DWORD dwFlags;
|
||
|
DWORD dwSeekTo;
|
||
|
MIDISEQINFO seqInfo;
|
||
|
|
||
|
dwFlags = dwParam1 & ~(MCI_WAIT | MCI_NOTIFY); // don't consider these here
|
||
|
if (!dwFlags)
|
||
|
return MCIERR_MISSING_PARAMETER;
|
||
|
if (dwFlags != (dwFlags & (MCI_TO | MCI_SEEK_TO_START | MCI_SEEK_TO_END)))
|
||
|
return MCIERR_UNRECOGNIZED_KEYWORD;
|
||
|
midiSeqMessage(pStream->hSeq, SEQ_GETINFO, (DWORD_PTR)(LPMIDISEQINFO)&seqInfo, 0L);
|
||
|
switch (dwFlags) {
|
||
|
case MCI_TO:
|
||
|
if (!RangeCheck(pStream, lpSeek->dwTo))
|
||
|
return MCIERR_OUTOFRANGE;
|
||
|
dwSeekTo = CnvtTimeToSeq(pStream, lpSeek->dwTo, &seqInfo);
|
||
|
break;
|
||
|
case MCI_SEEK_TO_START:
|
||
|
dwSeekTo = 0L;
|
||
|
break;
|
||
|
case MCI_SEEK_TO_END:
|
||
|
dwSeekTo = seqInfo.dwLength;
|
||
|
break;
|
||
|
default:
|
||
|
return MCIERR_FLAGS_NOT_COMPATIBLE;
|
||
|
}
|
||
|
// if playing, call self to pause 1st.
|
||
|
if (seqInfo.bPlaying)
|
||
|
mciDriverEntry(wDeviceID, MCI_PAUSE, dwFlags, (DWORD_PTR)lpSeek);
|
||
|
// set up to remember when seq calls back
|
||
|
if (midiSeqMessage(pStream->hSeq, SEQ_SEEKTICKS, dwSeekTo, 0) != MIDISEQERR_NOERROR)
|
||
|
return MCIERR_DEVICE_NOT_READY;
|
||
|
else if (dwParam1 & MCI_WAIT) {
|
||
|
if (pStream->hNotifyCB)
|
||
|
if (pStream->wNotifyMsg == MCI_SEEK)
|
||
|
Notify(pStream, (pStream->dwNotifyOldTo != lpSeek->dwTo) ? MCI_NOTIFY_ABORTED : MCI_NOTIFY_SUPERSEDED);
|
||
|
else if (pStream->wNotifyMsg == MCI_PLAY)
|
||
|
Notify(pStream, MCI_NOTIFY_SUPERSEDED);
|
||
|
do {
|
||
|
midiSeqMessage(pStream->hSeq, SEQ_GETINFO, (DWORD_PTR)(LPMIDISEQINFO)&seqInfo, 0L);
|
||
|
Yield();
|
||
|
} while ((seqInfo.bSeeking) && (!mciDriverYield(wDeviceID)));
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/*---------------------------------------------------------------------------*/
|
||
|
PUBLIC DWORD NEAR PASCAL msStatus(
|
||
|
pSeqStreamType pStream,
|
||
|
MCIDEVICEID wDeviceID,
|
||
|
DWORD dwFlags,
|
||
|
LPMCI_STATUS_PARMS lpStatus)
|
||
|
{
|
||
|
DWORD dwReturn;
|
||
|
MIDISEQINFO seqInfo;
|
||
|
DWORD dwStatusReturn = 0;
|
||
|
|
||
|
if (!(dwFlags & MCI_STATUS_ITEM))
|
||
|
return MCIERR_MISSING_PARAMETER;
|
||
|
if ((dwFlags & MCI_TRACK) && (!((lpStatus->dwItem) & (MCI_STATUS_POSITION | MCI_STATUS_LENGTH))))
|
||
|
return MCIERR_DUPLICATE_FLAGS;
|
||
|
dwReturn = 0;
|
||
|
midiSeqMessage(pStream->hSeq, SEQ_GETINFO, (DWORD_PTR)(LPMIDISEQINFO)&seqInfo, 0L);
|
||
|
switch (lpStatus->dwItem) {
|
||
|
UINT wThingsPerMin;
|
||
|
UINT wResource;
|
||
|
|
||
|
case MCI_STATUS_TIME_FORMAT:
|
||
|
wResource = (UINT)pStream->userDisplayType;
|
||
|
if (wResource == MCI_SEQ_FORMAT_SONGPTR)
|
||
|
dwStatusReturn = (DWORD)MAKEMCIRESOURCE(MCI_SEQ_FORMAT_SONGPTR, MCI_SEQ_FORMAT_SONGPTR_S);
|
||
|
else
|
||
|
dwStatusReturn = (DWORD)MAKEMCIRESOURCE(wResource, wResource + MCI_FORMAT_RETURN_BASE);
|
||
|
dwReturn = MCI_RESOURCE_RETURNED;
|
||
|
break;
|
||
|
case MCI_STATUS_POSITION:
|
||
|
if (dwFlags & MCI_TRACK) {
|
||
|
if ((lpStatus->dwTrack) != 1)
|
||
|
dwReturn = MCIERR_OUTOFRANGE;
|
||
|
else
|
||
|
dwStatusReturn = 0L; //beginning of track
|
||
|
} else if (dwFlags & MCI_STATUS_START)
|
||
|
dwStatusReturn = 0L; //beginning of track
|
||
|
else // normal status position request
|
||
|
dwStatusReturn = CnvtTimeFromSeq(pStream, seqInfo.dwCurrentTick, &seqInfo);
|
||
|
if (ColonizeOutput(pStream))
|
||
|
dwReturn = MCI_COLONIZED4_RETURN;
|
||
|
break;
|
||
|
case MCI_STATUS_LENGTH:
|
||
|
if (dwFlags & MCI_TRACK)
|
||
|
if ((lpStatus->dwTrack) != 1)
|
||
|
dwReturn = MCIERR_OUTOFRANGE;
|
||
|
dwStatusReturn = CnvtTimeFromSeq(pStream, seqInfo.dwLength, &seqInfo);
|
||
|
if (ColonizeOutput(pStream))
|
||
|
dwReturn = MCI_COLONIZED4_RETURN;
|
||
|
break;
|
||
|
case MCI_STATUS_READY:
|
||
|
dwReturn = MCI_RESOURCE_RETURNED;
|
||
|
if (seqInfo.bReadyToPlay)
|
||
|
dwStatusReturn = (DWORD)MAKEMCIRESOURCE(TRUE, MCI_TRUE);
|
||
|
else
|
||
|
dwStatusReturn = (DWORD)MAKEMCIRESOURCE(FALSE, MCI_FALSE);
|
||
|
break;
|
||
|
case MCI_STATUS_NUMBER_OF_TRACKS:
|
||
|
dwStatusReturn = 1;
|
||
|
break;
|
||
|
case MCI_STATUS_MODE:
|
||
|
dwReturn = MCI_RESOURCE_RETURNED;
|
||
|
if (seqInfo.bSeeking)
|
||
|
wResource = MCI_MODE_SEEK;
|
||
|
else if (seqInfo.bPlaying)
|
||
|
wResource = MCI_MODE_PLAY;
|
||
|
else if (pStream->bLastPaused)
|
||
|
wResource = MCI_MODE_PAUSE;
|
||
|
else
|
||
|
wResource = MCI_MODE_STOP;
|
||
|
dwStatusReturn = (DWORD)MAKEMCIRESOURCE(wResource, wResource);
|
||
|
break;
|
||
|
case MCI_STATUS_MEDIA_PRESENT:
|
||
|
dwReturn = MCI_RESOURCE_RETURNED;
|
||
|
dwStatusReturn = (DWORD)MAKEMCIRESOURCE(TRUE, MCI_TRUE);
|
||
|
break;
|
||
|
case MCI_SEQ_STATUS_DIVTYPE:
|
||
|
dwReturn = MCI_RESOURCE_RETURNED;
|
||
|
switch (seqInfo.wDivType) {
|
||
|
//NB: the MCI_SEQ_DIV... codes are string ids
|
||
|
case SEQ_DIV_PPQN:
|
||
|
wResource = MCI_SEQ_DIV_PPQN;
|
||
|
break;
|
||
|
case SEQ_DIV_SMPTE_24:
|
||
|
wResource = MCI_SEQ_DIV_SMPTE_24;
|
||
|
break;
|
||
|
case SEQ_DIV_SMPTE_25:
|
||
|
wResource = MCI_SEQ_DIV_SMPTE_25;
|
||
|
break;
|
||
|
case SEQ_DIV_SMPTE_30:
|
||
|
wResource = MCI_SEQ_DIV_SMPTE_30;
|
||
|
break;
|
||
|
case SEQ_DIV_SMPTE_30DROP:
|
||
|
wResource = MCI_SEQ_DIV_SMPTE_30DROP;
|
||
|
break;
|
||
|
}
|
||
|
dwStatusReturn = (DWORD)MAKEMCIRESOURCE(wResource, wResource);
|
||
|
break;
|
||
|
case MCI_SEQ_STATUS_TEMPO:
|
||
|
// tempo comes back in microseconds per tick -- convert to
|
||
|
// more human-readable form if smpte: fps else bpm
|
||
|
wThingsPerMin = (UINT)(USecPerMinute / ((DWORD)seqInfo.wResolution * (DWORD)seqInfo.dwTempo));
|
||
|
if (seqInfo.wDivType == SEQ_DIV_PPQN)
|
||
|
dwStatusReturn = (DWORD)wThingsPerMin;
|
||
|
else
|
||
|
dwStatusReturn = (DWORD)(wThingsPerMin / 60); // things per second
|
||
|
break;
|
||
|
case MCI_SEQ_STATUS_PORT:
|
||
|
switch (pStream->wPortNum) {
|
||
|
case (UINT)MIDI_MAPPER:
|
||
|
dwReturn = MCI_RESOURCE_RETURNED;
|
||
|
dwStatusReturn = (DWORD)MAKEMCIRESOURCE(MIDI_MAPPER, MIDIMAPPER_S);
|
||
|
break;
|
||
|
case MCI_SEQ_NONE:
|
||
|
dwReturn = MCI_RESOURCE_RETURNED;
|
||
|
dwStatusReturn = (DWORD)MAKEMCIRESOURCE(MCI_SEQ_NONE, MCI_SEQ_NONE_S);
|
||
|
break;
|
||
|
default:
|
||
|
dwStatusReturn = pStream->wPortNum;
|
||
|
break;
|
||
|
}
|
||
|
break;
|
||
|
case MCI_SEQ_STATUS_SLAVE:
|
||
|
dwReturn = MCI_RESOURCE_RETURNED;
|
||
|
switch(seqInfo.wInSync) {
|
||
|
case SEQ_SYNC_NOTHING:
|
||
|
dwStatusReturn = (DWORD)MAKEMCIRESOURCE(MCI_SEQ_NONE, MCI_SEQ_NONE_S);
|
||
|
break;
|
||
|
case SEQ_SYNC_FILE:
|
||
|
dwStatusReturn = (DWORD)MAKEMCIRESOURCE(MCI_SEQ_FILE, MCI_SEQ_FILE_S);
|
||
|
break;
|
||
|
case SEQ_SYNC_MIDI:
|
||
|
dwStatusReturn = (DWORD)MAKEMCIRESOURCE(MCI_SEQ_MIDI, MCI_SEQ_MIDI_S);
|
||
|
break;
|
||
|
case SEQ_SYNC_SMPTE:
|
||
|
dwStatusReturn = (DWORD)MAKEMCIRESOURCE(MCI_SEQ_SMPTE, MCI_SEQ_SMPTE_S);
|
||
|
break;
|
||
|
}
|
||
|
break;
|
||
|
case MCI_SEQ_STATUS_MASTER:
|
||
|
dwReturn = MCI_RESOURCE_RETURNED;
|
||
|
switch(seqInfo.wOutSync) {
|
||
|
case SEQ_SYNC_NOTHING:
|
||
|
dwStatusReturn = (DWORD)MAKEMCIRESOURCE(MCI_SEQ_NONE, MCI_SEQ_NONE_S);
|
||
|
break;
|
||
|
case SEQ_SYNC_MIDI:
|
||
|
dwStatusReturn = (DWORD)MAKEMCIRESOURCE(MCI_SEQ_MIDI, MCI_SEQ_MIDI_S);
|
||
|
break;
|
||
|
case SEQ_SYNC_SMPTE:
|
||
|
dwStatusReturn = (DWORD)MAKEMCIRESOURCE(MCI_SEQ_SMPTE, MCI_SEQ_SMPTE_S);
|
||
|
break;
|
||
|
}
|
||
|
break;
|
||
|
case MCI_SEQ_STATUS_OFFSET:
|
||
|
dwStatusReturn = seqInfo.mmSmpteOffset.u.smpte.hour + ((DWORD)seqInfo.mmSmpteOffset.u.smpte.min << 8) + ((DWORD)seqInfo.mmSmpteOffset.u.smpte.sec << 16) + ((DWORD)seqInfo.mmSmpteOffset.u.smpte.frame << 24);
|
||
|
dwReturn = MCI_COLONIZED4_RETURN;
|
||
|
break;
|
||
|
case MCI_STATUS_CURRENT_TRACK:
|
||
|
dwStatusReturn = 1;
|
||
|
break;
|
||
|
default:
|
||
|
dwReturn = MCIERR_UNSUPPORTED_FUNCTION;
|
||
|
break;
|
||
|
}
|
||
|
// Return the status value in the struct
|
||
|
lpStatus->dwReturn = dwStatusReturn;
|
||
|
return dwReturn;
|
||
|
}
|
||
|
|
||
|
/*---------------------------------------------------------------------------*/
|
||
|
PUBLIC DWORD NEAR PASCAL msGetDevCaps(
|
||
|
pSeqStreamType pStream,
|
||
|
MCIDEVICEID wDeviceID,
|
||
|
DWORD dwFlags,
|
||
|
LPMCI_GETDEVCAPS_PARMS lpCapParms)
|
||
|
{
|
||
|
if (!(dwFlags & MCI_GETDEVCAPS_ITEM)) {
|
||
|
dprintf1(("msGetDevCaps - no capability requested"));
|
||
|
return MCIERR_MISSING_PARAMETER;
|
||
|
}
|
||
|
|
||
|
switch (lpCapParms->dwItem) {
|
||
|
case MCI_GETDEVCAPS_HAS_AUDIO:
|
||
|
case MCI_GETDEVCAPS_COMPOUND_DEVICE:
|
||
|
case MCI_GETDEVCAPS_USES_FILES:
|
||
|
case MCI_GETDEVCAPS_CAN_PLAY:
|
||
|
|
||
|
|
||
|
if (midiOutGetNumDevs()) {
|
||
|
dprintf2(("msGetDevCaps - %d midi out port(s) found",
|
||
|
midiOutGetNumDevs()));
|
||
|
lpCapParms->dwReturn = (DWORD)MAKEMCIRESOURCE(TRUE, MCI_TRUE);
|
||
|
} else {
|
||
|
dprintf2(("msGetDevCaps - NO midi out ports found"));
|
||
|
lpCapParms->dwReturn = (DWORD)MAKEMCIRESOURCE(FALSE, MCI_FALSE);
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case MCI_GETDEVCAPS_CAN_EJECT:
|
||
|
case MCI_GETDEVCAPS_CAN_RECORD:
|
||
|
case MCI_GETDEVCAPS_HAS_VIDEO:
|
||
|
case MCI_GETDEVCAPS_CAN_SAVE:
|
||
|
lpCapParms->dwReturn = (DWORD)MAKEMCIRESOURCE(FALSE, MCI_FALSE);
|
||
|
break;
|
||
|
|
||
|
case MCI_GETDEVCAPS_DEVICE_TYPE:
|
||
|
lpCapParms->dwReturn = (DWORD)MAKEMCIRESOURCE(MCI_DEVTYPE_SEQUENCER, MCI_DEVTYPE_SEQUENCER);
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
return MCIERR_MISSING_PARAMETER;
|
||
|
}
|
||
|
return MCI_RESOURCE_RETURNED;
|
||
|
}
|
||
|
|
||
|
/*---------------------------------------------------------------------------*/
|
||
|
PUBLIC DWORD NEAR PASCAL msInfo(
|
||
|
pSeqStreamType pStream,
|
||
|
MCIDEVICEID wDeviceID,
|
||
|
DWORD dwFlags,
|
||
|
LPMCI_INFO_PARMS lpInfo)
|
||
|
{
|
||
|
UINT wReturnLength;
|
||
|
|
||
|
if (!lpInfo->lpstrReturn)
|
||
|
return MCIERR_PARAM_OVERFLOW;
|
||
|
dwFlags &= ~(MCI_NOTIFY | MCI_WAIT);
|
||
|
if (dwFlags != (dwFlags & (MCI_INFO_PRODUCT | MCI_INFO_FILE)))
|
||
|
return MCIERR_UNRECOGNIZED_KEYWORD;
|
||
|
wReturnLength = (UINT)lpInfo->dwRetSize;
|
||
|
*(lpInfo->lpstrReturn + wReturnLength - 1) = '\0';
|
||
|
switch (dwFlags) {
|
||
|
case MCI_INFO_PRODUCT:
|
||
|
LoadString(hInstance, MSEQ_PRODUCTNAME, lpInfo->lpstrReturn, wReturnLength);
|
||
|
break;
|
||
|
case MCI_INFO_FILE:
|
||
|
if (!pStream)
|
||
|
return MCIERR_UNSUPPORTED_FUNCTION;
|
||
|
else
|
||
|
wcsncpy(lpInfo->lpstrReturn, (LPWSTR)pStream->szFilename, wReturnLength);
|
||
|
break;
|
||
|
default:
|
||
|
return MCIERR_MISSING_PARAMETER;
|
||
|
}
|
||
|
|
||
|
|
||
|
if ( *(lpInfo->lpstrReturn + wReturnLength - 1) != '\0' ) {
|
||
|
return MCIERR_PARAM_OVERFLOW;
|
||
|
}
|
||
|
else {
|
||
|
lpInfo->dwRetSize = wcslen( lpInfo->lpstrReturn );
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*---------------------------------------------------------------------------*/
|
||
|
PUBLIC DWORD NEAR PASCAL msSet(
|
||
|
pSeqStreamType pStream,
|
||
|
MCIDEVICEID wDeviceID,
|
||
|
DWORD dwFlags,
|
||
|
LPMCI_SEQ_SET_PARMS lpSetParms)
|
||
|
{
|
||
|
DWORD dwReturn;
|
||
|
|
||
|
dwFlags &= ~(MCI_NOTIFY | MCI_WAIT | MCI_SET_ON | MCI_SET_OFF);
|
||
|
if (dwFlags != (dwFlags & (MCI_SEQ_SET_TEMPO | MCI_SEQ_SET_PORT | MCI_SEQ_SET_SLAVE | MCI_SEQ_SET_MASTER | MCI_SEQ_SET_OFFSET | MCI_SET_AUDIO | MCI_SET_VIDEO | MCI_SET_DOOR_OPEN | MCI_SET_DOOR_CLOSED | MCI_SET_TIME_FORMAT)))
|
||
|
return MCIERR_UNRECOGNIZED_KEYWORD;
|
||
|
dwReturn = 0; // no error by default
|
||
|
switch (dwFlags) {
|
||
|
UINT wPort;
|
||
|
MMTIME mmOffset;
|
||
|
|
||
|
case MCI_SET_TIME_FORMAT:
|
||
|
switch ((UINT)lpSetParms->dwTimeFormat) {
|
||
|
case MCI_FORMAT_SMPTE_30DROP:
|
||
|
case MCI_FORMAT_SMPTE_30:
|
||
|
case MCI_FORMAT_SMPTE_25:
|
||
|
case MCI_FORMAT_SMPTE_24:
|
||
|
case MCI_FORMAT_MILLISECONDS:
|
||
|
pStream->userDisplayType = lpSetParms->dwTimeFormat;
|
||
|
break;
|
||
|
case MCI_SEQ_FORMAT_SONGPTR:
|
||
|
if (pStream->fileDivType == SEQ_DIV_PPQN)
|
||
|
pStream->userDisplayType = lpSetParms->dwTimeFormat;
|
||
|
else
|
||
|
dwReturn = MCIERR_SEQ_DIV_INCOMPATIBLE;
|
||
|
break;
|
||
|
default:
|
||
|
dwReturn = MCIERR_BAD_TIME_FORMAT;
|
||
|
break;
|
||
|
}
|
||
|
break;
|
||
|
case MCI_SEQ_SET_TEMPO:
|
||
|
dwReturn = (DWORD)midiSeqMessage(pStream->hSeq, SEQ_SETTEMPO, lpSetParms->dwTempo, 0L);
|
||
|
break;
|
||
|
case MCI_SEQ_SET_PORT:
|
||
|
wPort = (UINT)lpSetParms->dwPort; // else use # passed in
|
||
|
if (wPort == MCI_SEQ_NONE) {
|
||
|
midiSeqMessage(pStream->hSeq, SEQ_SETPORTOFF, TRUE, 0L);
|
||
|
pStream->wPortNum = MCI_SEQ_NONE; //store port num (so can return "none")
|
||
|
} else if (!midiOutGetNumDevs())
|
||
|
dwReturn = MCIERR_SEQ_NOMIDIPRESENT;
|
||
|
else if ((wPort != MIDI_MAPPER) && (wPort >= midiOutGetNumDevs()))
|
||
|
dwReturn = MCIERR_SEQ_PORT_NONEXISTENT;
|
||
|
else if (wPort != pStream->wPortNum) { // ignore if using wPort already
|
||
|
MIDISEQINFO seqInfo;
|
||
|
|
||
|
// it's 0, 1...., or MAPPER
|
||
|
|
||
|
pStream->wPortNum = wPort; //store port number
|
||
|
midiSeqMessage(pStream->hSeq, SEQ_GETINFO, (DWORD_PTR)(LPMIDISEQINFO)&seqInfo, 0L);
|
||
|
if (seqInfo.bPlaying) {
|
||
|
midiSeqMessage(pStream->hSeq, SEQ_SETPORTOFF, TRUE, 0L);
|
||
|
dwReturn = OpenMidiPort(pStream);
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
case MCI_SEQ_SET_SLAVE:
|
||
|
switch (lpSetParms->dwSlave) {
|
||
|
case MCI_SEQ_SMPTE:
|
||
|
case MCI_SEQ_MIDI:
|
||
|
dwReturn = MCIERR_UNSUPPORTED_FUNCTION;
|
||
|
break;
|
||
|
case MCI_SEQ_NONE:
|
||
|
dwReturn = (DWORD)midiSeqMessage(pStream->hSeq, SEQ_SETSYNCSLAVE, SEQ_SYNC_NOTHING, 0L);
|
||
|
break;
|
||
|
case MCI_SEQ_FILE:
|
||
|
dwReturn = (DWORD)midiSeqMessage(pStream->hSeq, SEQ_SETSYNCSLAVE, SEQ_SYNC_FILE, 0L);
|
||
|
break;
|
||
|
}
|
||
|
break;
|
||
|
case MCI_SEQ_SET_MASTER:
|
||
|
switch (lpSetParms->dwMaster) {
|
||
|
case MCI_SEQ_SMPTE:
|
||
|
case MCI_SEQ_MIDI:
|
||
|
dwReturn = MCIERR_UNSUPPORTED_FUNCTION;
|
||
|
break;
|
||
|
case MCI_SEQ_NONE:
|
||
|
dwReturn = (DWORD)midiSeqMessage(pStream->hSeq, SEQ_SETSYNCMASTER, SEQ_SYNC_NOTHING, 0L);
|
||
|
break;
|
||
|
}
|
||
|
break;
|
||
|
case MCI_SEQ_SET_OFFSET:
|
||
|
mmOffset.u.smpte.hour = (BYTE)lpSetParms->dwOffset;
|
||
|
mmOffset.u.smpte.min = (BYTE)(lpSetParms->dwOffset >> 8);
|
||
|
mmOffset.u.smpte.sec = (BYTE)(lpSetParms->dwOffset >> 16);
|
||
|
mmOffset.u.smpte.frame = (BYTE)(lpSetParms->dwOffset >> 24);
|
||
|
if ((lpSetParms->dwOffset & 0x80808080) || // all positive
|
||
|
(mmOffset.u.smpte.hour > 23) ||
|
||
|
(mmOffset.u.smpte.min > 59) ||
|
||
|
(mmOffset.u.smpte.sec > 59) ||
|
||
|
(mmOffset.u.smpte.frame > 29)) // allow max for all fmts
|
||
|
dwReturn = MCIERR_OUTOFRANGE;
|
||
|
else
|
||
|
dwReturn = (DWORD)midiSeqMessage(pStream->hSeq, SEQ_SETSYNCSLAVE, SEQ_SYNC_OFFSET, (DWORD_PTR)(LPMMTIME)&mmOffset);
|
||
|
break;
|
||
|
case MCI_SET_AUDIO: //fall thru...
|
||
|
case MCI_SET_VIDEO:
|
||
|
case MCI_SET_DOOR_OPEN:
|
||
|
case MCI_SET_DOOR_CLOSED:
|
||
|
dwReturn = MCIERR_UNSUPPORTED_FUNCTION;
|
||
|
break;
|
||
|
default:
|
||
|
dwReturn = MCIERR_MISSING_PARAMETER;
|
||
|
}
|
||
|
return dwReturn;
|
||
|
}
|
||
|
|
||
|
/*---------------------------------------------------------------------------*/
|