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

📄 midi.cpp

📁 软件类别:多 媒 体 软件大小:12KB 运行环境:windows/ 软件 类别:多 媒 体 软件大小:12KB 运行环境:windows/
💻 CPP
📖 第 1 页 / 共 3 页
字号:
							+ lpciInfo->dwBytesRecorded );

	// When we see a new, empty buffer, set the start time on it...
	if( !lpciInfo->dwBytesRecorded )
		lpciInfo->tkStart = m_tkCurrentTime;

	// Use the above set start time to figure out how much longer we should fill
	// this buffer before officially declaring it as "full"
	if( m_tkCurrentTime - lpciInfo->tkStart > m_dwBufferTickLength )
		if( lpciInfo->bTimesUp ) {
			lpciInfo->bTimesUp = FALSE;
			return CONVERTERR_BUFFERFULL;
		} 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 + -