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

📄 musicsys.cpp

📁 游戏编程精粹2第六章源码
💻 CPP
📖 第 1 页 / 共 2 页
字号:
/* Copyright (C) Scott Patterson, 2001. 
 * All rights reserved worldwide.
 *
 * This software is provided "as is" without express or implied
 * warranties. You may freely copy and compile this source into
 * applications you distribute provided that the copyright text
 * below is included in the resulting source code, for example:
 * "Portions Copyright (C) Scott Patterson, 2001"
 */

// MusicSys.cpp

#include "StdAfx.h"
#include "math.h"
#include "MusicSys.h"
#include "InstrumentData.h"
#include "SequenceData.h"
#include "EventData.h"
#include "TextBoxes.h"

int MusicSequencerSystem_t::ms_anAudioTaper[128];

MusicSequencerSystemConfig_t::MusicSequencerSystemConfig_t()
{
	SetSequenceCount( 8 );
	SetTrackCount( 32 );
	SetVoiceArchitectureTypeCount( 2 );
	SetVoiceCount( 0, 32 );
	SetVoiceCount( 1, 32 );
}

MusicSequencerSystemConfig_t::~MusicSequencerSystemConfig_t()
{
}

void MusicSequencerSystemConfig_t::SetSequenceCount( int nSequenceCount )
{
	m_nSequenceCount = nSequenceCount;
}

int MusicSequencerSystemConfig_t::GetSequenceCount()
{
	return m_nSequenceCount;
}

void MusicSequencerSystemConfig_t::SetTrackCount( int nTrackCount )
{
	m_nTrackCount = nTrackCount;
}

int MusicSequencerSystemConfig_t::GetTrackCount()
{
	return m_nTrackCount;
}

void MusicSequencerSystemConfig_t::SetVoiceArchitectureTypeCount( int nVoiceArchitectureTypeCount )
{
	m_VoiceCountVector.resize( nVoiceArchitectureTypeCount );
	m_nVoiceArchitectureTypeCount = nVoiceArchitectureTypeCount;
}

int  MusicSequencerSystemConfig_t::GetVoiceArchitectureTypeCount()
{
	return m_nVoiceArchitectureTypeCount;
}

void MusicSequencerSystemConfig_t::SetVoiceCount( int nVoiceArchitectureType, int nVoiceCount )
{
	m_VoiceCountVector[nVoiceArchitectureType] = nVoiceCount;
}

int  MusicSequencerSystemConfig_t::GetVoiceCount( int nVoiceArchitectureType )
{
	return m_VoiceCountVector[nVoiceArchitectureType];
}

MusicSequencerSystem_t::MusicSequencerSystem_t()
{
	m_CSynMix = NULL;
}

MusicSequencerSystem_t::~MusicSequencerSystem_t()
{
}

#define TEST_INIT_ERROR(val,msg) \
	if(val < 0) \
	{ \
		TextBoxes_Output_Printf( OUTPUTMODE_COMMANDS, "\nERROR - %s - %s", CSyn_ErrorCodeToText(val), msg); \
	}

#define TEST_ERROR(val,msg) \
	if(val < 0) \
	{ \
		TextBoxes_Output_Printf( OUTPUTMODE_ERRORS, "ERROR - %s - %s\n", CSyn_ErrorCodeToText(val), msg); \
	}


