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

📄 mid2strm.c

📁 The source code of Doom legacy for windows
💻 C
📖 第 1 页 / 共 4 页
字号:
#endif
        pInTrack->pTrackPointer = pInTrack->pTrackData;
        pInTrack->fdwTrack = 0;
        pInTrack->bRunningStatus = 0;
        
        // Handle bozo MIDI files which contain empty track chunks
        //
        if (!pInTrack->iBytesLeft)
        {
            pInTrack->fdwTrack |= ITS_F_ENDOFTRK;
            continue;
        }
        
        
        // We always preread the time from each track so the mixer code can
        // determine which track has the next event with a minimum of work
        //
        if (!GetTrackVDWord(pInTrack, &pInTrack->tkNextEventDue))
        {
            CONS_Printf ( "Read error while reading first delta time of track.\n");
            goto Init_Cleanup;
        }
    }
    
    ots.tkTrack = 0;
    ots.pFirst = NULL;
    ots.pLast = NULL;
    
    fRet = TRUE;
    
Init_Cleanup:
    if (!fRet)
        Cleanup();
    
    return fRet;
}


// -------------
// GetInFileData
//
// Gets the requested number of UBYTEs of data from the input file and returns
// a pointer to them.
// 
// Returns a pointer to the data or NULL if we'd read more than is
// there.
// -------------
static UBYTE* GetInFileData(LONG iBytesToGet)
{
    UBYTE*  pRet;
    if (ifs.iBytesLeft < iBytesToGet)       // requested more UBYTEs than there are left
        return NULL;
    pRet = ifs.pFilePointer;                // pointer to requested data
    ifs.iBytesLeft -= iBytesToGet;
    ifs.pFilePointer += iBytesToGet;
    return pRet;
}


// -------
// Cleanup
//
// Free anything we ever allocated
// -------
static void Cleanup(void)
{
    PSTREAMBUF   pCurr;
    PSTREAMBUF   pNext;
    
    if (hInFile != INVALID_HANDLE_VALUE)    
        CloseHandle(hInFile);
    if (hOutFile != INVALID_HANDLE_VALUE)   
        CloseHandle(hOutFile);
    if (ifs.pFile)
        GlobalFreePtr(ifs.pFile);
    /* faB: made pTracks static
    if (ifs.pTracks)                                                  
        GlobalFreePtr(ifs.pTracks);
    */

    pCurr = ots.pFirst;
    while (pCurr)
    {
        pNext = pCurr->pNext;
        GlobalFreePtr(pCurr);
        pCurr = pNext;
    }
}


// --------------
// BuildNewTracks
//
// This is where the actual work gets done.
//
// Until all tracks are done,
//  Scan the tracks to find the next due event
//  Figure out where the event belongs in the new mapping
//  Put it there
// Add end of track metas to all new tracks that now have any data
//
// Return TRUE on success
// Prints its own error message if something goes wrong
// --------------
static BOOL BuildNewTracks(void)
{
    INTRACKSTATE*   pInTrack;
    INTRACKSTATE*   pInTrackFound;
    UINT            idx;
    DWORD           tkNext;
    TEMPEVENT       me;
    
    for(;;)
    {
        // Find nearest event due
        //
        pInTrackFound = NULL;
        tkNext = 0xFFFFFFFFL;
        
        for (idx = 0, pInTrack = ifs.pTracks; idx < ifs.nTracks; ++idx, ++pInTrack) {
            if ( (!(pInTrack->fdwTrack & ITS_F_ENDOFTRK)) && (pInTrack->tkNextEventDue < tkNext))
            {
                tkNext = pInTrack->tkNextEventDue;
                pInTrackFound = pInTrack;
            }
        }
        
        // None found? We must be done
        //
        if (!pInTrackFound)
            break;
        
        // Ok, get the event header from that track
        //
        if (!GetTrackEvent(pInTrackFound, &me))
        {
            CONS_Printf ( "MIDI file is corrupt!\n");
            return FALSE;
        }
        
        // Don't add end of track event 'til we're done
        //
        if (me.abEvent[0] == MIDI_META && me.abEvent[1] == MIDI_META_EOT)
            continue;
        
        if (!AddEventToStream(&me))
        {
            CONS_Printf ( "Out of memory building tracks.\n");
            return FALSE;
        }
    }       
    
    return TRUE;
}


//
// WriteStreamBuffers
//
// Write stream buffers into an MDS file (RIFF MIDS format)
//
// Return TRUE on success
// Prints its own error message if something goes wrong
//
#define FOURCC_MIDS mmioFOURCC('M','I','D','S')
#define FOURCC_fmt  mmioFOURCC('f','m','t',' ')
#define FOURCC_data mmioFOURCC('d','a','t','a')

