Esempio n. 1
0
bool QSoundSeq::PostLoad()
{
	if (readMode != READMODE_CONVERT_TO_MIDI)
		return true;

	// We need to add pitch bend events for vibrato, which is controlled by a software LFO
	//  This is actually a bit tricky because the LFO is running independent of the sequence
	//  tempo.  It gets updated (251/4) times a second, always.  We will have to convert 
	//  ticks in our sequence into absolute elapsed time, which means we also need to keep
	//  track of any tempo events that change the absolute time per tick.
	vector<MidiEvent*> tempoEvents;
	vector<MidiTrack*>& miditracks = midi->aTracks;

	// First get all tempo events, we assume they occur on track 1
	for (unsigned int i = 0; i < miditracks[0]->aEvents.size(); i++)
	{
		MidiEvent* event = miditracks[0]->aEvents[i];
		if (event->GetEventType() == MIDIEVENT_TEMPO)
			tempoEvents.push_back(event);
	}

	// Now for each track, gather all vibrato events, lfo events, pitch bend events and track end events
	for (unsigned int i = 0; i < miditracks.size(); i++)
	{
		vector<MidiEvent*> events(tempoEvents);
		MidiTrack* track = miditracks[i];
		int channel = this->aTracks[i]->channel;

		for (unsigned int j = 0; j < track->aEvents.size(); j++)
		{
			MidiEvent* event = miditracks[i]->aEvents[j];
			MidiEventType type = event->GetEventType();
			if (type == MIDIEVENT_MARKER || type == MIDIEVENT_PITCHBEND || type == MIDIEVENT_ENDOFTRACK)
				events.push_back(event);
		}
		// We need to sort by priority so that vibrato events get ordered correctly.  Since they cause the
		//  pitch bend range to be set, they need to occur before pitchbend events.
		stable_sort(events.begin(), events.end(), PriorityCmp());	//Sort all the events by priority
		stable_sort(events.begin(), events.end(), AbsTimeCmp());	//Sort all the events by absolute time, so that delta times can be recorded correctly
		

		// And now we actually add vibrato and pitch bend events
		const uint32_t ppqn = GetPPQN();					// pulses (ticks) per quarter note
		const uint32_t mpLFOt = (uint32_t)((1/(251/4.0)) * 1000000);	// microseconds per LFO tick
		uint32_t mpqn = 500000;							// microseconds per quarter note - 120 bpm default
		uint32_t mpt = mpqn / ppqn;						// microseconds per MIDI tick
		short pitchbend = 0;							// pitch bend in cents
		int pitchbendRange = 200;						// pitch bend range in cents default 2 semitones
		double vibrato = 0;								// vibrato depth in cents
		uint16_t tremelo = 0;								// tremelo depth.  we divide this value by 0x10000 to get percent amplitude attenuation
		uint16_t lfoRate = 0;								// value added to lfo env every lfo tick
		uint32_t lfoVal = 0;									// LFO envelope value. 0 - 0xFFFFFF .  Effective envelope range is -0x1000000 to +0x1000000
		int lfoStage = 0;								// 0 = rising from mid, 1 = falling from peak, 2 = falling from mid 3 = rising from bottom
		short lfoCents = 0;								// cents adjustment from most recent LFO val excluding pitchbend.
		long effectiveLfoVal = 0;
		//bool bLfoRising = true;;						// is LFO rising or falling?
		
		uint32_t startAbsTicks = 0;							// The MIDI tick time to start from for a given vibrato segment

		size_t numEvents = events.size();
		for (size_t j = 0; j < numEvents; j++)
		{
			MidiEvent* event = events[j];
			uint32_t curTicks = event->AbsTime;			//current absolute ticks
			
			if (curTicks > 0 /*&& (vibrato > 0 || tremelo > 0)*/ && startAbsTicks < curTicks)
			{
				// if we're starting a fresh vibrato segment, let's start the LFO env at 0 and set it to rise
				/*if (prevVibrato == 0 && prevTremelo == 0)
				{
					lfoVal = 0;
					lfoStage = 0;
				}*/
				
				long segmentDurTicks = curTicks - startAbsTicks;
				double segmentDur = segmentDurTicks * mpt;	// duration of this segment in micros
				double lfoTicks = segmentDur / (double)mpLFOt;
				double numLfoPhases = (lfoTicks * (double)lfoRate) / (double)0x20000;
				//double midiTicksPerPhase = (curTicks-startAbsTicks) / numLfoPhases;
				//double centsPerMidiTick = vibrato / midiTicksPerPhase;
				double lfoRatePerMidiTick = (numLfoPhases * 0x20000) / (double)segmentDurTicks;

				const uint8_t tickRes = 16;
				uint32_t lfoRatePerLoop = (uint32_t)((tickRes * lfoRatePerMidiTick) * 256);

				for (int t = 0; t < segmentDurTicks; t += tickRes)
				{
					lfoVal += lfoRatePerLoop;
					if (lfoVal > 0xFFFFFF)
					{
						lfoVal -= 0x1000000;
						lfoStage = (lfoStage+1) % 4;
					}
					effectiveLfoVal = lfoVal;
					if (lfoStage == 1)
						effectiveLfoVal = 0x1000000 - lfoVal;
					else if (lfoStage == 2)
						effectiveLfoVal = -((long)lfoVal);
					else if (lfoStage == 3)
						effectiveLfoVal = -0x1000000 + lfoVal;

					double lfoPercent = (effectiveLfoVal / (double)0x1000000);

					if (vibrato > 0)
					{
						lfoCents = (short)(lfoPercent * vibrato);
						track->InsertPitchBend(channel, (short)(((lfoCents + pitchbend) / (double)pitchbendRange) * 8192), startAbsTicks + t);
					}

					if (tremelo > 0)
					{
						uint8_t expression = ConvertPercentAmpToStdMidiVal((0x10000 - (tremelo*abs(lfoPercent))) / (double)0x10000);
						track->InsertExpression(channel, expression, startAbsTicks + t);
					}
				}
				// TODO add adjustment for segmentDurTicks % tickRes						
			}


			switch(event->GetEventType())
			{
			case MIDIEVENT_TEMPO:
				{
					TempoEvent* tempoevent = (TempoEvent*)event;
					mpqn = tempoevent->microSecs;
					mpt = mpqn / ppqn;
				}
				break;
			case MIDIEVENT_ENDOFTRACK:

				break;
			case MIDIEVENT_MARKER:
				{
					MarkerEvent* marker = (MarkerEvent*)event;
					if (marker->name == "vibrato")
					{
						vibrato = vibrato_depth_table[marker->databyte1] * (100/256.0);
						//pitchbendRange = max(200, (vibrato + 50));		//50 cents to allow for pitchbend values, which range -50/+50
						pitchbendRange = (int)max(200, ceil((vibrato+50)/100.0)*100);	//+50 cents to allow for pitchbend values, which range -50/+50
						track->InsertPitchBendRange(channel, pitchbendRange/100, pitchbendRange%100, curTicks);
						
						lfoCents = (short)((effectiveLfoVal / (double)0x1000000) * vibrato);
						
						if (curTicks > 0)
							track->InsertPitchBend(channel, (short)(((lfoCents + pitchbend) / (double)pitchbendRange) * 8192), curTicks);
					}
					else if (marker->name == "tremelo")
					{
						tremelo = tremelo_depth_table[marker->databyte1];
						if (tremelo == 0)
							track->InsertExpression(channel, 127, curTicks);
					}
					else if (marker->name == "lfo")
					{
						lfoRate = lfo_rate_table[marker->databyte1];
					}
					else if (marker->name == "resetlfo")
					{
						if (marker->databyte1 != 1)
							break;
						lfoVal = 0;
						effectiveLfoVal = 0;
						lfoStage = 0;
						lfoCents = 0;
						if (vibrato > 0)
							track->InsertPitchBend(channel, (short)(((0 + pitchbend) / (double)pitchbendRange) * 8192), curTicks);
						if (tremelo > 0)
							track->InsertExpression(channel, 127, curTicks);
					}
					else if (marker->name == "pitchbend")
					{
						pitchbend = (short)(((char)marker->databyte1 / 256.0) * 100);
						//stringstream stream;
						//stream << "cents: " << cents << "  finalval: " <<  (cents / (double)pitchbendRange) * 8192 << "\n";
						//ATLTRACE(stream.str().c_str());
						track->InsertPitchBend(channel, (short)(((lfoCents + pitchbend) / (double)pitchbendRange) * 8192), curTicks);
					}
				}
				break;
			}
			startAbsTicks = curTicks;

		}
	}

	

	return VGMSeq::PostLoad();
}