bool MusicSequencerSystem_t::Init( MusicSequencerSystemConfig_t &Config )
{
	CSynErr result;
	int i, j;

	// create an audio taper table
	double minAmplitude = 0.05;
	ms_anAudioTaper[0] = 0;
	for( i=1; i<128; i++ )
	{
		double fVal = minAmplitude / ( exp( log(minAmplitude) * (i / 127.0) ) );
		ms_anAudioTaper[i] = ConvertDoubleToFixed( fVal );
	}

	// CSyn Init
	/* Create a context for the synthesis to occur. */
	m_CSynContext = CSyn_CreateContext();
	if( m_CSynContext == NULL )
	{
		TextBoxes_Output_Printf( OUTPUTMODE_ERRORS, "\nCould not create CSynContext");
		return false;
	}

	/* CSyn synthesis engine must be started before allocating unit generators. */
	result = CSyn_StartEngine( m_CSynContext, 0, 44100.0 );
	TEST_INIT_ERROR(result, "CSyn_StartEngine");
	if( result < 0 ) return false;
	
	/* Create a mix for all the voices. */
	result = CSynMix_Create( m_CSynContext, &m_CSynMix, 0 );
	TEST_INIT_ERROR(result, "CSynMix_Create");
	if( result < 0 ) return false;

	m_Config = Config;

	// allocate sequence resources
	int nSVSize = m_Config.GetSequenceCount();
	m_SequenceVector.resize( nSVSize );

	// assign sequence resources
	for( i=0; i<nSVSize; i++ )
	{
		Sequence_t *pSequence = &m_SequenceVector[i];
		pSequence->m_bActive = false;
		m_FreeSequencePtrList.push_back( pSequence );
	}

	// allocate track resources
	int nTVSize = m_Config.GetTrackCount();
	m_TrackVector.resize( nTVSize );

	// assign track resources
	for( i=0; i<nTVSize; i++ )
	{
		Track_t *pTrack = &m_TrackVector[i];
		pTrack->m_bActive = false;
		m_FreeTrackPtrList.push_back( pTrack );
	}

	// allocate voice architecture resources
	int nVVVSize = m_Config.GetVoiceArchitectureTypeCount();
	m_VoiceVectorVector.resize( nVVVSize );
	m_ActiveVoicePtrListVector.resize( nVVVSize );
	m_FreeVoicePtrListVector.resize( nVVVSize );

	for( i=0; i<nVVVSize; i++ )
	{
		// allocate voice resources
		int nVVSize = m_Config.GetVoiceCount( i );
		m_VoiceVectorVector[i].resize( nVVSize );

		// assign voice resources
		for( j=0; j<nVVSize; j++ )
		{
			Voice_t *pVoice = &m_VoiceVectorVector[i][j];
			pVoice->m_bActive = false;
			pVoice->m_nVoiceArchitectureType = i;
			result = CSynVoice_Load( m_CSynMix, (enum CSynCircuitType)i, NULL, &pVoice->m_SynVoice );
			TEST_ERROR(result, "CSynVoice_Load");
			m_FreeVoicePtrListVector[i].push_back( pVoice );
		}
	}

	return true;
}

double MusicSequencerSystem_t::GetCSynUsage()
{
	if( m_CSynContext )
	{
		return CSyn_GetUsage( m_CSynContext );
	}
	return 0.0;
}

void MusicSequencerSystem_t::CheckForFinishedVoices()
{
	VoicePtrListVector_t::iterator iVPLV;

	for( iVPLV = m_ActiveVoicePtrListVector.begin(); iVPLV != m_ActiveVoicePtrListVector.end(); ++iVPLV )
	{
		VoicePtrList_t &VoicePtrList = *iVPLV;

		VoicePtrList_t::iterator iVPL;
		iVPL = VoicePtrList.begin();
		while( iVPL != VoicePtrList.end() )
		{
			Voice_t *pVoice = *iVPL;
			++iVPL;
			if( pVoice->m_nReleaseTick && (pVoice->m_nReleaseTick < m_nWorkTick) )
			{
				pVoice->m_pOwner->m_VoicePtrList.remove( pVoice );
				FreeActiveVoice( pVoice );
			}
		}
	}
}

void MusicSequencerSystem_t::PerformTargetStateChanges()
{
	TrackPtrList_t::iterator iTPL;
	for( iTPL = m_ActiveTrackPtrList.begin(); iTPL != m_ActiveTrackPtrList.end(); ++iTPL )
	{
		Track_t *pTrack = *iTPL;

		if( pTrack->m_State.m_Volume != pTrack->m_TargetState.m_Volume )
		{
			if( pTrack->m_State.m_Volume > pTrack->m_TargetState.m_Volume )
			{
				pTrack->m_State.m_Volume -= pTrack->m_Interpolator.m_VolumeStep;
			} else {
				pTrack->m_State.m_Volume += pTrack->m_Interpolator.m_VolumeStep;
			}


			VoicePtrList_t::iterator iVPL;
			for( iVPL = pTrack->m_VoicePtrList.begin(); iVPL != pTrack->m_VoicePtrList.end(); ++iVPL )
			{
				Voice_t *pVoice = *iVPL;
				CSynErr result;
				//CSynFixedPoint amplitude = ms_anAudioTaper[ (pVoice->m_nVelocity * pTrack->m_State.m_Volume) / 128 ];
				CSynFixedPoint amplitude = (u64)ms_anAudioTaper[pVoice->m_nVelocity] * (u64)ms_anAudioTaper[pTrack->m_State.m_Volume] / (1 << 15);
				result = CSynVoice_SetAmplitude( pVoice->m_SynVoice, m_nWorkTick, amplitude );
				TEST_ERROR(result, "CSynVoice_SetAmplitude");
			}

		}
	}
}

void MusicSequencerSystem_t::DisplayStats()
{
	TextBoxes_Stats_Clear();

	TextBoxes_Stats_Printf("\nSequences Active:\n\n");

	SequenceVector_t::iterator iSV;
	for( iSV = m_SequenceVector.begin(); iSV != m_SequenceVector.end(); ++iSV )
	{
		Sequence_t &Sequence = *iSV;
		char ch = Sequence.m_bActive ? 'X' : '-';
		TextBoxes_Stats_Printf("%c", ch);
	}

	TextBoxes_Stats_Printf("\n\nTracks Active:\n\n");

	TrackVector_t::iterator iTV;
	for( iTV = m_TrackVector.begin(); iTV != m_TrackVector.end(); ++iTV )
	{
		Track_t &Track = *iTV;
		char ch = Track.m_bActive ? 'X' : '-';
		TextBoxes_Stats_Printf("%c", ch);
	}

	int nType = 0;
	VoiceVectorVector_t::iterator iVVV;
	for( iVVV = m_VoiceVectorVector.begin(); iVVV != m_VoiceVectorVector.end(); ++iVVV )
	{
		VoiceVector_t &VoiceVector = *iVVV;
		
		TextBoxes_Stats_Printf("\n\nType %d Voices Active:\n\n", nType);

		int nUnusedCount = 0;
		VoiceVector_t::iterator iVV;
		for( iVV = VoiceVector.begin(); iVV != VoiceVector.end(); ++iVV )
		{
			Voice_t &Voice = *iVV;
			if( Voice.m_bActive )
			{
				TextBoxes_Stats_Printf("X");
			} else {
				nUnusedCount++;
			}
		}
		while( nUnusedCount-- )
		{
			TextBoxes_Stats_Printf("-");
		}
		nType++;
	}
}

u32 MusicSequencerSystem_t::CalcTimeStep( u16 qpm, u16 ppq, u16 ups )
{
    u32 ppu;
    u32 temp;

    temp = (u32)qpm * (u32)ppq;
    if( temp < 0x10000 )
    {
        ppu = ((temp * 0x10000) / 60)
              / (u32)ups;
    } else {
        ppu = ((temp / 60) * 0x10000)
              / (u32)ups;
    }

    return(ppu);
}

bool MusicSequencerSystem_t::AudioFrame()
{
	// this is where we might protect against callback reentrance
	// this demo sequencer does not require this since it runs in the game thread

	// display status information for this frame
	DisplayStats();

	// this demo assumes that AudioFrame is called 30 times a second
	// CSyn_GetTickRate returns ticks per second
	// CSyn_GetTickCount returns our current tick time

	// we will call the sequencer update function every 4 ticks which is a rate
	// of 172.265625Hz (we tell our sequencer that our updates per second is 172)

	#define MUSICSYS_UPDATES_PER_SECOND 172

	// get the number of frames per second
	u32 nFramesPerSecond = 30;

	// get the current tick count
	u32 nCurTick = CSyn_GetTickCount( m_CSynContext );

	// calculate the number of tick in a frame
	u32 nFrameTicks = CSyn_GetTickRate( m_CSynContext ) / nFramesPerSecond;

	// calculate the tick at the end of this frame
	u32 nFrameEndTick = nCurTick + nFrameTicks;

	// set the number of frames ahead to schedule
	u32 nFramesAhead = 7;

	// calculate the last scheduled tick that will be due this frame
	u32 nScheduledEndTick = nFrameEndTick + (nFramesAhead * nFrameTicks);

	static int nFirstTime = 1;
	if( nFirstTime )
	{
		// calculate the first scheduled tick that will be due this frame
		u32 nScheduledStartTick = nCurTick + (nFramesAhead * nFrameTicks);

		m_nWorkTick = nScheduledStartTick;
		nFirstTime = 0;
	}

	// this is where we might begin a critical section
	// this demo sequencer does not require this since it runs in the game thread
	// OSys_BeginAudioCriticalSection();

    // for the number of update intervals required on this frame
    // {
	//     iterate over sequences
	//         perform per sequence operations

	//         iterate over tracks
	//             perform per track operations
    //
    //     send low-level commands for this time step
    //     move to next time step
    // }

    // determine the number of update intervals required
    // to deliver during this callback

	// while our work ticks is less than the nScheduledEndTick
	while( m_nWorkTick <= nScheduledEndTick )
	{
		m_nWorkTick += 4;
		Update();
	}

	// this is where we might end a critical section
	// this demo sequencer does not require this since it runs in the game thread
	// OSys_EndAudioCriticalSection();

	return true;
}

void NoteOff_Function( MusicSequencerSystem_t *pMSS, Track_t *pTrack )
{
	CSynErr result;
	int releaseTime;
	EventData_t *pEventData = *pTrack->m_iEDPL;
	int nKey = pEventData->GetEventDataByte( 0 );

	// find any voices with this key to send a note off to
	VoicePtrList_t::iterator iVPL;
	for( iVPL = pTrack->m_VoicePtrList.begin(); iVPL != pTrack->m_VoicePtrList.end(); ++iVPL )
	{
		Voice_t *pVoice = *iVPL;
		if( pVoice->m_nKey == nKey )
		{
			result = CSynVoice_NoteOff( pVoice->m_SynVoice, pMSS->m_nWorkTick, &releaseTime );
			TEST_ERROR(result, "CSynVoice_NoteOff");

			pVoice->m_nReleaseTick = releaseTime + pMSS->m_nWorkTick;
			TextBoxes_Output_Printf( OUTPUTMODE_RELEASES, "NoteOff Key = %3d relTime = %d tick = %d\n",
				nKey, releaseTime, pMSS->m_nWorkTick);
		}
	}
	TextBoxes_Output_Printf( OUTPUTMODE_EVENTS, "NoteOff Key = %3d  relTime = %d tick = %d\n",
		nKey, releaseTime, pMSS->m_nWorkTick);

}

