diff --git a/docs/wiki/What-is-LittlePiggyTracker.md b/docs/wiki/What-is-LittlePiggyTracker.md index 1a3b4fe4..cbc37348 100644 --- a/docs/wiki/What-is-LittlePiggyTracker.md +++ b/docs/wiki/What-is-LittlePiggyTracker.md @@ -507,7 +507,7 @@ ARPG 4050: loops between original pitch, +4 semitones, +0 semitones, + 5 semiton ## HOP aabb -**play position will jump to the next phrase in a chain, jumping directly at position bb in the phrase.** +**play position will jump to the next phrase in a chain, jumping directly at position bb in the phrase, unless bb = FF, in which case the channel will be stopped** - hop is instant: instrument triggers and commands on the same row will be run. - no effect on instruments diff --git a/sources/Application/Player/Player.cpp b/sources/Application/Player/Player.cpp index ba1fe7f7..877dc160 100644 --- a/sources/Application/Player/Player.cpp +++ b/sources/Application/Player/Player.cpp @@ -671,6 +671,26 @@ bool Player::ProcessChannelCommand(int channel, FourCC cmd, ushort param) { } else { gr->SetGroove(channel, param); } + } break; + case I_CMD_HOP: { + bool channel_stop = (param & 0xFF) == 0xFF; + if (channel_stop) { + // Stop current channel + mixer_->StopChannel(channel); + + // If all channels are stopped, stop the song if in song mode. + if (GetSequencerMode() == SM_SONG) { + bool all_channels_stopped = true; + for (int c = 0; c < SONG_CHANNEL_COUNT; c++) { + all_channels_stopped &= !mixer_->IsChannelPlaying(c); + } + if (all_channels_stopped) { + Stop(); + } + } else { + liveQueueingMode_[channel] = QM_NONE; + } + } } break; case I_CMD_STOP: { switch (GetSequencerMode()) { @@ -882,14 +902,24 @@ void Player::playCursorPosition(int channel) { int Player::getChannelHop(int channel, int pos) { int phrase = viewData_->currentPlayPhrase_[channel]; - FourCC cc = viewData_->song_->phrase_->cmd1_[phrase * 16 + pos]; - if (cc == I_CMD_HOP) { - return (viewData_->song_->phrase_->param1_[phrase * 16 + pos]) & 0xF; - } - cc = viewData_->song_->phrase_->cmd2_[phrase * 16 + pos]; - if (cc == I_CMD_HOP) { - return (viewData_->song_->phrase_->param2_[phrase * 16 + pos]) & 0xF; - } + + Phrase *song_phrase = viewData_->song_->phrase_; + FourCC cc[] = {song_phrase->cmd1_[phrase * 16 + pos], + song_phrase->cmd2_[phrase * 16 + pos]}; + int param[] = {song_phrase->param1_[phrase * 16 + pos], + song_phrase->param2_[phrase * 16 + pos]}; + + // First hop takes precedence. + for(int i = 0; i < 2; i++) + if (cc[i] == I_CMD_HOP) { + int arg = param[i] & 0xFF; + if (arg != 0xFF) + return arg & 0xF; + else + // 'Hop stop' deferred to command processing stage. + return -1; + } + return -1; }