示例#1
0
文件: midi.cpp 项目: faesong/oom
void Audio::collectEvents(MidiTrack* track, unsigned int cts, unsigned int nts)
{
	int port = track->outPort();
	int channel = track->outChannel();
	int defaultPort = port;

	MidiDevice* md = midiPorts[port].device();
	MPEventList* playEvents = md->playEvents();
	MPEventList* stuckNotes = md->stuckNotes();

	PartList* pl = track->parts();
	for (iPart p = pl->begin(); p != pl->end(); ++p)
	{
		MidiPart* part = (MidiPart*) (p->second);
		// dont play muted parts
		if (part->mute())
			continue;
		EventList* events = part->events();
		unsigned partTick = part->tick();
		unsigned partLen = part->lenTick();
		int delay = track->delay;

		if (cts > nts)
		{
			printf("processMidi: FATAL: cur > next %d > %d\n",
					cts, nts);
			return;
		}
		unsigned offset = delay + partTick;
		if (offset > nts)
			continue;
		unsigned stick = (offset > cts) ? 0 : cts - offset;
		unsigned etick = nts - offset;
		// By T356. Do not play events which are past the end of this part.
		if (etick > partLen)
			continue;

		iEvent ie = events->lower_bound(stick);
		iEvent iend = events->lower_bound(etick);

		for (; ie != iend; ++ie)
		{
			Event ev = ie->second;
			port = defaultPort; //Reset each loop
			//
			//  dont play any meta events
			//
			if (ev.type() == Meta)
				continue;
			if (track->type() == Track::DRUM)
			{
				int instr = ev.pitch();
				// ignore muted drums
				if (ev.isNote() && drumMap[instr].mute)
					continue;
			}
			unsigned tick = ev.tick() + offset;
			unsigned frame = tempomap.tick2frame(tick) + frameOffset;
			switch (ev.type())
			{
				case Note:
				{
					int len = ev.lenTick();
					int pitch = ev.pitch();
					int velo = ev.velo();
					if (track->type() == Track::DRUM)
					{
						//
						// Map drum-notes to the drum-map values
						//
						int instr = ev.pitch();
						pitch = drumMap[instr].anote;
						port = drumMap[instr].port; //This changes to non-default port
						channel = drumMap[instr].channel;
						velo = int(double(velo) * (double(drumMap[instr].vol) / 100.0));
					}
					else
					{
						//
						// transpose non drum notes
						//
						pitch += (track->transposition + song->globalPitchShift());
					}

					if (pitch > 127)
						pitch = 127;
					if (pitch < 0)
						pitch = 0;
					velo += track->velocity;
					velo = (velo * track->compression) / 100;
					if (velo > 127)
						velo = 127;
					if (velo < 1) // no off event
						velo = 1;
					len = (len * track->len) / 100;
					if (len <= 0) // dont allow zero length
						len = 1;
					int veloOff = ev.veloOff();

					if (port == defaultPort)
					{
						//printf("Adding event normally: frame=%d port=%d channel=%d pitch=%d velo=%d\n",frame, port, channel, pitch, velo);

						// p3.3.25
						// If syncing to external midi sync, we cannot use the tempo map.
						// Therefore we cannot get sub-tick resolution. Just use ticks instead of frames.
						if (extSyncFlag.value())
							playEvents->add(MidiPlayEvent(tick, port, channel, 0x90, pitch, velo));
						else

							playEvents->add(MidiPlayEvent(frame, port, channel, 0x90, pitch, velo));

						stuckNotes->add(MidiPlayEvent(tick + len, port, channel,
								veloOff ? 0x80 : 0x90, pitch, veloOff));
					}
					else
					{ //Handle events to different port than standard.
						MidiDevice* mdAlt = midiPorts[port].device();
						if (mdAlt)
						{

							// p3.3.25
							if (extSyncFlag.value())
								mdAlt->playEvents()->add(MidiPlayEvent(tick, port, channel, 0x90, pitch, velo));
							else

								mdAlt->playEvents()->add(MidiPlayEvent(frame, port, channel, 0x90, pitch, velo));

							mdAlt->stuckNotes()->add(MidiPlayEvent(tick + len, port, channel,
									veloOff ? 0x80 : 0x90, pitch, veloOff));
						}
					}

					if (velo > track->activity())
						track->setActivity(velo);
				}
					break;

					// Added by T356.
				case Controller:
				{
					//int len   = ev.lenTick();
					//int pitch = ev.pitch();
					if (track->type() == Track::DRUM)
					{
						int ctl = ev.dataA();
						// Is it a drum controller event, according to the track port's instrument?
						MidiController *mc = midiPorts[defaultPort].drumController(ctl);
						if (mc)
						{
							int instr = ctl & 0x7f;
							ctl &= ~0xff;
							int pitch = drumMap[instr].anote & 0x7f;
							port = drumMap[instr].port; //This changes to non-default port
							channel = drumMap[instr].channel;
							MidiDevice* mdAlt = midiPorts[port].device();
							if (mdAlt)
							{
								// p3.3.25
								// If syncing to external midi sync, we cannot use the tempo map.
								// Therefore we cannot get sub-tick resolution. Just use ticks instead of frames.
								if (extSyncFlag.value())
									mdAlt->playEvents()->add(MidiPlayEvent(tick, port, channel,
										ME_CONTROLLER, ctl | pitch, ev.dataB()));
								else

									//playEvents->add(MidiPlayEvent(frame, port, channel, ev));
									mdAlt->playEvents()->add(MidiPlayEvent(frame, port, channel,
										ME_CONTROLLER, ctl | pitch, ev.dataB()));

							}
							break;
						}
					}
					// p3.3.25
					if (extSyncFlag.value())
						playEvents->add(MidiPlayEvent(tick, port, channel, ev));
					else

						playEvents->add(MidiPlayEvent(frame, port, channel, ev));
				}
					break;


				default:
					// p3.3.25
					if (extSyncFlag.value())
						playEvents->add(MidiPlayEvent(tick, port, channel, ev));
					else

						playEvents->add(MidiPlayEvent(frame, port, channel, ev));

					break;
			}
		}
	}
}