📄 mid2strm.c
字号:
// Emacs style mode select -*- C++ -*-
//-----------------------------------------------------------------------------
//
// $Id: mid2strm.c,v 1.5 2000/09/28 20:57:22 bpereira Exp $
//
// Copyright (C) 1998-2000 by DooM Legacy Team.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//
// $Log: mid2strm.c,v $
// Revision 1.5 2000/09/28 20:57:22 bpereira
// no message
//
// Revision 1.4 2000/09/01 19:34:37 bpereira
// no message
//
// Revision 1.3 2000/08/10 19:58:05 bpereira
// no message
//
// Revision 1.2 2000/02/27 00:42:12 hurdler
// fix CR+LF problem
//
// Revision 1.1.1.1 2000/02/22 20:32:33 hurdler
// Initial import into CVS (v1.29 pr3)
//
//
// DESCRIPTION:
// midi-2-stream conversion
// adapted from DirectX6 sample code
// note: a lot of code is duplicate : the standalone version
// simply converts in one pass the whole file, while the
// 'mid2streamXXXX' routines are called by win_snd.c and
// convert the midi data into stream buffers on the fly
//
//-----------------------------------------------------------------------------
#include "../doomdef.h" // warnings
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <windowsx.h>
#include <mmsystem.h>
#include "mid2strm.h"
#define LEGACY // use Legacy's console output for error messages
// (does not compile stand-alone)
// seems like Allegro's midi player ran the qmus2mid file because
// it uses a running status of 'note off' as default on start of conversion
#define BAD_MIDI_FIX 0x80 //should be 0 (that is running status initially -none-)
//but won't play qmus2mid output, or .. fix qmus2mid?
// faBBe's prefs..
#ifndef ULONG
typedef unsigned long ULONG;
typedef long LONG;
#endif
#ifndef UBYTE
typedef unsigned char UBYTE;
#endif
#define CB_STREAMBUF (4096) // Size of each stream buffer
#define MIDS_SHORTMSG (0x00000000)
#define MIDS_TEMPO (0x01000000)
// Macros for swapping hi/lo-endian data
//
#define WORDSWAP(w) (((w) >> 8) | (((w) << 8) & 0xFF00))
#define LONGSWAP(dw) (((dw) >> 24) | \
(((dw) >> 8) & 0x0000FF00) | \
(((dw) << 8) & 0x00FF0000) | \
(((dw) << 24) & 0xFF000000))
// In debug builds, TRACKERR will show us where the parser died
//
#ifdef DEBUGMIDISTREAM
#define TRACKERR(p,sz) ShowTrackError(p,sz);
#else
#define TRACKERR(p,sz)
#endif
// These structures are stored in MIDI files; they need to be UBYTE
// aligned.
//
#pragma pack(1)
// Chunk header. dwTag is either MTrk or MThd.
//
typedef struct
{
LONG dwTag; // Type
LONG cbChunk; // Length (hi-lo)
} MIDICHUNK;
// Contents of MThd chunk.
typedef struct
{
WORD wFormat; // Format (hi-lo)
WORD nTracks; // # tracks (hi-lo)
WORD wTimeDivision; // Time division (hi-lo)
} MIDIFILEHDR;
#pragma pack()
// Temporary event structure which stores event data until we're ready to
// dump it into a stream buffer
//
typedef struct
{
LONG tkEvent; // Absolute time of event
BYTE abEvent[4]; // Event type and parameters if channel msg
LONG dwEventLength; // Of data which follows if meta or sysex
UBYTE* pEvent; // -> Event data if applicable
} TEMPEVENT, *PTEMPEVENT;
// Description of a track open for read
//
#define ITS_F_ENDOFTRK 0x00000001
// Description of a stream buffer on the output side
//
typedef struct STREAMBUF *PSTREAMBUF;
typedef struct STREAMBUF
{
UBYTE* pBuffer; // -> Start of actual buffer
LONG tkStart; // Tick time just before first event
UBYTE* pbNextEvent; // Where to write next event
LONG iBytesLeft; // UBYTEs left in buffer
LONG iBytesLeftUncompressed; // UBYTEs left when uncompressed
PSTREAMBUF pNext; // Next buffer
} STREAMBUF;
// Description of output stream open for write
//
typedef struct
{
LONG tkTrack; // Current tick position in track
PSTREAMBUF pFirst; // First stream buffer
PSTREAMBUF pLast; // Last (current) stream buffer
} OUTSTREAMSTATE;
// Format of structs within a MSD file
//
// 'fmt ' chunk
//
#define MDS_F_NOSTREAMID 0x00000001 // Stream ID's skipped; reader inserts
typedef struct
{
LONG dwTimeFormat; // Low word == time format in SMF format
LONG cbMaxBuffer; // Guaranteed max buffer size
LONG dwFlags; // Format flags
} MIDSFMT;
// 'data' chunk buffer header
//
typedef struct
{
LONG tkStart; // Absolute tick offset at start of buffer
LONG cbBuffer; // Bytes in this buffer
} MIDSBUFFER;
// A few globals
//
static HANDLE hInFile = INVALID_HANDLE_VALUE;
static HANDLE hOutFile= INVALID_HANDLE_VALUE;
static OUTSTREAMSTATE ots;
static BOOL fCompress = FALSE;
INFILESTATE ifs;
static DWORD tkCurrentTime;
#ifdef DEBUGMIDISTREAM
static char gteBadRunStat[] = "Reference to missing running status.";
static char gteRunStatMsgTrunc[] = "Running status message truncated";
static char gteChanMsgTrunc[] = "Channel message truncated";
static char gteSysExLenTrunc[] = "SysEx event truncated (length)";
static char gteSysExTrunc[] = "SysEx event truncated";
static char gteMetaNoClass[] = "Meta event truncated (no class UBYTE)";
static char gteMetaLenTrunc[] = "Meta event truncated (length)";
static char gteMetaTrunc[] = "Meta event truncated";
#endif
// Prototypes
//
static BOOL Init(LPSTR szInFile, LPSTR szOutFile);
static UBYTE* GetInFileData(LONG cbToGet);
static void Cleanup(void);
static BOOL WriteStreamBuffers(void);
static BOOL GetTrackVDWord(INTRACKSTATE* pInTrack, ULONG* lpdw);
static BOOL GetTrackEvent(INTRACKSTATE* pInTrack, TEMPEVENT *pMe);
static BOOL AddEventToStream(TEMPEVENT *pMe);
static UBYTE* GetOutStreamBytes(LONG tkNow, LONG cbNeeded, LONG cbUncompressed);
#ifdef DEBUGMIDISTREAM
static void ShowTrackError(INTRACKSTATE* pInTrack, char* szErr);
#endif
// either uses the local one (prints out on stderr) or Legacy's console output
void CONS_Printf (char *fmt, ...);
void I_Error (char *error, ...);
#ifndef LEGACY
static BOOL BuildNewTracks(void);
// only for stand-alone version
//
void _cdecl main(int argc, char* argv[])
{
UINT idxFnames;
if (argc < 3)
{
CONS_Printf ( "Format is mid2strm [-c] infile outfile\n");
CONS_Printf ( "-c\tNo-stream-id compression\n");
exit(1);
}
idxFnames = 1;
if (argv[1][0] == '-')
{
++idxFnames;
if (argv[1][1] == 'c')
fCompress = TRUE;
}
if (!Init(argv[idxFnames], argv[idxFnames+1]))
exit(1);
if (!BuildNewTracks())
exit(1);
if (!WriteStreamBuffers())
exit(1);
// Add cleanup code!!!
//
Cleanup();
exit(0);
}
#endif
// ----
// Init (stand-alone version)
//
// Open the input and output files
// Allocate and read the entire input file into memory
// Validate the input file structure
// Allocate the input track structures and initialize them
// Initialize the output track structures
//
// Return TRUE on success
// Prints its own error message if something goes wrong
//
// ----
static BOOL Init(char* szInFile, char* szOutFile)
{
BOOL fRet = FALSE;
LONG cbRead;
ULONG* pChunkID;
ULONG* pChunkSize;
LONG iChunkSize;
MIDIFILEHDR* pHeader;
INTRACKSTATE* pInTrack;
UINT iTrack;
// Initialize things we'll try to free later if we fail
//
ifs.FileSize = 0;
ifs.pFile = NULL;
//ifs.pTracks = NULL;
// Attempt to open the input and output files
//
hInFile = CreateFile(szInFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (INVALID_HANDLE_VALUE == hInFile)
{
CONS_Printf ( "Could not open \"%s\" for read.\n", szInFile);
goto Init_Cleanup;
}
hOutFile = CreateFile(szOutFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (INVALID_HANDLE_VALUE == hOutFile)
{
CONS_Printf ( "Could not open \"%s\" for write.\n", szOutFile);
goto Init_Cleanup;
}
// Figure out how big the input file is and allocate a chunk of memory big enough
// to hold the whole thing. Read the whole file in at once.
//
if (((UINT)-1) == (ifs.FileSize = GetFileSize(hInFile, NULL)))
{
CONS_Printf ( "File system error on input file.\n");
goto Init_Cleanup;
}
if (NULL == (ifs.pFile = GlobalAllocPtr(GPTR, ifs.FileSize)))
{
CONS_Printf ( "Out of memory.\n");
goto Init_Cleanup;
}
if ((!ReadFile(hInFile, ifs.pFile, ifs.FileSize, &cbRead, NULL)) ||
cbRead != ifs.FileSize)
{
CONS_Printf ( "Read error on input file.\n");
goto Init_Cleanup;
}
// Set up to read from the memory buffer. Read and validate
// - MThd header
// - size of file header chunk
// - file header itself
//
ifs.iBytesLeft = ifs.FileSize;
ifs.pFilePointer = ifs.pFile;
// note: midi header size should always be 6
if ((pChunkID = (ULONG*)GetInFileData(sizeof(*pChunkID))) == NULL ||
*pChunkID != MThd ||
(pChunkSize = (ULONG*)GetInFileData(sizeof(*pChunkSize))) == NULL ||
(iChunkSize = LONGSWAP(*pChunkSize)) < sizeof(MIDIFILEHDR) ||
(pHeader = (MIDIFILEHDR*)GetInFileData(iChunkSize)) == NULL )
{
CONS_Printf ( "Read error on MIDI header.\n");
goto Init_Cleanup;
}
// File header is stored in hi-lo order. Swap this into Intel order and save
// parameters in our native int size (32 bits)
//
ifs.dwFormat = (LONG)WORDSWAP(pHeader->wFormat);
ifs.nTracks = (LONG)WORDSWAP(pHeader->nTracks);
ifs.dwTimeDivision = (LONG)WORDSWAP(pHeader->wTimeDivision);
#ifdef DEBUGMIDISTREAM
CONS_Printf ("MIDI Header:\n"
"------------\n"
"format: %d\n"
"number of tracks: %d\n"
"time division: %d\n", ifs.dwFormat, ifs.nTracks, ifs.dwTimeDivision);
#endif
// We know how many tracks there are; allocate the structures for them and parse
// them. The parse merely looks at the MTrk signature and track chunk length
// in order to skip to the next track header.
/* faB: now static
ifs.pTracks = (INTRACKSTATE*)GlobalAllocPtr(GPTR, ifs.nTracks*sizeof(INTRACKSTATE));
if (ifs.pTracks==NULL)
{
CONS_Printf ( "Out of memory.\n");
goto Init_Cleanup;
}
*/
// faB: made it static, but don't quit if there are more tracks, just skip'em
// (this isn't really a limit, since 32 tracks are really the maximum for MIDI files)
if (ifs.nTracks > MAX_MIDI_IN_TRACKS)
ifs.nTracks = MAX_MIDI_IN_TRACKS;
for (iTrack = 0, pInTrack = ifs.pTracks; iTrack < ifs.nTracks; ++iTrack, ++pInTrack)
{
if ((pChunkID = (ULONG*)GetInFileData(sizeof(*pChunkID))) == NULL ||
*pChunkID!= MTrk ||
(pChunkSize = (ULONG*)GetInFileData(sizeof(*pChunkSize))) == NULL)
{
CONS_Printf ( "Read error on track header.\n");
goto Init_Cleanup;
}
iChunkSize = LONGSWAP(*pChunkSize);
pInTrack->iTrackLen = iChunkSize;
pInTrack->iBytesLeft = iChunkSize;
pInTrack->pTrackData = GetInFileData(iChunkSize);
if (pInTrack->pTrackData == NULL)
{
CONS_Printf ( "Read error while reading track data.\n");
goto Init_Cleanup;
}
#ifdef DEBUGMIDISTREAM
CONS_Printf ("Track %d : length %d bytes\n", iTrack, iChunkSize);
pInTrack->nTrack = iTrack;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -