windows-nt/Source/XPSP1/NT/multimedia/media/mciseq/formats.c
2020-09-26 16:20:57 +08:00

333 lines
11 KiB
C

/******************************************************************************
Copyright (c) 1985-1998 Microsoft Corporation
Title: foramts.c - Multimedia Systems Media Control Interface
Contains specific mci time format conversion functions
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 "mmsys.h"
#include "list.h"
#include "mciseq.h"
/**************************** PRIVATE PROTOTYPES *************************/
PRIVATE DWORD NEAR PASCAL FPSDisplay(DWORD dwDisplayType);
PRIVATE DWORD NEAR PASCAL FPSFile(int wFileDiv);
PRIVATE DWORD NEAR PASCAL HMSFToFrames(DWORD dwCurrent, DWORD dwFormat);
PRIVATE DWORD NEAR PASCAL HMSFToMS(DWORD dwHmsf, DWORD dwFormat);
PRIVATE DWORD NEAR PASCAL FramesToHMSF(DWORD dwFrames, DWORD dwFormat);
/**************************** PRIVATE FUNCTIONS *************************/
PRIVATE DWORD NEAR PASCAL FPSDisplay(DWORD dwDisplayType)
// return the frames per second for smpte display types
// (utility function for format conversion routines)
{
switch (dwDisplayType)
{
case MCI_FORMAT_SMPTE_24:
return 24;
case MCI_FORMAT_SMPTE_25:
return 25;
case MCI_FORMAT_SMPTE_30DROP:
case MCI_FORMAT_SMPTE_30:
return 30;
#ifdef DEBUG
default:
return 0;
#endif
} //switch
return 0;
}
PRIVATE DWORD NEAR PASCAL FPSFile(int wFileDiv)
{
// returns frames per second based file division type
switch (wFileDiv)
{
case SEQ_DIV_SMPTE_24:
return 24;
case SEQ_DIV_SMPTE_25:
return 25;
case SEQ_DIV_SMPTE_30:
case SEQ_DIV_SMPTE_30DROP:
return 30;
#ifdef DEBUG
default:
return 0;
#endif
} //switch
return 0;
}
PRIVATE DWORD NEAR PASCAL HMSFToFrames(DWORD dwCurrent, DWORD dwFormat)
/* convert from hmsf (colonized) format to raw frames */
{
DWORD dwReturn;
HMSF hmsf = * ((HMSF FAR *) &dwCurrent); // cast dwCurrent to hmsf
int fps = (int)FPSDisplay(dwFormat);
dwReturn = ((DWORD)hmsf.hours * 60 * 60 * fps) +
((DWORD)hmsf.minutes * 60 * fps) +
((DWORD)hmsf.seconds * fps) +
hmsf.frames;
return dwReturn;
}
PRIVATE DWORD NEAR PASCAL HMSFToMS(DWORD dwHmsf, DWORD dwFormat)
// convert hmsf (colonized) format to milliseconds
{
DWORD dwReturn;
DWORD dwFrames = HMSFToFrames(dwHmsf, dwFormat);
int fps = (int)FPSDisplay(dwFormat);
dwReturn = ((dwFrames * 1000) + (fps/2)) / fps; // (fps/2) for rounding
return dwReturn;
}
PRIVATE DWORD NEAR PASCAL FramesToHMSF(DWORD dwFrames, DWORD dwFormat)
// convert from frames to hmsf (colonized) format
{
HMSF hmsf;
int fps = (int)FPSDisplay(dwFormat);
hmsf.hours = (BYTE)(dwFrames / ((DWORD) 60 * 60 * fps));
hmsf.minutes = (BYTE)((dwFrames % ((DWORD) 60 * 60 * fps)) / (60 * fps));
hmsf.seconds = (BYTE)((dwFrames % ((DWORD) 60 * fps)) / fps);
hmsf.frames = (BYTE)((dwFrames % fps));
return * ((DWORD FAR *) &hmsf);
}
/**************************** PUBLIC FUNCTIONS *************************/
PUBLIC BOOL NEAR PASCAL ColonizeOutput(pSeqStreamType pStream)
// tells whether the user display type is such that the output should
// displayed colonoized (i.e. "hh:mm:ss:ff")
{
if ((pStream->userDisplayType == MCI_FORMAT_SMPTE_24) ||
(pStream->userDisplayType == MCI_FORMAT_SMPTE_25) ||
(pStream->userDisplayType == MCI_FORMAT_SMPTE_30DROP) ||
(pStream->userDisplayType == MCI_FORMAT_SMPTE_30))
// smpte times are the only colonized formats
return TRUE;
else
return FALSE;
}
PUBLIC BOOL NEAR PASCAL FormatsEqual(pSeqStreamType pStream)
// tells whether the display format is compatible with the file format
// (i.e. will conversion have to be done in interaction with the user)
{
BOOL bReturn;
// Essentially, ppqn file type only compatible with song pointer display;
// SMPTE compatible with anything but ppqn
if (pStream->fileDivType == SEQ_DIV_PPQN)
{
if (pStream->userDisplayType == MCI_SEQ_FORMAT_SONGPTR)
bReturn = TRUE;
else
bReturn = FALSE;
}
else
{
if ((pStream->userDisplayType == MCI_FORMAT_SMPTE_24) ||
(pStream->userDisplayType == MCI_FORMAT_SMPTE_25) ||
(pStream->userDisplayType == MCI_FORMAT_SMPTE_30DROP) ||
(pStream->userDisplayType == MCI_FORMAT_SMPTE_30))
bReturn = TRUE;
else
bReturn = FALSE;
}
return bReturn;
}
PUBLIC DWORD NEAR PASCAL CnvtTimeToSeq(pSeqStreamType pStream, DWORD dwCurrent, MIDISEQINFO FAR * pSeqInfo)
/* The sequencer understands two time units: Ticks for ppqn files,
or frames for smpte files. The user data is presented either as
song pointer, milliseconds, or H(our)M(inute)S(econd)F(rame).
This routine converts FROM user time TO sequencer time.
*/
{
DWORD dwMs; // milliseconds
DWORD fps; // frames per second;
DWORD dwReturn;
DWORD dwTicks;
if (FormatsEqual(pStream))
{
if ((pStream->userDisplayType == MCI_FORMAT_SMPTE_24) ||
(pStream->userDisplayType == MCI_FORMAT_SMPTE_25) ||
(pStream->userDisplayType == MCI_FORMAT_SMPTE_30) ||
(pStream->userDisplayType == MCI_FORMAT_SMPTE_30DROP))
// both file and display are smpte
dwReturn = pSeqInfo->wResolution *
HMSFToFrames(dwCurrent, pStream->userDisplayType);
else
//both file and display are ppqn (convert song pointer to ppqn)
dwReturn = (pSeqInfo->wResolution * dwCurrent) / 4;
}
else if (pStream->fileDivType == SEQ_DIV_PPQN)
//ppqn file, display format !ppqn
{
if (pStream->userDisplayType != MCI_FORMAT_MILLISECONDS)
// must be smpte -- convert hmsf to milliseconds
dwMs = HMSFToMS(dwCurrent, pStream->userDisplayType);
else // must be milliseconds already
dwMs = dwCurrent;
// now that we have milliseconds, we must ask the sequencer to
// convert them to ppqn (using it's internal tempo map)
midiSeqMessage((HMIDISEQ) pStream->hSeq, SEQ_MSTOTICKS,
dwMs, (DWORD_PTR)(LPSTR) &dwTicks); // passed back in dwTicks
dwReturn = dwTicks;
}
else // smpte file, display format != smpte
{
// NB: don't worry about SONGPTR -- it's illegal here
// also, don't worry about HMSF -- it's equal
// The only possible case is ms -> ticks
fps = FPSFile(pStream->fileDivType);
dwReturn = ((dwCurrent * fps * pSeqInfo->wResolution) + 500) / 1000; // add 500 to round
}
return dwReturn;
}
PUBLIC DWORD NEAR PASCAL CnvtTimeFromSeq(pSeqStreamType pStream, DWORD dwTicks, MIDISEQINFO FAR * pSeqInfo)
// This routine converts FROM sequencer time TO user time.
{
DWORD dwMs; // milliseconds
DWORD fps; // frames per second;
DWORD dwReturn;
DWORD dwFrames;
DWORD dwNativeUnits;
if (pSeqInfo->wDivType == SEQ_DIV_PPQN)
dwNativeUnits = (dwTicks * 4) / pSeqInfo->wResolution;
else
dwNativeUnits = dwTicks / pSeqInfo->wResolution;
if (FormatsEqual(pStream))
{
if ((pStream->userDisplayType == MCI_FORMAT_SMPTE_24) ||
(pStream->userDisplayType == MCI_FORMAT_SMPTE_25) ||
(pStream->userDisplayType == MCI_FORMAT_SMPTE_30) ||
(pStream->userDisplayType == MCI_FORMAT_SMPTE_30DROP))
dwReturn = FramesToHMSF(dwNativeUnits, pStream->userDisplayType);
else
dwReturn = dwNativeUnits; // no conversion needed
}
else if (pStream->fileDivType == SEQ_DIV_PPQN)
{
// convert song ptr to ms
midiSeqMessage((HMIDISEQ) pStream->hSeq, SEQ_TICKSTOMS,
dwTicks, (DWORD_PTR)(LPSTR) &dwMs); // passed back in dwSongPtr
if (pStream->userDisplayType == MCI_FORMAT_MILLISECONDS)
dwReturn = dwMs;
else
// convert from ms to frames, and then frames to hmsf format
{
fps = FPSDisplay(pStream->userDisplayType);
dwFrames = (dwMs * fps) / 1000;
dwReturn = FramesToHMSF(dwFrames, pStream->userDisplayType);
}
}
else // smpte file
{
// NB: don't worry about SONGPTR -- it's illegal here
// also, don't worry about HMSF -- it's "equal"
// The only possible case is frames->ms
// set stream display type default based on file div type
fps = FPSFile(pStream->fileDivType);
dwReturn = ((dwTicks * 1000) + (fps/2)) /
(fps * pSeqInfo->wResolution);
// add (fps/2) to round
// BTW: it would take > 39 hours to overflow @ 30fps (23:59:59:29 is max)
}
return dwReturn;
}
PUBLIC BOOL NEAR PASCAL RangeCheck(pSeqStreamType pStream, DWORD dwValue)
/* range checks raw, unconverted data. checks if data is negative, or past
end of file length. also checks if smpte hours, minutes, seconds and frames
are all valid. Returns TRUE if legal, otherwise FALSE */
{
int fps;
HMSF hmsf;
DWORD dwLength;
MIDISEQINFO seqInfo;
// Get length, and convert it to display format
midiSeqMessage((HMIDISEQ) pStream->hSeq,
SEQ_GETINFO, (DWORD_PTR) (LPMIDISEQINFO) &seqInfo, 0L);
dwLength = CnvtTimeFromSeq(pStream, seqInfo.dwLength, &seqInfo);
switch (pStream->userDisplayType) // check length based on user format
{
case MCI_FORMAT_SMPTE_24:
case MCI_FORMAT_SMPTE_25:
case MCI_FORMAT_SMPTE_30:
case MCI_FORMAT_SMPTE_30DROP:
hmsf = * ((HMSF FAR *) &dwValue);
fps = (int)FPSDisplay(pStream->userDisplayType); // get frames per second
// check for format errors
if (((int)hmsf.frames >= fps) || (hmsf.seconds >= 60) ||
(hmsf.minutes >= 60) || (hmsf.hours > 24))
return FALSE;
// don't check for negative values, since using unsigned bytes
// (2's comp. negs would get caught above anyway)
// check for length error
if (HMSFToMS(* ((DWORD FAR *) &dwValue), pStream->userDisplayType) >
HMSFToMS(dwLength, pStream->userDisplayType))
return FALSE;
break;
case MCI_SEQ_FORMAT_SONGPTR:
case MCI_FORMAT_MILLISECONDS:
if (dwValue > dwLength)
return FALSE; // past end
}
return TRUE;
}