Example #1
0
void SoundGen2GS::advanceMidiPlayer() {
	if (_disableMidi)
		return;

	const uint8 *p;
	uint8 parm1, parm2;
	static uint8 cmd, chn;

	if (_playingSound == -1 || _vm->_game.sounds[_playingSound] == NULL) {
		warning("Error playing Apple IIGS MIDI sound resource");
		_playing = false;
		return;
	}

	IIgsMidi *midiObj = (IIgsMidi *) _vm->_game.sounds[_playingSound];

	_ticks++;
	_playing = true;
	p = midiObj->getPtr();

	while (true) {
		// Check for end of MIDI sequence marker (Can also be here before delta-time)
		if (*p == MIDI_STOP_SEQUENCE) {
			debugC(3, kDebugLevelSound, "End of MIDI sequence (Before reading delta-time)");
			_playing = false;
			midiObj->rewind();
			return;
		}
		if (*p == MIDI_TIMER_SYNC) {
			debugC(3, kDebugLevelSound, "Timer sync");
			p++; // Jump over the timer sync byte as it's not needed
			continue;
		}

		// Check for delta time
		uint8 delta = *p;
		if (midiObj->_ticks + delta > _ticks)
			break;
		midiObj->_ticks += delta;
		p++;

		// Check for end of MIDI sequence marker (This time it after reading delta-time)
		if (*p == MIDI_STOP_SEQUENCE) {
			debugC(3, kDebugLevelSound, "End of MIDI sequence (After reading delta-time)");
			_playing = false;
			midiObj->rewind();
			return;
		}

		// Separate byte into command and channel if it's a command byte.
		// Otherwise use running status (i.e. previously set command and channel).
		if (*p & 0x80) {
			cmd = *p++;
			chn = cmd & 0x0f;
			cmd >>= 4;
		}

		switch (cmd) {
		case MIDI_NOTE_OFF:
			parm1 = *p++;
			parm2 = *p++;
			debugC(3, kDebugLevelSound, "channel %X: note off (key = %d, velocity = %d)", chn, parm1, parm2);
			midiNoteOff(chn, parm1, parm2);
			break;
		case MIDI_NOTE_ON:
			parm1 = *p++;
			parm2 = *p++;
			debugC(3, kDebugLevelSound, "channel %X: note on (key = %d, velocity = %d)", chn, parm1, parm2);
			midiNoteOn(chn, parm1, parm2);
			break;
		case MIDI_CONTROLLER:
			parm1 = *p++;
			parm2 = *p++;
			debugC(3, kDebugLevelSound, "channel %X: controller %02X = %02X", chn, parm1, parm2);
			// The tested Apple IIGS AGI MIDI resources only used
			// controllers 0 (Bank select?), 7 (Volume) and 64 (Sustain On/Off).
			// Controller 0's parameter was in range 94-127,
			// controller 7's parameter was in range 0-127 and
			// controller 64's parameter was always 0 (i.e. sustain off).
			switch (parm1) {
			case 7:
				_channels[chn].setVolume(parm2);
				break;
			}
			break;
		case MIDI_PROGRAM_CHANGE:
			parm1 = *p++;
			debugC(3, kDebugLevelSound, "channel %X: program change %02X", chn, parm1);
			_channels[chn].setInstrument(getInstrument(parm1));
			break;
		case MIDI_PITCH_WHEEL:
			parm1 = *p++;
			parm2 = *p++;
			debugC(3, kDebugLevelSound, "channel %X: pitch wheel (unimplemented)", chn);
			break;

		default:
			debugC(3, kDebugLevelSound, "channel %X: unimplemented command %02X", chn, cmd);
			break;
		}
	}
Example #2
0
void SoundGen2GS::playMidiSound() {
	if (_disabledMidi)
		return;

	const uint8 *p;
	uint8 parm1, parm2;
	static uint8 cmd, ch;

	if (_playingSound == -1 || _vm->_game.sounds[_playingSound] == NULL) {
		warning("Error playing Apple IIGS MIDI sound resource");
		_playing = false;

		return;
	}

	IIgsMidi *midiObj = (IIgsMidi *) _vm->_game.sounds[_playingSound];

	_playing = true;
	p = midiObj->getPtr();

	midiObj->_soundBufTicks++;

	while (true) {
		uint8 readByte = *p;

		// Check for end of MIDI sequence marker (Can also be here before delta-time)
		if (readByte == MIDI_BYTE_STOP_SEQUENCE) {
			debugC(3, kDebugLevelSound, "End of MIDI sequence (Before reading delta-time)");
			_playing = false;

			midiObj->rewind();

			return;
		} else if (readByte == MIDI_BYTE_TIMER_SYNC) {
			debugC(3, kDebugLevelSound, "Timer sync");
			p++; // Jump over the timer sync byte as it's not needed

			continue;
		}

		uint8 deltaTime = readByte;
		if (midiObj->_midiTicks + deltaTime > midiObj->_soundBufTicks) {
			break;
		}
		midiObj->_midiTicks += deltaTime;
		p++; // Jump over the delta-time byte as it was already taken care of

		// Check for end of MIDI sequence marker (This time it after reading delta-time)
		if (*p == MIDI_BYTE_STOP_SEQUENCE) {
			debugC(3, kDebugLevelSound, "End of MIDI sequence (After reading delta-time)");
			_playing = false;

			midiObj->rewind();

			return;
		}

		// Separate byte into command and channel if it's a command byte.
		// Otherwise use running status (i.e. previously set command and channel).
		if (*p & 0x80) {
			cmd = *p++;
			ch = cmd & 0x0f;
			cmd >>= 4;
		}

		switch (cmd) {
		case MIDI_CMD_NOTE_OFF:
			parm1 = *p++;
			parm2 = *p++;
			midiNoteOff(ch, parm1, parm2);
			break;
		case MIDI_CMD_NOTE_ON:
			parm1 = *p++;
			parm2 = *p++;
			midiNoteOn(ch, parm1, parm2);
			break;
		case MIDI_CMD_CONTROLLER:
			parm1 = *p++;
			parm2 = *p++;
			midiController(ch, parm1, parm2);
			break;
		case MIDI_CMD_PROGRAM_CHANGE:
			parm1 = *p++;
			midiProgramChange(ch, parm1);
			break;
		case MIDI_CMD_PITCH_WHEEL:
			parm1 = *p++;
			parm2 = *p++;

			uint16 wheelPos = ((parm2 & 0x7F) << 7) | (parm1 & 0x7F); // 14-bit value
			midiPitchWheel(wheelPos);
			break;
		}
	}
Example #3
0
void InstrumentTrack::processInEvent( const MidiEvent& event, const MidiTime& time, f_cnt_t offset )
{
	if( Engine::getSong()->isExporting() )
	{
		return;
	}

	bool eventHandled = false;

	switch( event.type() )
	{
		// we don't send MidiNoteOn, MidiNoteOff and MidiKeyPressure
		// events to instrument as NotePlayHandle will send them on its
		// own
		case MidiNoteOn:
			if( event.velocity() > 0 )
			{
				if( m_notes[event.key()] == NULL )
				{
					NotePlayHandle* nph =
						NotePlayHandleManager::acquire(
								this, offset,
								typeInfo<f_cnt_t>::max() / 2,
								Note( MidiTime(), MidiTime(), event.key(), event.volume( midiPort()->baseVelocity() ) ),
								NULL, event.channel(),
								NotePlayHandle::OriginMidiInput );
					m_notes[event.key()] = nph;
					if( ! Engine::mixer()->addPlayHandle( nph ) )
					{
						m_notes[event.key()] = NULL;
					}
				}
				eventHandled = true;
				break;
			}

		case MidiNoteOff:
			if( m_notes[event.key()] != NULL )
			{
				// do actual note off and remove internal reference to NotePlayHandle (which itself will
				// be deleted later automatically)
				Engine::mixer()->requestChangeInModel();
				m_notes[event.key()]->noteOff( offset );
				if (isSustainPedalPressed() &&
					m_notes[event.key()]->origin() ==
					m_notes[event.key()]->OriginMidiInput)
				{
					m_sustainedNotes << m_notes[event.key()];
				}
				m_notes[event.key()] = NULL;
				Engine::mixer()->doneChangeInModel();
			}
			eventHandled = true;
			break;

		case MidiKeyPressure:
			if( m_notes[event.key()] != NULL )
			{
				// setVolume() calls processOutEvent() with MidiKeyPressure so the
				// attached instrument will receive the event as well
				m_notes[event.key()]->setVolume( event.volume( midiPort()->baseVelocity() ) );
			}
			eventHandled = true;
			break;

		case MidiPitchBend:
			// updatePitch() is connected to m_pitchModel::dataChanged() which will send out
			// MidiPitchBend events
			m_pitchModel.setValue( m_pitchModel.minValue() + event.pitchBend() * m_pitchModel.range() / MidiMaxPitchBend );
			break;

		case MidiControlChange:
			if( event.controllerNumber() == MidiControllerSustain )
			{
				if( event.controllerValue() > MidiMaxControllerValue/2 )
				{
					m_sustainPedalPressed = true;
				}
				else if (isSustainPedalPressed())
				{
					for (NotePlayHandle* nph : m_sustainedNotes)
					{
						if (nph && nph->isReleased())
						{
							if( nph->origin() ==
								nph->OriginMidiInput)
							{
								nph->setLength(
									MidiTime( static_cast<f_cnt_t>(
									nph->totalFramesPlayed() /
									Engine::framesPerTick() ) ) );
								midiNoteOff( *nph );
							}
						}
					}
					m_sustainedNotes.clear();
					m_sustainPedalPressed = false;
				}
			}
			if( event.controllerNumber() == MidiControllerAllSoundOff ||
				event.controllerNumber() == MidiControllerAllNotesOff ||
				event.controllerNumber() == MidiControllerOmniOn ||
				event.controllerNumber() == MidiControllerOmniOff ||
				event.controllerNumber() == MidiControllerMonoOn ||
				event.controllerNumber() == MidiControllerPolyOn )
			{
				silenceAllNotes();
			}
			break;

		case MidiMetaEvent:
			// handle special cases such as note panning
			switch( event.metaEvent() )
			{
				case MidiNotePanning:
					if( m_notes[event.key()] != NULL )
					{
						eventHandled = true;
						m_notes[event.key()]->setPanning( event.panning() );
					}
					break;
				default:
					qWarning( "InstrumentTrack: unhandled MIDI meta event: %i", event.metaEvent() );
					break;
			}
			break;

		default:
			break;
	}

	if( eventHandled == false && instrument()->handleMidiEvent( event, time, offset ) == false )
	{
		qWarning( "InstrumentTrack: unhandled MIDI event %d", event.type() );
	}

}