static BOOL WriteStreamBuffers(void)
{
    LONG                   cbFmt;
    LONG                   cbData;
    LONG                   cbRiff;
    PSTREAMBUF              psb;
    FOURCC                  fcc;
    FOURCC                  fcc2;
    MIDSFMT                 fmt;
    MIDSBUFFER              data;
    LONG                   cb;
    LONG                   cBuffers;
    
    // Walk buffer list to find entire size of data chunk
    //
    cbData = sizeof(cBuffers);
    cBuffers = 0;
    for (psb = ots.pFirst; psb; psb = psb->pNext, ++cBuffers)
        cbData += sizeof(MIDSBUFFER) + (CB_STREAMBUF - psb->iBytesLeft);
    
    cbFmt = sizeof(fmt);
    
    // Figure size of entire RIFF chunk
    //
    cbRiff = 
        sizeof(FOURCC) +                        // RIFF form type ('MIDS')
        sizeof(FOURCC) +                        // Format chunk type ('fmt ')
        sizeof(LONG) +                         // Format chunk size
        sizeof(MIDSFMT) +                       // Format chunk contents
        sizeof(FOURCC) +                        // Data chunk type ('data')
        sizeof(LONG) +                         // Data chunk size
        cbData;                                         // Data chunk contents
    
    fcc = FOURCC_RIFF;
    fcc2 = FOURCC_MIDS;
    if ((!WriteFile(hOutFile, &fcc, sizeof(fcc), &cb, NULL)) ||
        (!WriteFile(hOutFile, &cbRiff, sizeof(cbRiff), &cb, NULL)) ||
        (!WriteFile(hOutFile, &fcc2, sizeof(fcc2), &cb, NULL)))
        return FALSE;
    
    
    fmt.dwTimeFormat        = ifs.dwTimeDivision;
    fmt.cbMaxBuffer         = CB_STREAMBUF;
    fmt.dwFlags             = 0;
    if (fCompress)
        fmt.dwFlags             |= MDS_F_NOSTREAMID;
    
    fcc = FOURCC_fmt;
    if ((!WriteFile(hOutFile, &fcc, sizeof(fcc), &cb, NULL)) ||
        (!WriteFile(hOutFile, &cbFmt, sizeof(cbFmt), &cb, NULL)) ||
        (!WriteFile(hOutFile, &fmt, sizeof(fmt), &cb, NULL)))
        return FALSE;
    
    fcc = FOURCC_data;
    if ((!WriteFile(hOutFile, &fcc, sizeof(fcc), &cb, NULL)) ||
        (!WriteFile(hOutFile, &cbData, sizeof(cbData), &cb, NULL)) ||
        (!WriteFile(hOutFile, &cBuffers, sizeof(cBuffers), &cb, NULL)))
        return FALSE;
    
    for (psb = ots.pFirst; psb; psb = psb->pNext)
    {
        data.tkStart = psb->tkStart;
        data.cbBuffer = CB_STREAMBUF - psb->iBytesLeft;
        
        if ((!WriteFile(hOutFile, &data, sizeof(data), &cb, NULL)) ||
            (!WriteFile(hOutFile, psb->pBuffer, data.cbBuffer, &cb, NULL)))
            return FALSE;
    }
    
    return TRUE;
}


// --------------
// GetTrackVDWord
// Attempts to parse a variable length LONG from the given track. A VDWord
// in a MIDI file
//  (a) is in lo-hi format 
//  (b) has the high bit set on every UBYTE except the last
// Returns the LONG in *lpdw and TRUE on success; else
// FALSE if we hit end of track first. Sets ITS_F_ENDOFTRK
// if we hit end of track.
// --------------
static BOOL GetTrackVDWord(INTRACKSTATE* pInTrack, ULONG* lpdw)
{
    BYTE    b;
    LONG    dw = 0;
    
    if (pInTrack->fdwTrack & ITS_F_ENDOFTRK)
        return FALSE;
    
    do
    {
        if (!pInTrack->iBytesLeft)
        {
            pInTrack->fdwTrack |= ITS_F_ENDOFTRK;
            return FALSE;
        }
        
        b = *pInTrack->pTrackPointer++;
        --pInTrack->iBytesLeft;
        
        dw = (dw << 7) | (b & 0x7F);
    } while (b & 0x80);
    
    *lpdw = dw;
    
    return TRUE;
}


