Пример #1
0
bool Player_AD::parseCommand() {
	uint command = _musicData[_curOffset++];
	if (command == 0xFF) {
		// META EVENT
		// Get the command number.
		command = _musicData[_curOffset++];
		if (command == 47) {
			// End of track
			if (_loopFlag) {
				// In case the track is looping jump to the start.
				_curOffset = _musicLoopStart;
				_nextEventTimer = 0;
			} else {
				// Otherwise completely stop playback.
				stopMusic();
			}
			return true;
		} else if (command == 88) {
			// This is proposedly a debug information insertion. The CMS
			// player code handles this differently, but is still using
			// the same resources...
			_curOffset += 5;
		} else if (command == 81) {
			// Change tempo. This is used exclusively in Loom.
			const uint timing = _musicData[_curOffset + 2] | (_musicData[_curOffset + 1] << 8);
			_musicTicks = 0x73000 / timing;
			command = _musicData[_curOffset++];
			_curOffset += command;
		} else {
			// In case an unknown meta event occurs just skip over the
			// data by using the length supplied.
			command = _musicData[_curOffset++];
			_curOffset += command;
		}
	} else {
		if (command >= 0x90) {
			// NOTE ON
			// Extract the channel number and save it in command.
			command -= 0x90;

			const uint instrOffset = _instrumentOffset[command];
			if (instrOffset) {
				if (_musicData[instrOffset + 13] != 0) {
					setupRhythm(_musicData[instrOffset + 13], instrOffset);
				} else {
					// Priority 256 makes sure we always prefer music
					// channels over SFX channels.
					int channel = allocateHWChannel(256);
					if (channel != -1) {
						setupChannel(channel, _musicData + instrOffset);
						_voiceChannels[channel].lastEvent = command + 0x90;
						_voiceChannels[channel].frequency = _musicData[_curOffset];
						setupFrequency(channel, _musicData[_curOffset]);
					}
				}
			}
		} else {
			// NOTE OFF
			const uint note = _musicData[_curOffset];
			command += 0x10;

			// Find the output channel which plays the note.
			uint channel = 0xFF;
			for (int i = 0; i < ARRAYSIZE(_voiceChannels); ++i) {
				if (_voiceChannels[i].frequency == note && _voiceChannels[i].lastEvent == command) {
					channel = i;
					break;
				}
			}

			if (channel != 0xFF) {
				// In case a output channel playing the note was found,
				// stop it.
				noteOff(channel);
			} else {
				// In case there is no such note this will disable the
				// rhythm instrument played on the channel.
				command -= 0x90;
				const uint instrOffset = _instrumentOffset[command];
				if (instrOffset && _musicData[instrOffset + 13] != 0) {
					const uint rhythmInstr = _musicData[instrOffset + 13];
					if (rhythmInstr < 6) {
						_mdvdrState &= _mdvdrTable[rhythmInstr] ^ 0xFF;
						writeReg(0xBD, _mdvdrState);
					}
				}
			}
		}

		_curOffset += 2;
	}

	return false;
}
Пример #2
0
void MusicPlayer::callback() {
	if (!_isPlaying)
		return;

	_musicTimer += _musicTicks;
	if (_musicTimer < _timerLimit)
		return;

	_musicTimer -= _timerLimit;

	--_nextEventTimer;
	if (_nextEventTimer)
		return;

	while (true) {
		uint8_t command = _file.at(_curOffset++);
		if (command == 0xFF) {
			command = _file.at(_curOffset++);
			if (command == 47) {
				// End of track
				_isPlaying = false;
				return;
			} else if (command == 88) {
				// This is proposedly a debug information insertion. The CMS
				// player code handles this differently, but is still using
				// the same resources...
				_curOffset += 5;
			} else if (command == 81) {
				uint16_t timing = _file.at(_curOffset + 2) | (_file.at(_curOffset + 1) << 8);
				_musicTicks = 0x73000 / timing;
				command = _file.at(_curOffset++);
				_curOffset += command;
			} else {
				command = _file.at(_curOffset++);
				_curOffset += command;
			}
		} else {
			if (command >= 0x90) {
				command -= 0x90;

				const uint16_t instrOffset = _instrumentOffset[command];
				if (instrOffset) {
					if (_file.at(instrOffset + 13) != 0) {
						setupRhythm(_file[instrOffset + 13], instrOffset);
					} else {
						uint8_t channel = findFreeChannel();
						if (channel != 0xFF) {
							noteOff(channel);
							setupChannel(channel, instrOffset);
							_channelLastEvent[channel] = command + 0x90;
							_channelFrequency[channel] = _file.at(_curOffset);
							setupFrequency(channel, _file[_curOffset]);
						}
					}
				}
			} else {
				const uint8_t note = _file.at(_curOffset);
				command += 0x10;

				uint8_t channel = 0xFF;
				for (uint8_t i = 0; i < _voiceChannels; ++i) {
					if (_channelFrequency[i] == note && _channelLastEvent[i] == command) {
						channel = i;
						break;
					}
				}

				if (channel != 0xFF) {
					noteOff(channel);
				} else {
					command -= 0x90;
					const uint16_t instrOffset = _instrumentOffset[command];
					if (instrOffset && _file.at(instrOffset + 13) != 0) {
						const uint8_t rhythmInstr = _file[instrOffset + 13];
						//if (rhythmInstr >= 6)
						//	throw std::range_error("rhythmInstr >= 6");
						if (rhythmInstr < 6) {
							_mdvdrState &= _mdvdrTable[rhythmInstr] ^ 0xFF;
							writeReg(0xBD, _mdvdrState);
						}
					}
				}
			}

			_curOffset += 2;
		}

		if (_file.at(_curOffset) != 0)
			break;
		++_curOffset;
	}

	_nextEventTimer = _file.at(_curOffset++);
	if (_nextEventTimer & 0x80) {
		_nextEventTimer -= 0x80;
		_nextEventTimer <<= 7;
		_nextEventTimer |= _file.at(_curOffset++);
	}

	_nextEventTimer >>= _isLoom ? 2 : 1;
	if (!_nextEventTimer)
		_nextEventTimer = 1;
}