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

📄 mid2strm.c

📁 The source code of Doom legacy for windows
💻 C
📖 第 1 页 / 共 4 页
字号:
        // Parse the general format of:
        //  BYTE        bEvent (MIDI_SYSEX or MIDI_SYSEXEND)
        //  VLONG      cbParms
        //  BYTE        abParms[cbParms]
        //
        pMe->abEvent[0] = b;
        if (!GetTrackVDWord(pInTrack, &pMe->dwEventLength))
        {
            TRACKERR(pInTrack, gteSysExLenTrunc);
            return FALSE;
        }
        
        if (pInTrack->iBytesLeft < pMe->dwEventLength)
        {
            TRACKERR(pInTrack, gteSysExTrunc);
            pInTrack->fdwTrack |= ITS_F_ENDOFTRK;
            return FALSE;
        }
        
        pMe->pEvent = pInTrack->pTrackPointer;
        pInTrack->pTrackPointer += pMe->dwEventLength;
        pInTrack->iBytesLeft -= pMe->dwEventLength;
    } 
    else if (b == MIDI_META)
    {
        // It's a meta event. Parse the general form:
        //  BYTE        bEvent  (MIDI_META)
        //      BYTE    bClass
        //  VLONG      cbParms
        //      BYTE    abParms[cbParms]
        //
        pMe->abEvent[0] = b;
        
        if (!pInTrack->iBytesLeft)
        {
            TRACKERR(pInTrack, gteMetaNoClass);
            pInTrack->fdwTrack |= ITS_F_ENDOFTRK;
            return FALSE;
        }
        
        pMe->abEvent[1] = *pInTrack->pTrackPointer++;
        --pInTrack->iBytesLeft;
        
        if (!GetTrackVDWord(pInTrack, &pMe->dwEventLength))
        {       
            TRACKERR(pInTrack, gteMetaLenTrunc);
            return FALSE;
        }
        
        // NOTE: Perfectly valid to have a meta with no data
        // In this case, dwEventLength == 0 and pEvent == NULL
        //
        if (pMe->dwEventLength)
        {               
            if (pInTrack->iBytesLeft < pMe->dwEventLength)
            {
                TRACKERR(pInTrack, gteMetaTrunc);
                pInTrack->fdwTrack |= ITS_F_ENDOFTRK;
                return FALSE;
            }
            
            pMe->pEvent = pInTrack->pTrackPointer;
            pInTrack->pTrackPointer += pMe->dwEventLength;
            pInTrack->iBytesLeft -= pMe->dwEventLength;
        }
        
        if (pMe->abEvent[1] == MIDI_META_EOT)
            pInTrack->fdwTrack |= ITS_F_ENDOFTRK;
    }
    else
    {
        // Messages in this range are system messages and aren't supposed to
        // be in a normal MIDI file. If they are, we've misparsed or the
        // authoring software is stpuid.
        //
#ifdef DEBUGMIDISTREAM
        CONS_Printf ("System message not supposed to be in MIDI file..\n");
#endif
        return FALSE;
    }
    
    // Event time was already stored as the current track time
    //
    pMe->tkEvent = pInTrack->tkNextEventDue;
    
    // Now update to the next event time. The code above MUST properly
    // maintain the end of track flag in case the end of track meta is
    // missing. 
    //
    if (!(pInTrack->fdwTrack & ITS_F_ENDOFTRK))
    {
        LONG                           tkDelta;
        
        if (!GetTrackVDWord(pInTrack, &tkDelta))
            return FALSE;
        
        pInTrack->tkNextEventDue += tkDelta;
    }
    
    return TRUE;
}


