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

📄 midi.cpp

📁 软件类别:多 媒 体 软件大小:12KB 运行环境:windows/ 软件 类别:多 媒 体 软件大小:12KB 运行环境:windows/
💻 CPP
📖 第 1 页 / 共 3 页
字号:
}


void CMIDI :: SetChannelVolume(DWORD dwChannel, DWORD dwPercent) {
	ASSERT(dwChannel < m_Volumes.size());

	if( !m_bPlaying )
		return;

	m_Volumes[dwChannel] = (dwPercent > 100) ? 100 : dwPercent;
	DWORD dwEvent = MIDI_CTRLCHANGE | dwChannel | ((DWORD)MIDICTRL_VOLUME << 8) | ((DWORD)(m_Volumes[dwChannel]*VOLUME_MAX/100) << 16);
	MMRESULT mmrRetVal;
	if(( mmrRetVal = midiOutShortMsg((HMIDIOUT)m_hStream, dwEvent)) != MMSYSERR_NOERROR ) {
		MidiError(mmrRetVal);
		return;
	}
}


DWORD CMIDI :: GetChannelVolume(DWORD dwChannel) const {
	ASSERT(dwChannel < GetChannelCount());
	return m_Volumes[dwChannel];
}


void CMIDI :: SetTempo(DWORD dwPercent) {
	m_dwTempoMultiplier = dwPercent ? dwPercent : 1;
	m_bInsertTempo = TRUE;
}


DWORD CMIDI :: GetTempo() const {
	return m_dwTempoMultiplier;
}


void CMIDI :: SetInfinitePlay(BOOL bSet) {
	m_bLooped = bSet;
}

//////////////////////////////////////////////////////////////////////
// CMIDI -- implementation
//////////////////////////////////////////////////////////////////////

// This function converts MIDI data from the track buffers setup by a
// previous call to ConverterInit().  It will convert data until an error is
// encountered or the output buffer has been filled with as much event data
// as possible, not to exceed dwMaxLength. This function can take a couple
// bit flags, passed through dwFlags. Information about the success/failure
// of this operation and the number of output bytes actually converted will
// be returned in the CONVERTINFO structure pointed at by lpciInfo.
int CMIDI :: ConvertToBuffer(DWORD dwFlags, CONVERTINFO * lpciInfo) {
	int	    nChkErr;

	lpciInfo->dwBytesRecorded = 0;

	if( dwFlags & CONVERTF_RESET ) {
		m_dwProgressBytes = 0;
		m_dwStatus = 0;
		memset( &m_teTemp, 0, sizeof(TEMPEVENT));
		m_ptsTrack = m_ptsFound = 0;
	}

	// If we were already done, then return with a warning...
	if( m_dwStatus & CONVERTF_STATUS_DONE ) {
		if( m_bLooped ) {
			Rewind();
			m_dwProgressBytes = 0;
			m_dwStatus = 0;
		} else
			return CONVERTERR_DONE;
	} else if( m_dwStatus & CONVERTF_STATUS_STUCK ) {
		// The caller is asking us to continue, but we're already hosed because we
		// previously identified something as corrupt, so complain louder this time.
		return( CONVERTERR_STUCK );
	} else if( m_dwStatus & CONVERTF_STATUS_GOTEVENT ) {
		// Turn off this bit flag
		m_dwStatus ^= CONVERTF_STATUS_GOTEVENT;

		// The following code for this case is duplicated from below, and is
		// designed to handle a "straggler" event, should we have one left over
		// from previous processing the last time this function was called.

		// Don't add end of track event 'til we're done
		if( m_teTemp.byShortData[0] == MIDI_META && m_teTemp.byShortData[1] == MIDI_META_EOT ) {
			if( m_dwMallocBlocks ) {
				delete [] m_teTemp.pLongData;
				--m_dwMallocBlocks;
			}
		} else if(( nChkErr = AddEventToStreamBuffer( &m_teTemp, lpciInfo )) != CONVERTERR_NOERROR ) {
			if( nChkErr == CONVERTERR_BUFFERFULL ) {
				// Do some processing and tell caller that this buffer's full
				m_dwStatus |= CONVERTF_STATUS_GOTEVENT;
				return CONVERTERR_NOERROR;
			} else if( nChkErr == CONVERTERR_METASKIP ) {
				// We skip by all meta events that aren't tempo changes...
			} else {
				TRACE0("Unable to add event to stream buffer.\n");
				if( m_dwMallocBlocks ) {
					delete [] m_teTemp.pLongData;
					m_dwMallocBlocks--;
				}
				return( TRUE );
			}
		}
	}

	for(;;) {
		m_ptsFound = 0;
		m_tkNext = 0xFFFFFFFFL;
		// Find nearest event due
		for( register DWORD idx = 0; idx < m_Tracks.size(); ++idx ) {
			m_ptsTrack = &m_Tracks[idx];
			if( !(m_ptsTrack->fdwTrack & ITS_F_ENDOFTRK) && (m_ptsTrack->tkNextEventDue < m_tkNext) ) {
				m_tkNext = m_ptsTrack->tkNextEventDue;
				m_ptsFound = m_ptsTrack;
			}
		}

		// None found?  We must be done, so return to the caller with a smile.
		if( !m_ptsFound ) {
			m_dwStatus |= CONVERTF_STATUS_DONE;
			// Need to set return buffer members properly
			return CONVERTERR_NOERROR;
		}

		// Ok, get the event header from that track
		if( !GetTrackEvent( m_ptsFound, &m_teTemp )) {
			// Warn future calls that this converter is stuck at a corrupt spot
			// and can't continue
			m_dwStatus |= CONVERTF_STATUS_STUCK;
			return CONVERTERR_CORRUPT;
		}

		// Don't add end of track event 'til we're done
		if( m_teTemp.byShortData[0] == MIDI_META && m_teTemp.byShortData[1] == MIDI_META_EOT ) {
			if( m_dwMallocBlocks ) {
				delete [] m_teTemp.pLongData;
				--m_dwMallocBlocks;
			}
			continue;
		}

		if(( nChkErr = AddEventToStreamBuffer( &m_teTemp, lpciInfo )) != CONVERTERR_NOERROR ) {
			if( nChkErr == CONVERTERR_BUFFERFULL ) {
				// Do some processing and tell somebody this buffer is full...
				m_dwStatus |= CONVERTF_STATUS_GOTEVENT;
				return CONVERTERR_NOERROR;
			} else if( nChkErr == CONVERTERR_METASKIP ) {
				// We skip by all meta events that aren't tempo changes...
			} else {
				TRACE0("Unable to add event to stream buffer.\n");
				if( m_dwMallocBlocks ) {
					delete [] m_teTemp.pLongData;
					m_dwMallocBlocks--;
				}
				return TRUE;
			}
		}
	}	

	return CONVERTERR_NOERROR;
}

