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

📄 win_snd.c

📁 The source code of Doom legacy for windows
💻 C
📖 第 1 页 / 共 5 页
字号:
#ifdef DEBUGMIDISTREAM
    CONS_Printf("I_RegisterSong: \n");
#endif
    if (!memcmp(data,"MUS",3))
    {
        // convert mus to mid with a wonderful function
        // thanks to S.Bacquet for the sources of qmus2mid
        // convert mus to mid and load it in memory
        if((iErrorCode = qmus2mid((char *)data,pMus2MidData,89,64,0,len,MIDBUFFERSIZE,
                                   &iMus2MidSize))!=0)
        {
            CONS_Printf("Cannot convert mus to mid, converterror :%d\n",iErrorCode);
            return 0;
        }
        pMidiFileData = pMus2MidData;
    }
    else
    // support mid file in WAD !!! (no conversion needed)
    if (!memcmp(data,"MThd",4))
    {
        pMidiFileData = data;
    }
    else
    {
        CONS_Printf ("Music lump is not MID or MUS music format\n");
        return 0;
    }

    if (pMidiFileData == NULL)
    {
        CONS_Printf ("Not a valid MIDI file : %d\n",iErrorCode);
        return 0;
    }
#ifdef DEBUGMIDISTREAM //EVENMORE
    else
    {
        I_SaveMemToFile (pMidiFileData, iMus2MidSize, "c:/temp/debug.mid");
    }
#endif
    
    // setup midi stream buffer
    if (StreamBufferSetup(pMidiFileData, iMus2MidSize)) {
        Mid2StreamConverterCleanup();
        I_Error ("I_RegisterSong: StreamBufferSetup FAILED");
    }

    return 1;
}


// -----------------
// StreamBufferSetup
// This function uses the filename stored in the global character array to
// open a MIDI file. Then it goes about converting at least the first part of
// that file into a midiStream buffer for playback.
// -----------------

//mid2strm.c - returns TRUE if an error occurs
BOOL Mid2StreamConverterInit( unsigned char* pMidiData, ULONG iMidiSize );
void Mid2StreamConverterCleanup( void );


// -----------------
// StreamBufferSetup
// - returns TRUE if a problem occurs
// -----------------
static BOOL StreamBufferSetup( unsigned char* pMidiData, int iMidiSize )
{
    MMRESULT            mmrRetVal;
    MIDIPROPTIMEDIV     mptd;
    BOOL    bFoundEnd = FALSE;
    int     dwConvertFlag;
    int     nChkErr;
    int     idx;

#ifdef DEBUGMIDISTREAM
    if (hStream == NULL)
        I_Error ("StreamBufferSetup: hStream is NULL!");
#endif
    
    // pause midi stream before manipulate there buffers
    midiStreamPause(hStream);

    // allocate the stream buffers (only once)
    for (idx = 0; idx < NUM_STREAM_BUFFERS; idx++ )
    {
        ciStreamBuffers[idx].mhBuffer.dwBufferLength = OUT_BUFFER_SIZE;
        if( ciStreamBuffers[idx].mhBuffer.lpData == NULL ) {
            if(( ciStreamBuffers[idx].mhBuffer.lpData = GlobalAllocPtr( GHND, OUT_BUFFER_SIZE )) == NULL )
            {
                return (FALSE);
            }
        }
    }

    // returns TRUE in case of conversion error
    if (Mid2StreamConverterInit( pMidiData, iMidiSize ))
        return( TRUE );

    // Initialize the volume cache array to some pre-defined value
    for( idx = 0; idx < MAX_MIDI_IN_TRACKS; idx++ )
        dwVolCache[idx] = VOL_CACHE_INIT;
   
    mptd.cbStruct = sizeof(mptd);
    mptd.dwTimeDiv = ifs.dwTimeDivision;
    if(( mmrRetVal = midiStreamProperty(hStream,(LPBYTE)&mptd,
                                        MIDIPROP_SET | MIDIPROP_TIMEDIV ))
                    != MMSYSERR_NOERROR )
    {
        MidiErrorMessageBox( mmrRetVal );
        return( TRUE );
    }

    nEmptyBuffers = 0;
    dwConvertFlag = CONVERTF_RESET;

    for( nCurrentBuffer = 0; nCurrentBuffer < NUM_STREAM_BUFFERS; nCurrentBuffer++ )
    {
        // Tell the converter to convert up to one entire buffer's length of output
        // data. Also, set a flag so it knows to reset any saved state variables it
        // may keep from call to call.
        ciStreamBuffers[nCurrentBuffer].dwStartOffset = 0;
        ciStreamBuffers[nCurrentBuffer].dwMaxLength = OUT_BUFFER_SIZE;
        ciStreamBuffers[nCurrentBuffer].tkStart = 0;
        ciStreamBuffers[nCurrentBuffer].bTimesUp = FALSE;
        
        if(( nChkErr = Mid2StreamConvertToBuffer( dwConvertFlag,
                                                  &ciStreamBuffers[nCurrentBuffer] ))
                    != CONVERTERR_NOERROR )
        {
            if( nChkErr == CONVERTERR_DONE )
            {
                bFoundEnd = TRUE;
            }
            else
            {
                CONS_Printf( "StreamBufferSetup: initial conversion pass failed" );
                return( TRUE );
            }
        }
        ciStreamBuffers[nCurrentBuffer].mhBuffer.dwBytesRecorded
            = ciStreamBuffers[nCurrentBuffer].dwBytesRecorded;
        
        if( !bBuffersPrepared ) {
            if(( mmrRetVal = midiOutPrepareHeader( (HMIDIOUT)hStream,
                &ciStreamBuffers[nCurrentBuffer].mhBuffer,
                sizeof(MIDIHDR))) != MMSYSERR_NOERROR )
            {
                MidiErrorMessageBox( mmrRetVal );
                return( TRUE );
            }
        }
        if(( mmrRetVal = midiStreamOut( hStream,
            &ciStreamBuffers[nCurrentBuffer].mhBuffer,
            sizeof(MIDIHDR))) != MMSYSERR_NOERROR )
        {
            MidiErrorMessageBox( mmrRetVal );
            break;
        }
        dwConvertFlag = 0;
        
        if( bFoundEnd )
            break;
    }

    bBuffersPrepared = TRUE;
    nCurrentBuffer = 0;

    // MIDI volume
    dwVolumePercent = (cv_musicvolume.value * 1000) / 32;
    if( hStream )
        SetAllChannelVolumes( dwVolumePercent );
    
    return( FALSE );
}


// ----------------
// SetChannelVolume
// Call here delayed by MIDI stream callback, to adapt the volume event of the
// midi stream to our own set volume percentage.
// ----------------
void I_SetMidiChannelVolume( DWORD dwChannel, DWORD dwVolumePercent )
{
    DWORD       dwEvent, dwVol;
    MMRESULT    mmrRetVal;

    if( !bMidiPlaying )
            return;

    dwVol = ( dwVolCache[dwChannel] * dwVolumePercent ) / 1000;
    //CONS_Printf ("setvolchannel %d vol %d\n", dwChannel, dwVol);
    dwEvent = MIDI_CTRLCHANGE | dwChannel | ((DWORD)MIDICTRL_VOLUME << 8)
                                          | ((DWORD)dwVol << 16);
    if(( mmrRetVal = midiOutShortMsg( (HMIDIOUT)hStream, dwEvent ))
                                                        != MMSYSERR_NOERROR )
    {
#ifdef DEBUGMIDISTREAM
        MidiErrorMessageBox( mmrRetVal );
#endif
        return;
    }
}



// ------------------
// MidiStreamCallback
// This is the callback handler which continually refills MIDI data buffers
// as they're returned to us from the audio subsystem.
// ------------------
static void CALLBACK MidiStreamCallback (HMIDIIN hMidi, UINT uMsg, DWORD dwInstance,
                                                 DWORD dwParam1, DWORD dwParam2 )
{
    //static int  nWaitingBuffers = 0;
    MMRESULT    mmrRetVal;
    int         nChkErr;
    MIDIEVENT   *pme;
    MIDIHDR     *pmh;


    switch( uMsg )
        {
        case MOM_DONE:
            //faB:  dwParam1 is LPMIDIHDR

            if( uCallbackStatus == STATUS_CALLBACKDEAD )
                return;

            nEmptyBuffers++;

            //faB: we reached end of song, but we wait until all the buffers are returned
            if( uCallbackStatus == STATUS_WAITINGFOREND )
            {
                if( nEmptyBuffers < NUM_STREAM_BUFFERS )
                {
                    return;
                }
                else
                {
                    // stop the song when end reached (was not looping)
                    uCallbackStatus = STATUS_CALLBACKDEAD;
                    SetEvent( hBufferReturnEvent );
                    I_StopSong(0);
                    return;
                }
            }

            // This flag is set whenever the callback is waiting for all buffers to
            // come back.
            if( uCallbackStatus == STATUS_KILLCALLBACK )
            {
                // Count NUM_STREAM_BUFFERS-1 being returned for the last time
                if( nEmptyBuffers < NUM_STREAM_BUFFERS )
                {
                    return;
                }
                // Then send a stop message when we get the last buffer back...
                else
                {
                    // Change the status to callback dead
                    uCallbackStatus = STATUS_CALLBACKDEAD;
                    SetEvent( hBufferReturnEvent );
                    return;
                }
            }

            dwProgressBytes += ciStreamBuffers[nCurrentBuffer].mhBuffer.dwBytesRecorded;

        // -------------------------------------------------
        // Fill an available buffer with audio data again...
        // -------------------------------------------------

            if( bMidiPlaying && nEmptyBuffers )
            {
                ciStreamBuffers[nCurrentBuffer].dwStartOffset = 0;
                ciStreamBuffers[nCurrentBuffer].dwMaxLength = OUT_BUFFER_SIZE;
                ciStreamBuffers[nCurrentBuffer].tkStart = 0;
                ciStreamBuffers[nCurrentBuffer].dwBytesRecorded = 0;
                ciStreamBuffers[nCurrentBuffer].bTimesUp = FALSE;
                
                if(( nChkErr = Mid2StreamConvertToBuffer( 0, &ciStreamBuffers[nCurrentBuffer] ))
                    != CONVERTERR_NOERROR )
                {
                    if( nChkErr == CONVERTERR_DONE )
                    {
                        // Don't include this one in the count
                        //nWaitingBuffers = NUM_STREAM_BUFFERS - 1;
                        uCallbackStatus = STATUS_WAITINGFOREND;
                        return;
                    }
                    else
                    {
                        //faB: we're not in the main thread so we can't call I_Error() now
                        //     log the error message out, and post exit message.
                        CONS_Printf( "MidiStreamCallback(): conversion pass failed!" );
                        PostMessage(hWndMain, WM_CLOSE, 0, 0);
                        return;
                    }
                }
                
                ciStreamBuffers[nCurrentBuffer].mhBuffer.dwBytesRecorded
                    = ciStreamBuffers[nCurrentBuffer].dwBytesRecorded;
                
                if(( mmrRetVal = midiStreamOut( hStream,
                    &ciStreamBuffers[nCurrentBuffer].mhBuffer,
                    sizeof(MIDIHDR))) != MMSYSERR_NOERROR )
                {
                    MidiErrorMessageBox( mmrRetVal );
                    Mid2StreamConverterCleanup();
                    return;
                }
                nCurrentBuffer = ( nCurrentBuffer + 1 ) % NUM_STREAM_BUFFERS;
                nEmptyBuffers--;
            }

            break;

        case MOM_POSITIONCB:
            pmh = (MIDIHDR *)dwParam1;
            pme = (MIDIEVENT *)(pmh->lpData + pmh->dwOffset);
            if( MIDIEVENT_TYPE( pme->dwEvent ) == MIDI_CTRLCHANGE )
            {
#ifdef DEBUGMIDISTREAM
                if( MIDIEVENT_DATA1( pme->dwEvent ) == MIDICTRL_VOLUME_LSB )
                {
                    CONS_Printf ( "Got an LSB volume event" );
                    PostMessage (hWndMain, WM_CLOSE, 0, 0); //faB: can't I_Error() here
                    break;
                }
#endif
                // this is meant to respond to our own intention, from mid2strm.c
                if( MIDIEVENT_DATA1( pme->dwEvent ) != MIDICTRL_VOLUME )
                    break;
                
                // Mask off the channel number and cache the volume data byte
                //CONS_Printf ( "dwvolcache %d = %d\n", MIDIEVENT_CHANNEL( pme->dwEvent ),  MIDIEVENT_VOLUME( pme->dwEvent ));
                dwVolCache[ MIDIEVENT_CHANNEL( pme->dwEvent )] = MIDIEVENT_VOLUME( pme->dwEvent );
                // call SetChannelVolume() later to adjust MIDI volume control message to our
                // own current volume level.
                PostMessage( hWndMain, WM_MSTREAM_UPDATEVOLUME,
                             MIDIEVENT_CHANNEL( pme->dwEvent ), 0L );
            }
            break;

        default:
            break;
        }

    return;
}


// ---------------------
// Mid2StreamFreeBuffers
// This function unprepares and frees all our buffers -- something we must
// do to work around a bug in MMYSYSTEM that prevents a device from playing
// back properly unless it is closed and reopened after each stop.
// ---------------------
static void Mid2StreamFreeBuffers( void )
{
    DWORD       idx;
    MMRESULT    mmrRetVal;

    if( bBuffersPrepared )
    {
        for( idx = 0; idx < NUM_STREAM_BUFFERS; idx++ ) {
                if(( mmrRetVal = midiOutUnprepareHeader( (HMIDIOUT)hStream,
                                        &ciStreamBuffers[idx].mhBuffer,
                                        sizeof(MIDIHDR)))
                                                != MMSYSERR_NOERROR )
                {
                    MidiErrorMessageBox( mmrRetVal );
                }
        }
        bBuffersPrepared = FALSE;
        }

    //faB: I don't free the stream buffers here, but rather allocate them
    //      once at startup, and free'em at shutdown
}

⌨️ 快捷键说明

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