// ----------------
// AddEventToStream
//
// Put the given event onto the given output track.
// pMe must point to an event filled out in accordance with the
// description given in GetTrackEvent
//
// Returns TRUE on sucess or FALSE if we're out of memory
// ----------------
static BOOL AddEventToStream(TEMPEVENT *pMe)
{
    PLONG   pdw;
    LONG    tkNow, tkDelta;
    UINT    cdw;

    tkNow = ots.tkTrack;

    // Delta time is absolute event time minus absolute time
    // already gone by on this track
    //
    tkDelta = pMe->tkEvent - ots.tkTrack;
    
    // Event time is now current time on this track
    //
    ots.tkTrack = pMe->tkEvent;
    
    if (pMe->abEvent[0] < MIDI_SYSEX)
    {
        // Channel message. We know how long it is, just copy it. Need 3 LONG's: delta-t, 
        // stream-ID, event
        // 
        // TODO: Compress with running status
        //
        
        cdw = (fCompress ? 2 : 3);
        if (NULL == (pdw = (PLONG)GetOutStreamBytes(tkNow, cdw * sizeof(LONG), 3 * sizeof(LONG))))
            return FALSE;
        
        *pdw++ = tkDelta;
        if (!fCompress)
            *pdw++ = 0;
        *pdw =  (pMe->abEvent[0]) |
            (((LONG)pMe->abEvent[1]) << 8) |
            (((LONG)pMe->abEvent[2]) << 16) |
            MIDS_SHORTMSG;
    }
    else if (pMe->abEvent[0] == MIDI_SYSEX || pMe->abEvent[0] == MIDI_SYSEXEND)
    {
        CONS_Printf ( "NOTE: Ignoring SysEx for now.\n");
    }
    else
    {
        // Better be a meta event.
        //  BYTE                bEvent
        //  BYTE                bClass
        //      VLONG          cbParms
        //      BYTE            abParms[cbParms]
        //
        if (!(pMe->abEvent[0] == MIDI_META))
        {
            CONS_Printf ("Mid2Stream: error1\n");
            return FALSE;
        }
        
        // The only meta-event we care about is change tempo
        //
        if (pMe->abEvent[1] != MIDI_META_TEMPO)
            return TRUE;
        
        if (!(pMe->dwEventLength == 3))
        {
            CONS_Printf ("Mid2Stream: error2\n");
            return FALSE;
        }
        
        cdw = (fCompress ? 2 : 3);
        pdw = (PLONG)GetOutStreamBytes(tkNow, cdw * sizeof(LONG), 3 * sizeof(LONG));
        if (NULL == pdw)
            return FALSE;
        
        *pdw++ = tkDelta;
        if (!fCompress)
            *pdw++ = (LONG)-1;
        *pdw =  (pMe->pEvent[2]) |
            (((LONG)pMe->pEvent[1]) << 8) |
            (((LONG)pMe->pEvent[0]) << 16) |
            MIDS_TEMPO;
    }
    
    return TRUE;    
}


// -----------------
// GetOutStreamBytes
//
// This function performs the memory management and pseudo-file I/O for output
// tracks.
// 
// We build a linked list of stream buffers as they would exist if they were
// about to be played. Each buffer is CB_STREAMBUF UBYTEs long maximum. They are
// filled as full as possible; events are not allowed to cross buffers.
//
// Returns a pointer to the number of requested UBYTEs or NULL if we're out of memory
// -----------------
static UBYTE* GetOutStreamBytes(LONG tkNow, LONG cbNeeded, LONG cbUncompressed)
{
    UBYTE* pb;
    
    // Round request up to the next LONG boundry. This aligns the final output buffer correctly
    // and allows the above routines to deal with UBYTE-aligned data
    //
    cbNeeded = (cbNeeded + 3) & ~3;
    cbUncompressed = (cbUncompressed + 3) & ~3;
    
    if (!(cbUncompressed >= cbNeeded))
    {
        CONS_Printf ("GetOutStreamBytes: error\n");
        return NULL;
    }
    
    if (NULL == ots.pLast || cbUncompressed > ots.pLast->iBytesLeftUncompressed)
    {
        PSTREAMBUF pNew;
        
        pNew = GlobalAllocPtr(GHND, sizeof(*pNew) + CB_STREAMBUF);
        if (NULL == pNew)
            return NULL;
        
        pNew->pBuffer           = (UBYTE*)(pNew + 1);   //skip PSTRAMBUF struct header..
        pNew->tkStart           = tkNow;
        pNew->pbNextEvent       = pNew->pBuffer;
        pNew->iBytesLeft        = CB_STREAMBUF;
        pNew->iBytesLeftUncompressed = CB_STREAMBUF;
        pNew->pNext             = NULL;
        
        if (!ots.pLast)
        {
            ots.pFirst = pNew;
            ots.pLast  = pNew;
        }
        else
        {
            ots.pLast->pNext = pNew;
            ots.pLast        = pNew;
        }
    }
    
    // If there's STILL not enough room for the requested block, then an event is bigger than 
    // the buffer size -- this is unacceptable. 
    //
    if (cbNeeded > ots.pLast->iBytesLeft)
    {
        CONS_Printf ( "NOTE: An event requested %lu UBYTEs of memory; the\n", cbNeeded);
        CONS_Printf ( "      maximum configured buffer size is %lu.\n", (LONG)CB_STREAMBUF);
        
        return NULL;
    }
    
    pb = ots.pLast->pbNextEvent;
    
    ots.pLast->pbNextEvent += cbNeeded;
    ots.pLast->iBytesLeft -= cbNeeded;
    ots.pLast->iBytesLeftUncompressed -= cbUncompressed;
    
    return pb;
}