// GetTrackEvent
//
// Fills in the event struct with the next event from the track
//
// pteTemp->tkEvent will contain the absolute tick time of the event
// pteTemp->byShortData[0] will contain
//  MIDI_META if the event is a meta event;
//   in this case pteTemp->byShortData[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 pteTemp->byShortData[1]
//   and pteTemp->byShortData[2] will contain the rest of the event.
//
// pteTemp->dwEventLength will contain
//  The total length of the channel message in pteTemp->byShortData if
//   the event is a channel message
//  The total length of the paramter data pointed to by
//   pteTemp->pLongData otherwise
//
// pteTemp->pLongData 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. 
// ptsTrack->pTrackPointers, and ptsTrack->byRunningStatus).
//
BOOL CMIDI :: GetTrackEvent(TRACK * ptsTrack, TEMPEVENT * pteTemp) {
	DWORD   idx;
	UINT    dwEventLength;

	// Clear out the temporary event structure to get rid of old data...
	memset( pteTemp, 0, sizeof(TEMPEVENT));

	// Already at end of track? There's nothing to read.
    if( ptsTrack->fdwTrack & ITS_F_ENDOFTRK )
		return FALSE;

	// Get the first byte, which determines the type of event.
	BYTE byByte;
	if( !GetTrackByte(ptsTrack, &byByte) )
		return FALSE;

	// If the high bit is not set, then this is a channel message
	// which uses the status byte 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( !(byByte & 0x80) ) {
		// No previous status byte? We're hosed.
		if( !ptsTrack->byRunningStatus ) {
			TrackError(ptsTrack, gteBadRunStat);
			return FALSE;
		}

		pteTemp->byShortData[0] = ptsTrack->byRunningStatus;
		pteTemp->byShortData[1] = byByte;

		byByte = pteTemp->byShortData[0] & 0xF0;
		pteTemp->dwEventLength = 2;

		// Only program change and channel pressure events are 2 bytes long;
		// the rest are 3 and need another byte
		if(( byByte != MIDI_PRGMCHANGE ) && ( byByte != MIDI_CHANPRESS )) {
			if( !GetTrackByte( ptsTrack, &pteTemp->byShortData[2] ))
				return FALSE;
			++pteTemp->dwEventLength;
		}
	} else if(( byByte & 0xF0 ) != MIDI_SYSEX ) {
		// Not running status, not in SysEx range - must be
		// normal channel message (0x80-0xEF)
		pteTemp->byShortData[0] = byByte;
		ptsTrack->byRunningStatus = byByte;

		// Strip off channel and just keep message type
		byByte &= 0xF0;

		dwEventLength = ( byByte == MIDI_PRGMCHANGE || byByte == MIDI_CHANPRESS ) ? 1 : 2;
		pteTemp->dwEventLength = dwEventLength + 1;

		if( !GetTrackByte( ptsTrack, &pteTemp->byShortData[1] ))
			return FALSE;
		if( dwEventLength == 2 )
			if( !GetTrackByte( ptsTrack, &pteTemp->byShortData[2] ))
				return FALSE;
	} else if(( byByte == MIDI_SYSEX ) || ( byByte == 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 proper
		// event type back on the output track, however.)
		//
		// Parse the general format of:
		//  BYTE 	bEvent (MIDI_SYSEX or MIDI_SYSEXEND)
		//  VDWORD 	cbParms
		//  BYTE   	abParms[cbParms]
		pteTemp->byShortData[0] = byByte;
		if( !GetTrackVDWord( ptsTrack, &pteTemp->dwEventLength )) {
			TrackError( ptsTrack, gteSysExLenTrunc );
			return FALSE;
		}

		// Malloc a temporary memory block to hold the parameter data
		pteTemp->pLongData = new BYTE [pteTemp->dwEventLength];
		if( pteTemp->pLongData == 0 ) {
			TrackError( ptsTrack, gteNoMem );
			return FALSE;
		}
		// Increment our counter, which tells the program to look around for
		// a malloc block to free, should it need to exit or reset before the
		// block would normally be freed
		++m_dwMallocBlocks;

		// Copy from the input buffer to the parameter data buffer
		for( idx = 0; idx < pteTemp->dwEventLength; idx++ )
			if( !GetTrackByte( ptsTrack, pteTemp->pLongData + idx )) {
				TrackError( ptsTrack, gteSysExTrunc );
				return FALSE;
			}
	} else if( byByte == MIDI_META ) {
		// It's a meta event. Parse the general form:
		//  BYTE	bEvent	(MIDI_META)
		//  BYTE	bClass
		//  VDWORD	cbParms
		//  BYTE	abParms[cbParms]
		pteTemp->byShortData[0] = byByte;

		if( !GetTrackByte( ptsTrack, &pteTemp->byShortData[1] ))
			return FALSE;

		if( !GetTrackVDWord( ptsTrack, &pteTemp->dwEventLength )) {	
			TrackError( ptsTrack, gteMetaLenTrunc );
			return FALSE;
		}

		// NOTE: It's perfectly valid to have a meta with no data
		// In this case, dwEventLength == 0 and pLongData == NULL
		if( pteTemp->dwEventLength ) {		
			// Malloc a temporary memory block to hold the parameter data
			pteTemp->pLongData = new BYTE [pteTemp->dwEventLength];
			if( pteTemp->pLongData == 0 ) {
				TrackError( ptsTrack, gteNoMem );
				return FALSE;
			}
			// Increment our counter, which tells the program to look around for
			// a malloc block to free, should it need to exit or reset before the
			// block would normally be freed
			++m_dwMallocBlocks;

			// Copy from the input buffer to the parameter data buffer
			for( idx = 0; idx < pteTemp->dwEventLength; idx++ )
				if( !GetTrackByte( ptsTrack, pteTemp->pLongData + idx )) {
					TrackError( ptsTrack, gteMetaTrunc );
					return FALSE;
				}
		}

		if( pteTemp->byShortData[1] == MIDI_META_EOT )
			ptsTrack->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 either misparsed or the
		// authoring software is stupid.
		return FALSE;
	}

	// Event time was already stored as the current track time
	pteTemp->tkEvent = ptsTrack->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.  NOTE: This code is a continuation of the track event
	// time pre-read which is done at the end of track initialization.
	if( !( ptsTrack->fdwTrack & ITS_F_ENDOFTRK )) {
		DWORD	tkDelta;

		if( !GetTrackVDWord( ptsTrack, &tkDelta ))
			return FALSE;

		ptsTrack->tkNextEventDue += tkDelta;
	}

	return TRUE;
}


// GetTrackVDWord
//
// Attempts to parse a variable length DWORD from the given track. A VDWord
// in a MIDI file
//  (a) is in lo-hi format 
//  (b) has the high bit set on every byte except the last
//
// Returns the DWORD in *lpdw and TRUE on success; else
// FALSE if we hit end of track first.
BOOL CMIDI :: GetTrackVDWord(TRACK * ptsTrack, LPDWORD lpdw) {
	ASSERT(ptsTrack != 0);
	ASSERT(lpdw != 0);

	if( ptsTrack->fdwTrack & ITS_F_ENDOFTRK )
		return FALSE;

	BYTE	byByte;
	DWORD	dw = 0;

	do {
		if( !GetTrackByte( ptsTrack, &byByte ))
			return FALSE;

		dw = ( dw << 7 ) | ( byByte & 0x7F );
	} while( byByte & 0x80 );

	*lpdw = dw;

	return TRUE;
}


// AddEventToStreamBuffer
//
// Put the given event into the given stream buffer at the given location
// pteTemp must point to an event filled out in accordance with the
// description given in GetTrackEvent
//
// Handles its own error notification by displaying to the appropriate
// output device (either our debugging window, or the screen).
int CMIDI :: AddEventToStreamBuffer( TEMPEVENT * pteTemp, CONVERTINFO *lpciInfo ) {
	MIDIEVENT * pmeEvent = (MIDIEVENT *)( lpciInfo->mhBuffer.lpData
							+ lpciInfo->dwStartOffset

⌨️ 快捷键说明

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