void NoteOn_Function( MusicSequencerSystem_t *pMSS, Track_t *pTrack )
{
	CSynErr result;
	EventData_t *pEventData = *pTrack->m_iEDPL;
	int nKey = pEventData->GetEventDataByte( 0 );
	int nVelocity = pEventData->GetEventDataByte( 1 );
	if( nVelocity == 0 ) 
	{
		NoteOff_Function( pMSS, pTrack );
		return;
	}

	int nType = pTrack->m_pInstrumentData->GetType();
	Voice_t *pVoice = pMSS->MakeActiveVoice( nType );
	if( pVoice )
	{
		result = CSynVoice_SetSound( pVoice->m_SynVoice, pTrack->m_pInstrumentData->GetCSynSound() );
		TEST_ERROR(result, "CSynVoice_SetSound");

		int nPan = pTrack->m_State.m_Pan;
		int nCSynPanValue = ConvertIntegerToFixed(nPan - 64) / 64;
		result = CSynVoice_SetPan( pVoice->m_SynVoice, pMSS->m_nWorkTick, nCSynPanValue );
		TEST_ERROR(result, "CSynVoice_SetPan");
		TextBoxes_Output_Printf( OUTPUTMODE_PANS, "Pan = %3d as fixed = 0x%08x\n", nPan, nCSynPanValue);

		CSynFixedPoint frequency = CSynConvert_PitchToFrequency( ConvertIntegerToFixed( nKey ) );

		// this is a quick test that assumes the sample rate is 44100
		AudioSample *pAudioSample = pTrack->m_pInstrumentData->GetSampleInfoPtr();
		if( pAudioSample->baseNote < (nKey - 12) )
		{
			TextBoxes_Output_Printf( OUTPUTMODE_PITCHES, "Out of range Key = %3d baseNote = %3d Instrument = %3d tick = %d\n",
				nKey, pAudioSample->baseNote, pTrack->m_pInstrumentData->GetID(), pMSS->m_nWorkTick );
		} else {
			TextBoxes_Output_Printf( OUTPUTMODE_PITCHES, "Key = %3d Freq = 0x%08x tick = %d\n",
				nKey, frequency, pMSS->m_nWorkTick);
		}

		//CSynFixedPoint amplitude = pMSS->ms_anAudioTaper[ (nVelocity * pTrack->m_State.m_Volume) / 128 ];
		CSynFixedPoint amplitude = (u64)pMSS->ms_anAudioTaper[nVelocity] * (u64)pMSS->ms_anAudioTaper[pTrack->m_State.m_Volume] / (1 << 15);

		TextBoxes_Output_Printf( OUTPUTMODE_AMPLITUDES, "Vel = %3d Vol = %3d Amp = 0x%08x tick = %d\n",
			nVelocity, pTrack->m_State.m_Volume, amplitude, pMSS->m_nWorkTick );

		//result = CSynVoice_NoteOn( pVoice->m_SynVoice, pMSS->m_nWorkTick, frequency, amplitude );
		result = CSynVoice_NoteOn( pVoice->m_SynVoice, pMSS->m_nWorkTick, frequency, amplitude );
		TEST_ERROR(result, "CSynVoice_NoteOn");

		pVoice->m_pOwner = pTrack;
		pVoice->m_nKey = nKey;
		pVoice->m_nVelocity = nVelocity;
		pTrack->m_VoicePtrList.push_back( pVoice );

		TextBoxes_Output_Printf( OUTPUTMODE_VOICES, "Success Key = %3d Vel = %3d tick = %d\n",
			nKey, nVelocity, pMSS->m_nWorkTick);
	} else {
		TextBoxes_Output_Printf( OUTPUTMODE_VOICES, "Failed Key = %3d Vel = %3d tick = %d\n",
			nKey, nVelocity, pMSS->m_nWorkTick);
	}
	TextBoxes_Output_Printf( OUTPUTMODE_EVENTS, "NoteOn  Key = %3d Velocity = %3d tick = %d\n",
		nKey, nVelocity, pMSS->m_nWorkTick);
}

void SetPan_Function( MusicSequencerSystem_t *pMSS, Track_t *pTrack )
{
	EventData_t *pEventData = *pTrack->m_iEDPL;

	// we already know the controller is 10
	// u8 nController = pEventData->GetEventDataByte( 0 );

	// get the pan value
	u8 nValue = pEventData->GetEventDataByte( 1 );

	// we just set the pan for the next note

⌨️ 快捷键说明

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