#ifdef DEBUGMIDISTREAM
static void ShowTrackError(INTRACKSTATE* pInTrack, char* szErr)
{
    unsigned char* data;
    int i;

    CONS_Printf ( "Track %u: %s\n", pInTrack->nTrack, szErr);
    CONS_Printf ( "Track offset %lu\n", (LONG)(pInTrack->pTrackPointer - pInTrack->pTrackData));
    CONS_Printf ( "Track total %lu  Track left %lu\n", pInTrack->iTrackLen, pInTrack->iBytesLeft);

    CONS_Printf (" Midi header: ");
    data = ifs.pFile;
    for (i = 0; i < 6; i++)
    {
        CONS_Printf ("%02x ", data[i]);
    }
    
    CONS_Printf ( "Track data: ");

    data = pInTrack->pTrackData;
    for (i = 0; i < 512; i++)
    {
        CONS_Printf ("%02x ", data[i]);
    }

}
#endif

#ifndef LEGACY
// if LEGACY is not defined, the stand-alone version will print out error messages to stderr
void CONS_Printf (char *fmt, ...)
{
    va_list     argptr;
    char        txt[512];

    va_start (argptr,fmt);
    vsprintf (txt,fmt,argptr);
    va_end   (argptr);

    fprintf  (stderr,"%s", txt);
}
#endif

// ==========================================================================
//                                 MIDI STREAM PLAYBACK (called by win_snd.c)
// ==========================================================================
// the following code is used with Legacy (not in stand-alone)
#ifdef LEGACY

static  DWORD   dwBufferTickLength;
        DWORD   dwProgressBytes;
// win_snd.c
extern  BOOL    bMidiLooped;


// --------------------------
// Mid2StreamConverterCleanup
// Free whatever was allocated before exiting program
// --------------------------
void Mid2StreamConverterCleanup (void)
{
    // hmm.. nothing to clean up.. since I made the INTRACKSTATE's static

    /* faB: made pTracks static
    if (ifs.pTracks)                                                  
        GlobalFreePtr(ifs.pTracks);
    */
}


// -----------------------
// Mid2StreamConverterInit
// 
// Validate the input file structure
// Allocate the input track structures and initialize them (faB: now STATIC)
// Initialize the output track structures
//
// Return TRUE on success
// -----------------------
BOOL Mid2StreamConverterInit( UBYTE* pMidiData, ULONG iMidiSize )
{
    BOOL            fRet = TRUE;
    ULONG*          pChunkID;
    ULONG*          pChunkSize;
    LONG            iChunkSize;
    MIDIFILEHDR*    pHeader;
    INTRACKSTATE*   pInTrack;
    UINT            iTrack;

    tkCurrentTime = 0;

    // Initialize things we'll try to free later if we fail
    ZeroMemory( &ifs, sizeof(INFILESTATE));
    //ifs.pTracks = NULL;   //faB: now static

    // Set up to read from the memory buffer. Read and validate
    // - MThd header
    // - size of file header chunk
    // - file header itself
    //      
    ifs.FileSize = iMidiSize;
    ifs.pFile = pMidiData;
    ifs.iBytesLeft = ifs.FileSize;
    ifs.pFilePointer = ifs.pFile;

#ifdef DEBUGMIDISTREAM
    CONS_Printf ("Midi file size: %d\n", iMidiSize);
#endif

    // 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;
    }

    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);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -