?? midi.cpp
字號:
} else
lpciInfo->bTimesUp = TRUE;
DWORD tkNow = m_tkCurrentTime;
// Delta time is absolute event time minus absolute time
// already gone by on this track
DWORD tkDelta = pteTemp->tkEvent - m_tkCurrentTime;
// Event time is now current time on this track
m_tkCurrentTime = pteTemp->tkEvent;
if( m_bInsertTempo ) {
m_bInsertTempo = FALSE;
if( lpciInfo->dwMaxLength-lpciInfo->dwBytesRecorded < 3*sizeof(DWORD)) {
// Cleanup from our write operation
return CONVERTERR_BUFFERFULL;
}
if( m_dwCurrentTempo ) {
pmeEvent->dwDeltaTime = 0;
pmeEvent->dwStreamID = 0;
pmeEvent->dwEvent = ( m_dwCurrentTempo * 100 ) / m_dwTempoMultiplier;
pmeEvent->dwEvent |= (((DWORD)MEVT_TEMPO ) << 24 ) | MEVT_F_SHORT;
lpciInfo->dwBytesRecorded += 3 * sizeof(DWORD);
pmeEvent += 3 * sizeof(DWORD);
}
}
if( pteTemp->byShortData[0] < MIDI_SYSEX ) {
// Channel message. We know how long it is, just copy it.
// Need 3 DWORD's: delta-t, stream-ID, event
if( lpciInfo->dwMaxLength-lpciInfo->dwBytesRecorded < 3*sizeof(DWORD)) {
// Cleanup from our write operation
return CONVERTERR_BUFFERFULL;
}
pmeEvent->dwDeltaTime = tkDelta;
pmeEvent->dwStreamID = 0;
pmeEvent->dwEvent = ( pteTemp->byShortData[0] )
| (((DWORD)pteTemp->byShortData[1] ) << 8 )
| (((DWORD)pteTemp->byShortData[2] ) << 16 )
| MEVT_F_SHORT;
if((( pteTemp->byShortData[0] & 0xF0) == MIDI_CTRLCHANGE ) && ( pteTemp->byShortData[1] == MIDICTRL_VOLUME )) {
// If this is a volume change, generate a callback so we can grab
// the new volume for our cache
pmeEvent->dwEvent |= MEVT_F_CALLBACK;
}
lpciInfo->dwBytesRecorded += 3 *sizeof(DWORD);
} else if(( pteTemp->byShortData[0] == MIDI_SYSEX ) || ( pteTemp->byShortData[0] == MIDI_SYSEXEND )) {
TRACE0("AddEventToStreamBuffer: Ignoring SysEx event.\n");
if( m_dwMallocBlocks ) {
delete [] pteTemp->pLongData;
--m_dwMallocBlocks;
}
} else {
// Better be a meta event.
// BYTE byEvent
// BYTE byEventType
// VDWORD dwEventLength
// BYTE pLongEventData[dwEventLength]
ASSERT( pteTemp->byShortData[0] == MIDI_META );
// The only meta-event we care about is change tempo
if( pteTemp->byShortData[1] != MIDI_META_TEMPO ) {
if( m_dwMallocBlocks ) {
delete [] pteTemp->pLongData;
--m_dwMallocBlocks;
}
return CONVERTERR_METASKIP;
}
// We should have three bytes of parameter data...
ASSERT(pteTemp->dwEventLength == 3);
// Need 3 DWORD's: delta-t, stream-ID, event data
if( lpciInfo->dwMaxLength - lpciInfo->dwBytesRecorded < 3 *sizeof(DWORD)) {
// Cleanup the temporary event if necessary and return
if( m_dwMallocBlocks ) {
delete [] pteTemp->pLongData;
--m_dwMallocBlocks;
}
return CONVERTERR_BUFFERFULL;
}
pmeEvent->dwDeltaTime = tkDelta;
pmeEvent->dwStreamID = 0;
// Note: this is backwards from above because we're converting a single
// data value from hi-lo to lo-hi format...
pmeEvent->dwEvent = ( pteTemp->pLongData[2] )
| (((DWORD)pteTemp->pLongData[1] ) << 8 )
| (((DWORD)pteTemp->pLongData[0] ) << 16 );
// This next step has absolutely nothing to do with the conversion of a
// MIDI file to a stream, it's simply put here to add the functionality
// of the tempo slider. If you don't need this, be sure to remove the
// next two lines.
m_dwCurrentTempo = pmeEvent->dwEvent;
pmeEvent->dwEvent = (pmeEvent->dwEvent * 100 ) / m_dwTempoMultiplier;
pmeEvent->dwEvent |= (((DWORD)MEVT_TEMPO ) << 24 ) | MEVT_F_SHORT;
m_dwBufferTickLength = (m_dwTimeDivision * 1000 * BUFFER_TIME_LENGTH) / m_dwCurrentTempo;
TRACE1("m_dwBufferTickLength = %lu\n", m_dwBufferTickLength);
if( m_dwMallocBlocks ) {
delete [] pteTemp->pLongData;
--m_dwMallocBlocks;
}
lpciInfo->dwBytesRecorded += 3 *sizeof(DWORD);
}
return CONVERTERR_NOERROR;
}
// StreamBufferSetup()
//
// Opens a MIDI stream. Then it goes about converting the data into a midiStream buffer for playback.
BOOL CMIDI :: StreamBufferSetup() {
int nChkErr;
BOOL bFoundEnd = FALSE;
MMRESULT mmrRetVal;
if( !m_hStream )
if(( mmrRetVal = midiStreamOpen( &m_hStream,
&m_uMIDIDeviceID,
DWORD(1), DWORD(MidiProc),
DWORD(this),
CALLBACK_FUNCTION )) != MMSYSERR_NOERROR ) {
MidiError(mmrRetVal);
return FALSE;
}
// allocate stream buffers and initialise them
m_StreamBuffers.resize(NUM_STREAM_BUFFERS);
MIDIPROPTIMEDIV mptd;
mptd.cbStruct = sizeof(mptd);
mptd.dwTimeDiv = m_dwTimeDivision;
if(( mmrRetVal = midiStreamProperty( m_hStream, (LPBYTE)&mptd,
MIDIPROP_SET | MIDIPROP_TIMEDIV )) != MMSYSERR_NOERROR ) {
MidiError( mmrRetVal );
return FALSE;
}
m_nEmptyBuffers = 0;
DWORD dwConvertFlag = CONVERTF_RESET;
for( m_nCurrentBuffer = 0; m_nCurrentBuffer < NUM_STREAM_BUFFERS; m_nCurrentBuffer++ ) {
m_StreamBuffers[m_nCurrentBuffer].mhBuffer.dwBufferLength = OUT_BUFFER_SIZE;
m_StreamBuffers[m_nCurrentBuffer].mhBuffer.lpData = new char [OUT_BUFFER_SIZE];
if( m_StreamBuffers[m_nCurrentBuffer].mhBuffer.lpData == 0 )
return FALSE;
// 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.
m_StreamBuffers[m_nCurrentBuffer].dwStartOffset = 0;
m_StreamBuffers[m_nCurrentBuffer].dwMaxLength = OUT_BUFFER_SIZE;
m_StreamBuffers[m_nCurrentBuffer].tkStart = 0;
m_StreamBuffers[m_nCurrentBuffer].bTimesUp = FALSE;
if(( nChkErr = ConvertToBuffer( dwConvertFlag, &m_StreamBuffers[m_nCurrentBuffer] )) != CONVERTERR_NOERROR ) {
if( nChkErr == CONVERTERR_DONE ) {
bFoundEnd = TRUE;
} else {
TRACE0("Initial conversion pass failed\n");
return FALSE;
}
}
m_StreamBuffers[m_nCurrentBuffer].mhBuffer.dwBytesRecorded = m_StreamBuffers[m_nCurrentBuffer].dwBytesRecorded;
if( !m_bBuffersPrepared )
if(( mmrRetVal = midiOutPrepareHeader( (HMIDIOUT)m_hStream,
&m_StreamBuffers[m_nCurrentBuffer].mhBuffer,
sizeof(MIDIHDR))) != MMSYSERR_NOERROR ) {
MidiError( mmrRetVal );
return FALSE;
}
if(( mmrRetVal = midiStreamOut( m_hStream,
&m_StreamBuffers[m_nCurrentBuffer].mhBuffer,
sizeof(MIDIHDR))) != MMSYSERR_NOERROR ) {
MidiError(mmrRetVal);
break;
}
dwConvertFlag = 0;
if( bFoundEnd )
break;
}
m_bBuffersPrepared = TRUE;
m_nCurrentBuffer = 0;
return TRUE;
}
// 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.
void CMIDI :: FreeBuffers() {
DWORD idx;
MMRESULT mmrRetVal;
if( m_bBuffersPrepared ) {
for( idx = 0; idx < NUM_STREAM_BUFFERS; idx++ )
if(( mmrRetVal = midiOutUnprepareHeader( (HMIDIOUT)m_hStream,
&m_StreamBuffers[idx].mhBuffer,
sizeof(MIDIHDR))) != MMSYSERR_NOERROR ) {
MidiError(mmrRetVal);
}
m_bBuffersPrepared = FALSE;
}
// Free our stream buffers...
for( idx = 0; idx < NUM_STREAM_BUFFERS; idx++ )
if( m_StreamBuffers[idx].mhBuffer.lpData ) {
delete [] m_StreamBuffers[idx].mhBuffer.lpData;
m_StreamBuffers[idx].mhBuffer.lpData = 0;
}
}
//////////////////////////////////////////////////////////////////////
// CMIDI -- error handling
//////////////////////////////////////////////////////////////////////
void CMIDI :: MidiError(MMRESULT mmResult) {
#ifdef _DEBUG
char chText[512];
midiOutGetErrorText(mmResult, chText, sizeof(chText));
TRACE1("Midi error: %hs\n", chText);
#endif
}
void CMIDI :: TrackError(TRACK * ptsTrack, LPSTR lpszErr ) {
TRACE1("Track buffer offset %lu\n", (DWORD)(ptsTrack->pTrackCurrent - ptsTrack->pTrackStart));
TRACE1("Track total length %lu\n", ptsTrack->dwTrackLength);
TRACE1("%hs\n", lpszErr);
}
//////////////////////////////////////////////////////////////////////
// CMIDI -- overridables
//////////////////////////////////////////////////////////////////////
void CMIDI :: OnMidiOutOpen() {
}
void CMIDI :: OnMidiOutDone(MIDIHDR & rHdr) {
if( m_uCallbackStatus == STATUS_CALLBACKDEAD )
return;
++m_nEmptyBuffers;
if( m_uCallbackStatus == STATUS_WAITINGFOREND ) {
if( m_nEmptyBuffers < NUM_STREAM_BUFFERS )
return;
else {
m_uCallbackStatus = STATUS_CALLBACKDEAD;
Stop();
SetEvent(m_hBufferReturnEvent);
return;
}
}
// This flag is set whenever the callback is waiting for all buffers to
// come back.
if( m_uCallbackStatus == STATUS_KILLCALLBACK ) {
// Count NUM_STREAM_BUFFERS-1 being returned for the last time
if( m_nEmptyBuffers < NUM_STREAM_BUFFERS )
return;
else {
// Change the status to callback dead
m_uCallbackStatus = STATUS_CALLBACKDEAD;
SetEvent(m_hBufferReturnEvent);
return;
}
}
m_dwProgressBytes += m_StreamBuffers[m_nCurrentBuffer].mhBuffer.dwBytesRecorded;
///////////////////////////////////////////////////////////////////////////////
// Fill an available buffer with audio data again...
if( m_bPlaying && m_nEmptyBuffers ) {
m_StreamBuffers[m_nCurrentBuffer].dwStartOffset = 0;
m_StreamBuffers[m_nCurrentBuffer].dwMaxLength = OUT_BUFFER_SIZE;
m_StreamBuffers[m_nCurrentBuffer].tkStart = 0;
m_StreamBuffers[m_nCurrentBuffer].dwBytesRecorded = 0;
m_StreamBuffers[m_nCurrentBuffer].bTimesUp = FALSE;
int nChkErr;
if(( nChkErr = ConvertToBuffer( 0, &m_StreamBuffers[m_nCurrentBuffer] )) != CONVERTERR_NOERROR ) {
if( nChkErr == CONVERTERR_DONE ) {
m_uCallbackStatus = STATUS_WAITINGFOREND;
return;
} else {
TRACE0("MidiProc() conversion pass failed!\n");
return;
}
}
m_StreamBuffers[m_nCurrentBuffer].mhBuffer.dwBytesRecorded = m_StreamBuffers[m_nCurrentBuffer].dwBytesRecorded;
MMRESULT mmrRetVal;
if( (mmrRetVal = midiStreamOut(m_hStream, &m_StreamBuffers[m_nCurrentBuffer].mhBuffer, sizeof(MIDIHDR))) != MMSYSERR_NOERROR ) {
MidiError(mmrRetVal);
return;
}
m_nCurrentBuffer = ( m_nCurrentBuffer + 1 ) % NUM_STREAM_BUFFERS;
m_nEmptyBuffers--;
}
}
void CMIDI :: OnMidiOutPositionCB(MIDIHDR & rHdr, MIDIEVENT & rEvent) {
if( MIDIEVENT_TYPE(rEvent.dwEvent) == MIDI_CTRLCHANGE )
{
if( MIDIEVENT_DATA1(rEvent.dwEvent) == MIDICTRL_VOLUME ) {
// Mask off the channel number and cache the volume data byte
m_Volumes[MIDIEVENT_CHANNEL(rEvent.dwEvent)] = DWORD(MIDIEVENT_VOLUME(rEvent.dwEvent)*100/VOLUME_MAX);
if( m_pWndParent && ::IsWindow(m_pWndParent->GetSafeHwnd()) )
// Do not use SendMessage(), because a change of the midi stream has no effect
// during callback handling, so if the owner wants to adjust the volume, as a
// result of the windows message, (s)he will not hear that change.
m_pWndParent->PostMessage(
WM_MIDI_VOLUMECHANGED,
WPARAM(this),
LPARAM(
MAKELONG(
WORD(MIDIEVENT_CHANNEL(rEvent.dwEvent)),
WORD(MIDIEVENT_VOLUME(rEvent.dwEvent)*100/VOLUME_MAX)
)
)
);
}
}
}
void CMIDI :: OnMidiOutClose() {
}
//////////////////////////////////////////////////////////////////////
// CMIDI -- static members
//////////////////////////////////////////////////////////////////////
void CMIDI :: MidiProc(HMIDIOUT hMidi, UINT uMsg, DWORD dwInstanceData, DWORD dwParam1, DWORD dwParam2) {
CMIDI * pMidi = (CMIDI *) dwInstanceData;
ASSERT(pMidi != 0);
MIDIHDR * pHdr = (MIDIHDR*) dwParam1;
switch(uMsg) {
case MOM_OPEN:
pMidi->OnMidiOutOpen();
break;
case MOM_CLOSE:
pMidi->OnMidiOutClose();
break;
case MOM_DONE:
ASSERT(pHdr != 0);
pMidi->OnMidiOutDone(*pHdr);
break;
case MOM_POSITIONCB:
ASSERT(pHdr != 0);
pMidi->OnMidiOutPositionCB(*pHdr, *((MIDIEVENT*)(pHdr->lpData + pHdr->dwOffset)));
break;
default:
break;
}
}
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -