⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 mid2strm.c

📁 The source code of Doom legacy for windows
💻 C
📖 第 1 页 / 共 4 页
字号:
// 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 + -