From 2adbb166b5514ad0d4c9e18a4f86759d042526d8 Mon Sep 17 00:00:00 2001 From: szeli1 <143485814+szeli1@users.noreply.github.com> Date: Mon, 17 Jun 2024 16:11:55 +0200 Subject: [PATCH] arpeggiator sorted mode fixed (#7025) Fixes an issue where sorted arpeggios over multiple notes used a largely unusable algorithm. piano-octave-arp instead of octave-arp-piano. Fixes #6499 Fixes #4491 --- src/core/InstrumentFunctions.cpp | 67 +++++++++++++++++++++----------- 1 file changed, 45 insertions(+), 22 deletions(-) diff --git a/src/core/InstrumentFunctions.cpp b/src/core/InstrumentFunctions.cpp index 549d658fc..3687c0b74 100644 --- a/src/core/InstrumentFunctions.cpp +++ b/src/core/InstrumentFunctions.cpp @@ -31,6 +31,9 @@ #include "InstrumentTrack.h" #include "PresetPreviewPlayHandle.h" +#include +#include + namespace lmms { @@ -348,10 +351,11 @@ void InstrumentFunctionArpeggio::processNote( NotePlayHandle * _n ) _n->setMasterNote(); const int selected_arp = m_arpModel.value(); + const auto arpMode = static_cast(m_arpModeModel.value()); - ConstNotePlayHandleList cnphv = NotePlayHandle::nphsOfInstrumentTrack( _n->instrumentTrack() ); + ConstNotePlayHandleList cnphv = NotePlayHandle::nphsOfInstrumentTrack(_n->instrumentTrack()); - if( static_cast(m_arpModeModel.value()) != ArpMode::Free && cnphv.size() == 0 ) + if(arpMode != ArpMode::Free && cnphv.size() == 0 ) { // maybe we're playing only a preset-preview-note? cnphv = PresetPreviewPlayHandle::nphsOfInstrumentTrack( _n->instrumentTrack() ); @@ -363,10 +367,25 @@ void InstrumentFunctionArpeggio::processNote( NotePlayHandle * _n ) } } + // avoid playing same key for all + // currently playing notes if sort mode is enabled + if (arpMode == ArpMode::Sort && _n != cnphv.first()) { return; } + const InstrumentFunctionNoteStacking::ChordTable & chord_table = InstrumentFunctionNoteStacking::ChordTable::getInstance(); const int cur_chord_size = chord_table.chords()[selected_arp].size(); - const int range = static_cast(cur_chord_size * m_arpRangeModel.value() * m_arpRepeatsModel.value()); - const int total_range = range * cnphv.size(); + const int total_chord_size = cur_chord_size * cnphv.size(); + // how many notes are in a single chord (multiplied by range) + const int singleNoteRange = static_cast(cur_chord_size * m_arpRangeModel.value() * m_arpRepeatsModel.value()); + // how many notes are in the final chord + const int range = arpMode == ArpMode::Sort ? singleNoteRange * cnphv.size() : singleNoteRange; + + if (arpMode == ArpMode::Sort) + { + std::sort(cnphv.begin(), cnphv.end(), [](const NotePlayHandle* a, const NotePlayHandle* b) + { + return a->key() < b->key(); + }); + } // number of frames that every note should be played const auto arp_frames = (f_cnt_t)(m_arpTimeModel.value() / 1000.0f * Engine::audioEngine()->outputSampleRate()); @@ -375,11 +394,11 @@ void InstrumentFunctionArpeggio::processNote( NotePlayHandle * _n ) // used for calculating remaining frames for arp-note, we have to add // arp_frames-1, otherwise the first arp-note will not be setup // correctly... -> arp_frames frames silence at the start of every note! - int cur_frame = ( ( static_cast(m_arpModeModel.value()) != ArpMode::Free ) ? + int cur_frame = (arpMode != ArpMode::Free ? cnphv.first()->totalFramesPlayed() : - _n->totalFramesPlayed() ) + arp_frames - 1; + _n->totalFramesPlayed()) + arp_frames - 1; // used for loop - f_cnt_t frames_processed = ( static_cast(m_arpModeModel.value()) != ArpMode::Free ) ? cnphv.first()->noteOffset() : _n->noteOffset(); + f_cnt_t frames_processed = arpMode != ArpMode::Free ? cnphv.first()->noteOffset() : _n->noteOffset(); while( frames_processed < Engine::audioEngine()->framesPerPeriod() ) { @@ -395,17 +414,6 @@ void InstrumentFunctionArpeggio::processNote( NotePlayHandle * _n ) frames_processed += remaining_frames_for_cur_arp; - // in sorted mode: is it our turn or do we have to be quiet for - // now? - if( static_cast(m_arpModeModel.value()) == ArpMode::Sort && - ( ( cur_frame / arp_frames ) % total_range ) / range != (f_cnt_t) _n->index() ) - { - // update counters - frames_processed += arp_frames; - cur_frame += arp_frames; - continue; - } - // Skip notes randomly if( m_arpSkipModel.value() ) { @@ -435,7 +443,7 @@ void InstrumentFunctionArpeggio::processNote( NotePlayHandle * _n ) // process according to arpeggio-direction... if (dir == ArpDirection::Up || dir == ArpDirection::Down) { - cur_arp_idx = ( cur_frame / arp_frames ) % range; + cur_arp_idx = (cur_frame / arp_frames) % range; } else if ((dir == ArpDirection::UpAndDown || dir == ArpDirection::DownAndUp) && range > 1) { @@ -454,7 +462,7 @@ void InstrumentFunctionArpeggio::processNote( NotePlayHandle * _n ) else if( dir == ArpDirection::Random ) { // just pick a random chord-index - cur_arp_idx = (int)( range * ( (float) rand() / (float) RAND_MAX ) ); + cur_arp_idx = static_cast(range * static_cast(rand()) / static_cast(RAND_MAX)); } // Divide cur_arp_idx with wanted repeats. The repeat feature will not affect random notes. @@ -464,7 +472,7 @@ void InstrumentFunctionArpeggio::processNote( NotePlayHandle * _n ) if( m_arpCycleModel.value() && dir != ArpDirection::Random ) { cur_arp_idx *= m_arpCycleModel.value() + 1; - cur_arp_idx %= static_cast( range / m_arpRepeatsModel.value() ); + cur_arp_idx %= static_cast(range / m_arpRepeatsModel.value()); } // If ArpDirection::Down or ArpDirection::DownAndUp, invert the final range. @@ -474,8 +482,23 @@ void InstrumentFunctionArpeggio::processNote( NotePlayHandle * _n ) } // now calculate final key for our arp-note - const int sub_note_key = base_note_key + (cur_arp_idx / cur_chord_size ) * + int sub_note_key = 0; + if (arpMode != ArpMode::Sort) + { + sub_note_key = base_note_key + (cur_arp_idx / cur_chord_size) * KeysPerOctave + chord_table.chords()[selected_arp][cur_arp_idx % cur_chord_size]; + } + else + { + const auto octaveDiv = std::div(cur_arp_idx, total_chord_size); + const int octave = octaveDiv.quot; + const auto arpDiv = std::div(octaveDiv.rem, cnphv.size()); + const int arpIndex = arpDiv.rem; + const int chordIndex = arpDiv.quot; + sub_note_key = cnphv[arpIndex]->key() + + chord_table.chords()[selected_arp][chordIndex] + + octave * KeysPerOctave; + } // range-checking if( sub_note_key >= NumKeys ||