// -------------
// GetTrackEvent
//
// Fills in the event struct with the next event from the track
//
// pMe->tkEvent will contain the absolute tick time of the event
// pMe->abEvent[0] will contain
//  MIDI_META if the event is a meta event;
//   in this case pMe->abEvent[1] will contain the meta class
//  MIDI_SYSEX or MIDI_SYSEXEND if the event is a SysEx event
//  Otherwise, the event is a channel message and pMe->abEvent[1]
//   and pMe->abEvent[2] will contain the rest of the event.
//
// pMe->dwEventLength will contain
//  The total length of the channel message in pMe->abEvent if
//   the event is a channel message
//  The total length of the paramter data pointed to by
//   pMe->pEvent otherwise
//
// pMe->pEvent will point at any additional paramters if the 
//  event is a SysEx or meta event with non-zero length; else
//  it will contain NULL
//
// Returns TRUE on success or FALSE on any kind of parse error
// Prints its own error message ONLY in the debug version
//
// Maintains the state of the input track (i.e. pInTrack->iBytesLeft,
// pInTrack->pTrackPointers, and pInTrack->bRunningStatus).
// -------------
static BOOL GetTrackEvent(INTRACKSTATE* pInTrack, TEMPEVENT *pMe)
{
    BYTE     b;
    LONG     dwEventLength;
    
    // Clear out the temporary event structure to get rid of old data...
    ZeroMemory( pMe, sizeof(TEMPEVENT));
    
    // Already at end of track? There's nothing to read.
    //
    if ((pInTrack->fdwTrack & ITS_F_ENDOFTRK) || !pInTrack->iBytesLeft)
        return FALSE;
    
    // Get the first UBYTE, which determines the type of event.
    //
    b = *pInTrack->pTrackPointer++;
    --pInTrack->iBytesLeft;
    
    // If the high bit is not set, then this is a channel message
    // which uses the status UBYTE from the last channel message
    // we saw. NOTE: We do not clear running status across SysEx or
    // meta events even though the spec says to because there are
    // actually files out there which contain that sequence of data.
    //
    if (!(b & 0x80))
    {
        // No previous status UBYTE? We're hosed.
        //
        if (!pInTrack->bRunningStatus)
        {
            TRACKERR(pInTrack, gteBadRunStat);
            return FALSE;
        }
        
        //faB: the last midi command issued on that track
        pMe->abEvent[0] = pInTrack->bRunningStatus;
        pMe->abEvent[1] = b;        // the data !
        
        b = pMe->abEvent[0] & 0xF0;
        pMe->dwEventLength = 2;       //2 data bytes
        
        // Only program change and channel pressure events are 2 UBYTEs long;
        // the rest are 3 and need another UBYTE
        //
        if ((b != MIDI_PRGMCHANGE) && (b != MIDI_CHANPRESS))
        {
            if (!pInTrack->iBytesLeft)
            {
                TRACKERR(pInTrack, gteRunStatMsgTrunc);
                pInTrack->fdwTrack |= ITS_F_ENDOFTRK;
                return FALSE;
            }
            
            pMe->abEvent[2] = *pInTrack->pTrackPointer++;
            --pInTrack->iBytesLeft;
            ++pMe->dwEventLength;
        }
    }
    else if ((b & 0xF0) != MIDI_SYSEX)
    {
        // Not running status, not in SysEx range - must be
        // normal channel message (0x80-0xEF)
        //
        pMe->abEvent[0] = b;
        pInTrack->bRunningStatus = b;
        
        // Strip off channel and just keep message type
        //
        b &= 0xF0;
        
        dwEventLength = (b == MIDI_PRGMCHANGE || b == MIDI_CHANPRESS) ? 1 : 2;
        pMe->dwEventLength = dwEventLength + 1;
        
        if (pInTrack->iBytesLeft < dwEventLength)
        {
            TRACKERR(pInTrack, gteChanMsgTrunc);
            pInTrack->fdwTrack |= ITS_F_ENDOFTRK;
            return FALSE;
        }
        
        pMe->abEvent[1] = *pInTrack->pTrackPointer++;
        if (dwEventLength == 2)
            pMe->abEvent[2] = *pInTrack->pTrackPointer++;
        
        pInTrack->iBytesLeft -= dwEventLength;
    } 
    else if (b == MIDI_SYSEX || b == MIDI_SYSEXEND)
    {
        // One of the SysEx types. (They are the same as far as we're concerned;
        // there is only a semantic difference in how the data would actually
        // get sent when the file is played. We must take care to put the correct
        // event type back on the output track, however.)
        //

⌨️ 快捷键说明

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