diff --git a/TODO b/TODO index 63fbf7d7..ae70aefd 100644 --- a/TODO +++ b/TODO @@ -1,11 +1,29 @@ TO DO for Seq66 0.99.17 Chris Ahlstrom -2019-04-13 to 2024-12-04 +2019-04-13 to 2024-12-08 Hottest: - Figure out issue #132, the libasound2 issue. + - #133 Pattern Length Change Issues - QObject::setParent: Cannot set parent, new parent is in a different thread + - Pattern fix: + - Using measures = 3/4 changes the time signature in the pattern + editor, but not in the time pane. Also, although END + is at the third beat, both time and roll leave the measure + a 4/4. + - Add usage/processing of Quantization parameters in + line_edit_q_full/jitter/none/tighted. + - Make sure changes raise the modified flag and asterisk indicator. + - Enable / disable Set / Reset according to modification. + - Flagging issues: + - Large measure does not check-mark Expand. + - The Other Fixes do not check-mark their effects flags. + - Definitely need a new screen-shot and documentation galore. + - Consider an option to Import a normal MIDI file so that a flag + is raised so that it will not be saved with Seq66 SeqSpecs. + Make it visible to be clear. + - Add .notemap support, identical to .drums. From Testing: @@ -201,7 +219,7 @@ ISSUES: #103 One-shot (repetitions != 0) patterns do not play This feature works, but requires the pattern to be armed before playback begins, either in Live or Song mode. Should we make the loop - auto-arm, or make this an option somehow? No. + auto-arm, or make this an option somehow? No. STATUS: Closed. #104 Really confusing usage @@ -239,7 +257,7 @@ ISSUES: machine. A ton of refactoring and fixes for the damn MIDI Mapper. #111 Time signature changes does not get saved on .midi file - + STATUS: In progress. - Ctrl-Z in pattern editor works, but does not restore original view until pattern is reopened. Not sure why, it should work. @@ -344,6 +362,32 @@ ISSUES: STATUS: Fixed in the "wip" branch, will be merged at the next release. +#133 Pattern Length Change Issues + + In the pattern edit window, the reset pattern to 1 button no longer works + properly. It either shrinks to some bar other than 1 or just does nothing + at all. + + There is also a behaviour when you manually select or type the number of + bars and it is smaller than the amount of already existing bars with notes + in, the notes get deleted after a warning dialog. This does not work when + the Expand mode is selected. + + Steps to reproduce: + + 1: Create two (or more) bars with notes in, with Expand mode selected. + 2: Shrink to one bar + + These have appeared since https://github.com/ahlstromcj/seq66/issues/128 so + possibly connected. + + A third issue which occurred before was with manually editing the amount + of bars with double digits. The bars refresh for each digit entered. For + example So if you have 16 bars and you want to shrink to 14 bars, it will + first shrink it to 1 bar as you type over. If the bars are full then the + dialog will pop up about losing notes. If you then click ok, instead of + cancel, you will lose anything in the latter 15 bars. + To close as pushed off to version 2: 1 JACK Metadata MIDINAM support diff --git a/contrib/DIR_COLORS b/contrib/DIR_COLORS index 5ae0ef90..a17ab452 100644 --- a/contrib/DIR_COLORS +++ b/contrib/DIR_COLORS @@ -5,7 +5,7 @@ # \file DIR_COLORS # \author Chris Ahlstrom # \date 2008-02-26 -# \updates 2024-10-30 +# \updates 2024-12-07 # \version $Revision$ # \license $XPC_SUITE_GPL_LICENSE$ # @@ -417,6 +417,7 @@ EXEC 01;32 .ini 01;33 .keymap 01;33 .mutes 01;33 +.notemap 01;33 .opts 01;33 .options 01;33 .pc 01;33 diff --git a/doc/latex/tex/pattern_editor.tex b/doc/latex/tex/pattern_editor.tex index e8f04787..e54f6e4b 100644 --- a/doc/latex/tex/pattern_editor.tex +++ b/doc/latex/tex/pattern_editor.tex @@ -369,14 +369,16 @@ \subsection{Pattern Editor / Second Row} Alignment, reversing, and quantization can still be done. \item \textbf{Measures}. Allows the measures (length) of the pattern to be changed in two ways, - depending on how the number is entered: as a simple integer (e.g. - 1, 1.0, 0.25), - or as a simple time-signature fraction (e.g. "3/4"). + depending on how the number is entered: as a simple integer or + floating-point number (e.g. + 1, 1.0, 1.75), + or as a simple time-signature fraction (e.g. "3/4"; the "/" is a + key indicator of this kind of change). \begin{itemize} \item \textbf{Measures}. Entering an integer or a floating-point scale factor will scale the pattern events, - and change the number of measures, if applicable. + and change the number of measures. If the measure value is less than \texttt{1.0}, the pattern will only be compressed. If less than 1 measure in length, the result will be @@ -385,6 +387,7 @@ \subsection{Pattern Editor / Second Row} Entering a fraction such as \textbf{3/4} or \textbf{12/8} will change the time signature of the pattern \textbf{and} \textsl{compress it to 1 measure}. + This obviously works best for shorter patterns. \end{itemize} \item \textbf{Scale Factor}. Scales the pattern. For example, 2.0 doubles the length, and 0.5 diff --git a/libseq66/include/midi/calculations.hpp b/libseq66/include/midi/calculations.hpp index 8908c8f1..645819d0 100644 --- a/libseq66/include/midi/calculations.hpp +++ b/libseq66/include/midi/calculations.hpp @@ -28,7 +28,7 @@ * \library seq66 application * \author Chris Ahlstrom * \date 2015-11-07 - * \updates 2024-11-23 + * \updates 2024-12-07 * \license GNU GPLv2 or above * * These items were moved from the globals.h module so that only the modules @@ -131,13 +131,14 @@ lengthfix_cast (int v) enum class alteration { - none = 0, /**< grid_quant_none: Not adjusting timing of pattern. */ - tighten, /**< grid_quant_tighten: Adjust timing less forcefully. */ - quantize, /**< grid_quant_full: Adjust timing strictly. */ - jitter, /**< grid_quant_jitter: Randomize timing slightly. */ - random, /**< grid_quant_random: Randomize event magnitude a bit. */ - notemap, /**< grid_quant_notemap: Apply a configured note-mapping. */ - max /**< Illegal value. */ + none = 0, /**< grid_quant_none: Not adjustment of pattern. */ + tighten, /**< grid_quant_tighten: Adjust timing less halfway. */ + quantize, /**< grid_quant_full: Adjust timing strictly. */ + jitter, /**< grid_quant_jitter: Randomize timing slightly. */ + random, /**< grid_quant_random: Randomize event magnitude. */ + notemap, /**< grid_quant_notemap: Apply configured note-mapping. */ + rev_notemap, /**< Apply the note-map in the reverser direction. */ + max /**< Illegal value. */ }; inline int @@ -643,6 +644,10 @@ extern std::string extract_a2j_port_name (const std::string & alias); extern midipulse closest_snap (int S, midipulse p); extern midipulse down_snap (int S, midipulse p); extern midipulse up_snap (int S, midipulse p); +extern bool fequal (double x, double y); +extern bool fnotequal (double x, double y); +extern bool flessthan (double x, double y); +extern bool fgreaterthan (double x, double y); } // namespace seq66 diff --git a/libseq66/include/midi/eventlist.hpp b/libseq66/include/midi/eventlist.hpp index 5dad6143..8bee04e2 100644 --- a/libseq66/include/midi/eventlist.hpp +++ b/libseq66/include/midi/eventlist.hpp @@ -28,7 +28,7 @@ * \library seq66 application * \author Chris Ahlstrom * \date 2015-09-19 - * \updates 2024-12-04 + * \updates 2024-12-08 * \license GNU GPLv2 or above * * This module extracts the event-list functionality from the sequencer @@ -426,8 +426,8 @@ class eventlist midibyte status, midibyte cc, int snap, int divide ); - bool quantize_all_events (int snap, int divide); - bool quantize_notes (int snap, int divide); + bool quantize_all_events (int snap, int divide = 1); + bool quantize_notes (int snap, int divide = 1, bool all = false); midipulse adjust_timestamp (event & er, midipulse deltatick); void scale_note_off (event & noteoff, double factor); midipulse apply_time_factor @@ -440,10 +440,10 @@ class eventlist bool move_selected_notes (midipulse delta_tick, int delta_note); bool move_selected_events (midipulse delta_tick); bool align_left (bool relink = false); - bool randomize_selected (midibyte status, int plus_minus); - bool randomize_selected_notes (int range); - bool jitter_events (int snap, int jitr); - bool jitter_notes (int snap, int jitr); + bool randomize (midibyte status, int plus_minus, bool all = false); + bool randomize_notes (int range, bool all = false); + bool jitter_all_events (int snap, int jitr); + bool jitter_notes (int snap, int jitr, bool all = false); void link_new (bool wrap = false); bool link_notes (event::iterator eon, event::iterator eoff); #if defined SEQ66_LINK_NEWEST_NOTE_ON_RECORD diff --git a/libseq66/include/play/performer.hpp b/libseq66/include/play/performer.hpp index 55d56e05..09bdf7d1 100644 --- a/libseq66/include/play/performer.hpp +++ b/libseq66/include/play/performer.hpp @@ -28,7 +28,7 @@ * \library seq66 application * \author Chris Ahlstrom * \date 2018-11-12 - * \updates 2024-11-21 + * \updates 2024-12-05 * \license GNU GPLv2 or above * * The main player! Coordinates sets, patterns, mutes, playlists, you name @@ -2257,7 +2257,7 @@ class performer bool merge_sequence (seq::number seqno); bool move_sequence (seq::number seqno); bool finish_move (seq::number seqno); - bool fix_sequence (seq::number seqno, fixparameters & params); + bool fix_pattern (seq::number seqno, fixparameters & params); bool remove_set (screenset::number setno); bool clear_set (screenset::number setno); bool swap_sets (seq::number set0, seq::number set1); diff --git a/libseq66/include/play/sequence.hpp b/libseq66/include/play/sequence.hpp index 64b0bdcb..aae8241d 100644 --- a/libseq66/include/play/sequence.hpp +++ b/libseq66/include/play/sequence.hpp @@ -28,7 +28,7 @@ * \library seq66 application * \author Chris Ahlstrom * \date 2015-07-30 - * \updates 2024-11-24 + * \updates 2024-12-08 * \license GNU GPLv2 or above * * The functions add_list_var() and add_long_list() have been replaced by @@ -87,8 +87,9 @@ using colorbyte = char; * of those cases, the timestamps of all events will be adjusted * accordingly. * - * \var fp_quan_type - * Indicates if all events are to be tighted or quantized. + * \var fp_alter_type + * Indicates how all events are to be altered, such as being tightened, + * quantized, note-mapping, etc. * * \var fp_align_left * Indicates if the offset of the first event or, preferably first note @@ -139,8 +140,11 @@ using colorbyte = char; struct fixparameters { lengthfix fp_fix_type; - alteration fp_quan_type; - int fp_jitter; + alteration fp_alter_type; + int fp_tighten_range; + int fp_quantize_range; + int fp_random_range; + int fp_jitter_range; bool fp_align_left; bool fp_reverse; bool fp_reverse_in_place; @@ -1833,9 +1837,9 @@ class sequence void decrement_selected (midibyte status, midibyte /*control*/); bool grow_selected (midipulse deltatick); bool stretch_selected (midipulse deltatick); - bool randomize_selected (midibyte status, int range = -1); - bool randomize_selected_notes (int range = -1); - bool jitter_notes (int jitter = -1); + bool randomize (midibyte status, int range = -1, bool all = false); + bool randomize_notes (int range = -1, bool all = false); + bool jitter_notes (int jitter = -1, bool all = false); bool mark_selected (); void unpaint_all (); void verify_and_link (bool wrap = false); @@ -2022,8 +2026,8 @@ class sequence return m_parent; } - bool quantize_events (midibyte status, midibyte cc, int divide); - bool quantize_notes (int divide); + bool quantize_events (midibyte status, midibyte cc, int divide = 1); + bool quantize_notes (int divide = 1); bool change_ppqn (int p); void put_event_on_bus (const event & ev); void reset_loop (); diff --git a/libseq66/src/midi/calculations.cpp b/libseq66/src/midi/calculations.cpp index 9c57f7bc..6d2953b7 100644 --- a/libseq66/src/midi/calculations.cpp +++ b/libseq66/src/midi/calculations.cpp @@ -25,7 +25,7 @@ * \library seq66 application * \author Chris Ahlstrom * \date 2015-11-07 - * \updates 2024-11-23 + * \updates 2024-12-05 * \license GNU GPLv2 or above * * This code was moved from the globals module so that other modules @@ -1748,6 +1748,72 @@ up_snap (int S, midipulse p) return result; } +/** + * Comparison of floating-point values. We can tolerate a fairly large + * epsilon value in our MIDI code. + * + * See https://realtimecollisiondetection.net/blog/?p=89 for some gory + * details. + * + * if (Abs(x – y) <= EPSILON * Max(1.0f, Abs(x), Abs(y)) + */ + +static double s_epsilon = 0.0001; + +static double +one_max (double a, double b) +{ + double result = 1.0f; + if (std::fabs(a) > result) + result = std::fabs(a); + + if (std::fabs(b) > result) + result = std::fabs(b); + + return result; +} + +/** + * Calculates x == y to within the epsilon, and returns true if that is so. + */ + +bool +fequal (double x, double y) +{ + return std::fabs(x - y) <= s_epsilon * one_max(std::fabs(x), std::fabs(y)); +} + +/** + * Calculates x != y to within the epsilon, and returns true if that is so. + */ + +bool +fnotequal (double x, double y) +{ + return std::fabs(x - y) > s_epsilon * one_max(std::fabs(x), std::fabs(y)); +} + +/** + * Calculates x < y and returns true if that is so. + */ + +bool +flessthan (double x, double y) +{ + return x < (y - s_epsilon * one_max(std::fabs(x), std::fabs(y))); +} + +/** + * Calculates x > y and returns true if that is so. + */ + +bool +fgreaterthan (double x, double y) +{ + return x > (y + s_epsilon * one_max(std::fabs(x), std::fabs(y))); +} + + } // namespace seq66 /* diff --git a/libseq66/src/midi/eventlist.cpp b/libseq66/src/midi/eventlist.cpp index efed3ba1..7439e867 100644 --- a/libseq66/src/midi/eventlist.cpp +++ b/libseq66/src/midi/eventlist.cpp @@ -25,7 +25,7 @@ * \library seq66 application * \author Chris Ahlstrom * \date 2015-09-19 - * \updates 2024-12-04 + * \updates 2024-12-08 * \license GNU GPLv2 or above * * This container now can indicate if certain Meta events (time-signaure or @@ -841,7 +841,7 @@ eventlist::quantize_events if (er.is_marked()) /* ignore marked events */ { er.unmark(); - continue; + continue; /* it was a linked note */ } if (astatus == EVENT_CONTROL_CHANGE) canselect = match && d0 == cc; /* correct status and cc */ @@ -877,7 +877,7 @@ eventlist::quantize_events f->set_timestamp(ts1); } } - f->mark(); /* mark linked note for later */ + f->mark(); /* mark linked note, ignore */ } } } @@ -892,14 +892,15 @@ eventlist::quantize_events * Quantizes all events, unconditionally. No adjustment for wrapped notes * is made. * - * \param snap_tick + * \param snap * Provides the maximum amount to move the events. Actually, events are * moved to the previous or next snap_tick value depend on whether they * are halfway to the next one or not. * * \param divide * An indicator of the amount of quantization. The values are either - * 1 ("quantize") or 2 ("tighten"). + * 1 ("quantize") or 2 ("tighten"). The default value is 1, which makes + * the snap parameter the quantization value. */ bool @@ -909,9 +910,8 @@ eventlist::quantize_all_events (int snap, int divide) midipulse len = get_length(); bool tight = divide == 2; for (auto & er : m_events) - { result = tight ? er.tighten(snap, len) : er.quantize(snap, len) ; - } + if (result) verify_and_link(); /* sorts them again!!! */ @@ -920,17 +920,28 @@ eventlist::quantize_all_events (int snap, int divide) /** * Quantize/tighten all Note events, including Aftertouch. + * + * \param snap + * The value to which to snap the events. + * + * \param divide + * Divides the snap, e.g. in order to "tighten" rather than fully + * quantize. Use set to 1 (the default) or 2. + * + * \param all + * If false (the default), then only selected notes are acted on. + * Otherwise, they all are. */ bool -eventlist::quantize_notes (int snap, int divide) +eventlist::quantize_notes (int snap, int divide, bool all) { bool result = false; midipulse len = get_length(); bool tight = divide == 2; for (auto & er : m_events) { - if (er.is_selected_note()) + if (all || er.is_selected_note()) { if (er.is_marked()) /* ignore marked events */ { @@ -1316,14 +1327,14 @@ eventlist::reverse_events (bool inplace, bool relink) */ bool -eventlist::randomize_selected (midibyte astatus, int range) +eventlist::randomize (midibyte astatus, int range, bool all) { bool result = false; if (range > 0) { for (auto & e : m_events) { - if (e.is_selected_status(astatus)) + if (all || e.is_selected_status(astatus)) { if (e.randomize(range)) result = true; @@ -1335,26 +1346,30 @@ eventlist::randomize_selected (midibyte astatus, int range) /** * This function randomizes a Note On or Note Off message, and more - * thoroughly than randomize_selected(). We want to be able to "jitter" the + * thoroughly than randomize(). We want to be able to "jitter" the * velocity (data byte d[1]) of the note. The note pitch (d[0]) is not * altered. * * \param range * Provides the amount of velocity randomization. * + * \param all + * If true (the default is false), handle all notes, not just the + * selected notes. + * * \return * Returns true if any event got altered. */ bool -eventlist::randomize_selected_notes (int range) +eventlist::randomize_notes (int range, bool all) { bool result = false; if (range > 0) { for (auto & e : m_events) { - if (e.is_selected_note()) /* randomizable event? */ + if (all || e.is_selected_note()) /* randomizable event? */ { if (! e.is_note_off_recorded()) /* don't ruin fake Off */ { @@ -1364,7 +1379,7 @@ eventlist::randomize_selected_notes (int range) } } if (result) - verify_and_link(); /* sort and relink notes */ + verify_and_link(); /* sort & relink notes */ } return result; } @@ -1388,7 +1403,7 @@ eventlist::randomize_selected_notes (int range) */ bool -eventlist::jitter_events (int snap, int jitr) +eventlist::jitter_all_events (int snap, int jitr) { bool result = false; if (jitr > 0) @@ -1399,7 +1414,7 @@ eventlist::jitter_events (int snap, int jitr) if (e.is_marked()) /* ignore marked events */ { e.unmark(); - continue; + continue; /* it was a linked note */ } if (e.jitter(snap, jitr, get_length())) { @@ -1413,6 +1428,8 @@ eventlist::jitter_events (int snap, int jitr) * In some cases, the linked Note Off, when quantized, * will end up next to the Note On. How to fix? If they * are closer than half the snap, add the snap. + * + * Hmmmm, how about the zero-length correction??? */ event::iterator f = e.link(); @@ -1452,19 +1469,23 @@ eventlist::jitter_events (int snap, int jitr) * \param jitr * Provides the amount of time jitter in ticks. * + * \param all + * If true (the default is false), all events are jittered, + * not just the selected events. + * * \return * Returns true if some jittering was actually done. */ bool -eventlist::jitter_notes (int snap, int jitr) +eventlist::jitter_notes (int snap, int jitr, bool all) { bool result = false; if (jitr > 0) { for (auto & e : m_events) { - if (e.is_selected_note()) /* ca 2023-08-20 */ + if (all || e.is_selected_note()) { if (e.jitter(snap, jitr, get_length())) result = true; diff --git a/libseq66/src/play/performer.cpp b/libseq66/src/play/performer.cpp index 905f5267..efc12f1d 100644 --- a/libseq66/src/play/performer.cpp +++ b/libseq66/src/play/performer.cpp @@ -24,7 +24,7 @@ * \library seq66 application * \author Chris Ahlstrom and others * \date 2018-11-12 - * \updates 2024-11-13 + * \updates 2024-12-05 * \license GNU GPLv2 or above * * Also read the comments in the Seq64 version of this module, perform. @@ -2300,7 +2300,7 @@ performer::finish_move (seq::number seqno) } bool -performer::fix_sequence (seq::number seqno, fixparameters & params) +performer::fix_pattern (seq::number seqno, fixparameters & params) { bool result = false; seq::pointer s = get_sequence(seqno); diff --git a/libseq66/src/play/sequence.cpp b/libseq66/src/play/sequence.cpp index f23b2265..d39f464f 100644 --- a/libseq66/src/play/sequence.cpp +++ b/libseq66/src/play/sequence.cpp @@ -25,7 +25,7 @@ * \library seq66 application * \author Chris Ahlstrom * \date 2015-07-24 - * \updates 2024-12-04 + * \updates 2024-12-08 * \license GNU GPLv2 or above * * The functionality of this class also includes handling some of the @@ -2627,14 +2627,14 @@ sequence::grow_selected (midipulse delta) */ bool -sequence::randomize_selected (midibyte status, int range) +sequence::randomize (midibyte status, int range, bool all) { automutex locker(m_mutex); m_events_undo.push(m_events); /* push_undo(), no lock */ if (range == (-1)) range = usr().randomization_amount(); - bool result = m_events.randomize_selected(status, range); + bool result = m_events.randomize(status, range, all); if (result) modify(); @@ -2642,14 +2642,14 @@ sequence::randomize_selected (midibyte status, int range) } bool -sequence::randomize_selected_notes (int range) +sequence::randomize_notes (int range, bool all) { automutex locker(m_mutex); m_events_undo.push(m_events); /* push_undo(), no lock */ if (range == (-1)) range = usr().randomization_amount(); - bool result = m_events.randomize_selected_notes(range); + bool result = m_events.randomize_notes(range, all); if (result) modify(); @@ -2658,13 +2658,25 @@ sequence::randomize_selected_notes (int range) /** * For usage by fix_pattern() and by Tools / Timing / Jitter. + * Note the snap() parameter, to avoid gross jittering. + * + * \param jitr + * Provides the maximum range of the jittering in MIDI tick units. + * + * \param all + * If true (the default is false), all events are jittered, + * not just the selected events. The value "true" is to be + * used by fix_pattern(). + * + * \return + * Returns true if events got jittered. */ bool -sequence::jitter_notes (int jitr) +sequence::jitter_notes (int jitr, bool all) { automutex locker(m_mutex); - bool result = m_events.jitter_notes(snap(), jitr); + bool result = m_events.jitter_notes(snap(), jitr, all); if (result) modify(); @@ -3375,6 +3387,9 @@ sequence::trunc_measures (double measures) * * See the documentation for the fixparameters structure in the sequence.hpp * module. + * + * \return + * Returns true if the fixup succeeded. */ bool @@ -3395,16 +3410,15 @@ sequence::fix_pattern (fixparameters & params) push_undo(); if (params.fp_align_left) { - params.fp_align_left = m_events.align_left(); /* realigned? */ + params.fp_align_left = m_events.align_left(); /* realigned? */ if (params.fp_align_left) tempefx = bit_set(tempefx, fixeffect::shifted); else - result = false; /* op failed */ + result = false; /* op failed */ } if (params.fp_reverse || params.fp_reverse_in_place) - { result = m_events.reverse_events(params.fp_reverse_in_place); - } + if (result) { bool fixmeasures = params.fp_fix_type == lengthfix::measures; @@ -3412,7 +3426,15 @@ sequence::fix_pattern (fixparameters & params) bool timesig = params.fp_use_time_signature; if (fixmeasures) { - if (newmeasures != double(currentbars)) + if (timesig) + { + result = apply_length + ( + params.fp_beats_per_bar, int(get_ppqn()), + params.fp_beat_width, int(newmeasures) + ); + } + else if (newmeasures != double(currentbars)) { newscalefactor = newmeasures / double(currentbars); newmeasures = trunc_measures(newmeasures); @@ -3421,6 +3443,7 @@ sequence::fix_pattern (fixparameters & params) newscalefactor, params.fp_save_note_length ); } + result = false; /* op not done */ } else if (fixscale) { @@ -3429,14 +3452,6 @@ sequence::fix_pattern (fixparameters & params) newscalefactor, params.fp_save_note_length ); } - if (timesig) - { - result = apply_length - ( - params.fp_beats_per_bar, int(get_ppqn()), - params.fp_beat_width, int(newmeasures) - ); - } if (newlength > 0 && newlength != currentlen) { int measures = get_measures(newlength); @@ -3451,37 +3466,40 @@ sequence::fix_pattern (fixparameters & params) else if (newscalefactor < 1.00) tempefx = bit_set(tempefx, fixeffect::shrunk); } + switch (params.fp_alter_type) + { + case alteration::tighten: - /* - * These functions operate only on selected events. - */ + result = m_events.quantize_all_events(params.fp_tighten_range); + break; - if (params.fp_quan_type == alteration::tighten) - { - result = m_events.quantize_all_events(snap(), 2); - } - else if (params.fp_quan_type == alteration::quantize) - { - result = m_events.quantize_all_events(snap(), 1); - } - if (params.fp_quan_type == alteration::jitter) - { - result = m_events.jitter_notes(snap(), params.fp_jitter); - } -#if defined SEQ66_USE_ADDED_ALTERATIONS - if (params.fp_quan_type == alteration::random) - { - m_events.select_all(); // TODO - result = m_events_randomize_selected_notes - ( - params.fp_jitter, params.fp_jitter // FIXME - ); - } - if (params.fp_quan_type == alteration::notemap) - { - result = m_events.jitter_notes(params.fp_jitter); // TODO + case alteration::quantize: + + result = m_events.quantize_all_events(params.fp_quantize_range); + break; + + case alteration::jitter: + + result = jitter_notes(params.fp_jitter_range, true); + break; + + case alteration::random: + + result = randomize_notes (params.fp_random_range, true); + break; + + case alteration::notemap: + + // WRONG: + // Need to get the notemapper and call + // repitch(). + result = false; // TODO + break; + + default: + + break; } -#endif if (result) { params.fp_scale_factor = newscalefactor; @@ -5964,7 +5982,7 @@ sequence::set_length (midipulse len, bool adjust_triggers, bool verify) } /** - * Sets the sequence length based on the three given parameters. There's an + * Sets the sequence length based on the first four parameters. There's an * implicit "adjust-triggers = true" parameter used in this function. Please * note that there is an overload that takes only a measure number and uses * the current beats/bar, PPQN, and beat-width values of this sequence. The @@ -7412,7 +7430,7 @@ sequence::handle_edit_action (eventlist::edit action, int var) case eventlist::edit::randomize_events: - (void) randomize_selected(m_status, var); + (void) randomize(m_status, var); break; case eventlist::edit::quantize_notes: diff --git a/seq_qt5/forms/qpatternfix.ui b/seq_qt5/forms/qpatternfix.ui index 559983b8..77198c0e 100644 --- a/seq_qt5/forms/qpatternfix.ui +++ b/seq_qt5/forms/qpatternfix.ui @@ -6,8 +6,8 @@ 0 0 - 500 - 316 + 713 + 420 @@ -24,7 +24,7 @@ 12 8 - 473 + 671 38 @@ -34,7 +34,6 @@ 11 - 75 true @@ -46,7 +45,7 @@ - Qt::Horizontal + Qt::Orientation::Horizontal @@ -73,7 +72,7 @@ Pattern No. - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter @@ -84,7 +83,7 @@ - 48 + 56 0 @@ -100,10 +99,10 @@ - QFrame::Box + QFrame::Shape::Box - QFrame::Sunken + QFrame::Shadow::Sunken 2 @@ -120,7 +119,7 @@ - 64 + 108 0 @@ -136,10 +135,10 @@ - Pulses + Ticks (pulses) - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter @@ -147,7 +146,7 @@ - 64 + 96 0 @@ -163,10 +162,10 @@ - QFrame::Box + QFrame::Shape::Box - QFrame::Sunken + QFrame::Shadow::Sunken 2 @@ -183,13 +182,12 @@ 8 56 - 245 - 121 + 221 + 151 - 75 true @@ -204,60 +202,72 @@ 12 24 - 189 - 89 + 201 + 111 22 - - + + - 50 false - - &None + + If an integer, the number of measures is +changed; events are compressed or expanded +as needed. + +If a fraction (e.g. 3/4), the pattern is reduced to +one measure with the given time signature. - - - - - - false + + &Measures - - + + - 50 false + + Shrink/expand the pattern by a factor, +accounting for the time of the last event. + - &Measures + Scale &Factor - - - - - + + - 50 false + + No change in pattern length. + - Scale &Factor + &None + + + + + + + + + + false @@ -273,21 +283,20 @@ - 348 + 530 56 - 141 - 181 + 161 + 291 - 75 true true - Effect + Applied Effects false @@ -300,11 +309,24 @@ 8 24 - 125 - 141 + 141 + 251 + + + + + true + false + + + + Alteration + + + @@ -312,11 +334,13 @@ - 50 true false + + Events will be shifted. + Shift @@ -326,10 +350,13 @@ - 50 + true false + + Events will be reversed. + Reverse @@ -342,11 +369,13 @@ - 50 true false + + Events will be compressed. + Shrin&k @@ -359,11 +388,13 @@ - 50 true false + + Events will expand. + E&xpand @@ -373,11 +404,13 @@ - 50 true false + + The time signature will change. + Time Sig @@ -385,8 +418,12 @@ + + Notes/events will be dropped by this +change. + - Truncated! + Truncate! @@ -397,14 +434,13 @@ 8 - 180 - 205 - 125 + 230 + 221 + 171 - 75 true @@ -415,9 +451,9 @@ 12 - 20 - 185 - 97 + 24 + 201 + 132 @@ -425,10 +461,12 @@ - 50 false + + Move events left to remove delays. + &Align left @@ -438,10 +476,13 @@ - 50 false + + Reverse the order of all events, flipping +the measures. + Reverse measures @@ -451,10 +492,12 @@ - 50 false + + Reverse notes, preserving relative location. + Reverse in place @@ -464,10 +507,12 @@ - 50 false + + When scaling, don't change note length. + &Preserve note length @@ -479,9 +524,9 @@ - 224 - 256 - 253 + 260 + 360 + 401 38 @@ -494,6 +539,9 @@ 28 + + Apply the settings. + &Set @@ -507,6 +555,9 @@ 28 + + Go back to the original pattern. + &Reset @@ -520,6 +571,9 @@ 0 + + Close this dialog. + &Close @@ -530,106 +584,185 @@ - 204 - 57 - 139 - 149 + 230 + 56 + 291 + 291 - 75 true - Quantization + Alteration 17 24 - 121 - 121 + 266 + 246 - - + + + + true + + + Plus/minus range of time jitter in ticks. + + + + + + + false + + + + + + + true + + + Plus/minus range of magnitude change in MIDI units. + + + + + + + true + + + If not blank, replaces the snap/2 value. + + + + + - 50 false + + Do partial quantization. + - None + &Tight Q ticks - - + + + + + false + + + + Randomize event magnitude or note velocity. + + + Random amplitude + + + + + - false + true + + + If not blank, replaces the snap value. - - + + - 50 false + + No alteration operations will be done. + - &Tighten + None - - - - false + + + + + false + + + + Remap notes as per the loaded 'drum' file. + + + Note-map - + - 50 false + + Fully quantization to the snap value. + - F&ull + F&ull Q ticks - - - - false + + + + + false + + + + Jitter the timestamps of the events. + + + Time/tick jitter - - + + - 50 false - Jitter + Rev Note-map - - + + + + ... + + @@ -639,12 +772,9 @@ btn_change_none line_edit_none btn_change_pick - line_edit_pick + line_edit_measures btn_change_scale line_edit_scale - btn_quan_none - btn_quan_tighten - btn_quan_full btn_align_left btn_set btn_reset diff --git a/seq_qt5/include/qpatternfix.hpp b/seq_qt5/include/qpatternfix.hpp index 7d8c5512..4b31e28b 100644 --- a/seq_qt5/include/qpatternfix.hpp +++ b/seq_qt5/include/qpatternfix.hpp @@ -27,7 +27,7 @@ * \library seq66 application * \author Chris Ahlstrom * \date 2022-04-09 - * \updates 2023-08-16 + * \updates 2024-12-08 * \license GNU GPLv2 or above * * Provides a way to modulate MIDI controller events. @@ -125,12 +125,18 @@ private slots: * buttonClicked() signals. */ - void slot_effect (); + void slot_effect_clear (); void slot_length_fix (int fixlengthid); void slot_measure_change (); void slot_scale_change (); - void slot_quan_change (int quanid); + void slot_alt_change (int altid); + + void slot_tighten_change (); + void slot_full_change (); void slot_jitter_change (); + void slot_random_change (); + void slot_notemap_file (); + void slot_align_change (int dummy); void slot_reverse_change (int dummy); void slot_reverse_in_place (int dummy); @@ -157,7 +163,7 @@ private slots: * Access to radio-buttons for alteration. */ - QButtonGroup * m_quan_group; + QButtonGroup * m_alt_group; /** * Access to the performance controller. @@ -212,7 +218,27 @@ private slots: * The current way the user has selected for alteration. */ - alteration m_quan_type; + alteration m_alt_type; + + /** + * The range of tightening to apply. Normally this is snap() / 2. + */ + + int m_tighten_range; + + /** + * The range of full quantization to apply. Normally this is snap(). + */ + + int m_full_range; + + /** + * The range of amplitude randomization to apply. For control events, + * this is a magnitude of the control. For notes, it is the velocity. + * Program change need not apply. :-) + */ + + int m_random_range; /** * The range of jitter to apply. Here, jitter is a randomization of @@ -222,6 +248,19 @@ private slots: int m_jitter_range; + /** + * Indicates if the note-map is to be reversed. + */ + + bool m_reverse_notemap; + + /** + * Holds the file-name of the notemap file, which is a '.drums' file, + * but we also want to support '.notemap'. + */ + + std::string m_notemap_file; + /** * The current number of measures for the adjustment. This is a double * so that it can be fractional, such as "3/4" --> 0.75. But otherwise, diff --git a/seq_qt5/src/qpatternfix.cpp b/seq_qt5/src/qpatternfix.cpp index 4ad330ac..af7abd0c 100644 --- a/seq_qt5/src/qpatternfix.cpp +++ b/seq_qt5/src/qpatternfix.cpp @@ -24,7 +24,7 @@ * \library seq66 application * \author Chris Ahlstrom * \date 2022-04-09 - * \updates 2022-08-19 + * \updates 2024-12-08 * \license GNU GPLv2 or above * * This dialog provides a way to combine the following pattern adjustments: @@ -38,7 +38,6 @@ * similar to the LFO dialog. * * This dialog was inspired by Ahlstrom's poor playing and timing skills. - * */ #include @@ -88,26 +87,31 @@ qpatternfix::qpatternfix QFrame (parent), ui (new Ui::qpatternfix), m_fixlength_group (nullptr), - m_quan_group (nullptr), + m_alt_group (nullptr), m_performer (p), - m_seq (s), - m_backup_events (s.events()), /* for slot_reset() */ + m_seq (s), /* track() accessor */ + m_backup_events (s.events()), /* for slot_reset() */ m_backup_measures (s.get_measures()), m_backup_beats (s.get_beats_per_bar()), m_backup_width (s.get_beat_width()), m_edit_frame (editparent), - m_length_type (lengthfix::none), - m_quan_type (alteration::none), - m_jitter_range (usr().jitter_range(s.get_ppqn() / 4)), - m_measures (double(m_backup_measures)), - m_scale_factor (1.0), - m_align_left (false), - m_reverse (false), - m_reverse_in_place (false), - m_save_note_length (true), - m_use_time_sig (false), - m_time_sig_beats (0), - m_time_sig_width (0), + m_length_type (lengthfix::none), /* lengthfix fp_fixtype */ + m_alt_type (alteration::none), /* alteration fp_... */ + m_tighten_range (s.snap() / 2), + m_full_range (s.snap()), + m_random_range (usr().randomization_amount()), + m_jitter_range (usr().jitter_range(s.get_ppqn() / 4)), /* fp_... */ + m_reverse_notemap (false), + m_notemap_file (rc().notemap_filename()), + m_measures (double(m_backup_measures)), /* fp_measures */ + m_scale_factor (1.0), /* fp_scale_factor */ + m_align_left (false), /* bool fp_align_left */ + m_reverse (false), /* bool fp_reverse */ + m_reverse_in_place (false), /* bool fp_reverse_... */ + m_save_note_length (true), /* bool fp_save_note... */ + m_use_time_sig (false), /* bool fp_use_time_... */ + m_time_sig_beats (0), /* int fp_beats_per_bar */ + m_time_sig_width (0), /* int fp_beat_width */ m_is_modified (false), m_was_clean (! s.modified()) { @@ -131,21 +135,28 @@ qpatternfix::initialize (bool startup) std::string value = std::to_string(int(track().get_length())); ui->label_pulses->setText(qt(value)); value = std::to_string(int(m_measures)); - ui->line_edit_pick->setText(qt(value)); + ui->line_edit_measures->setText(qt(value)); value = std::to_string(1); /* actually a float */ ui->line_edit_scale->setText(qt(value)); - ui->btn_effect_shift->setChecked(false); - ui->btn_effect_reverse->setChecked(false); - ui->btn_effect_shrink->setChecked(false); - ui->btn_effect_expand->setChecked(false); - ui->btn_effect_time_sig->setChecked(false); - ui->btn_effect_truncate->setChecked(false); + ui->btn_effect_alteration->setEnabled(false); ui->btn_effect_shift->setEnabled(false); ui->btn_effect_reverse->setEnabled(false); ui->btn_effect_shrink->setEnabled(false); ui->btn_effect_expand->setEnabled(false); ui->btn_effect_time_sig->setEnabled(false); ui->btn_effect_truncate->setEnabled(false); + + /* + * ui->btn_effect_alteration->setChecked(false); + * ui->btn_effect_shift->setChecked(false); + * ui->btn_effect_reverse->setChecked(false); + * ui->btn_effect_shrink->setChecked(false); + * ui->btn_effect_expand->setChecked(false); + * ui->btn_effect_time_sig->setChecked(false); + * ui->btn_effect_truncate->setChecked(false); + */ + + slot_effect_clear(); ui->btn_align_left->setChecked(false); ui->btn_reverse->setChecked(false); ui->btn_reverse_in_place->setChecked(false); @@ -187,7 +198,7 @@ qpatternfix::initialize (bool startup) ); connect ( - ui->line_edit_pick, SIGNAL(editingFinished()), + ui->line_edit_measures, SIGNAL(editingFinished()), this, SLOT(slot_measure_change()) ); connect @@ -198,55 +209,152 @@ qpatternfix::initialize (bool startup) ui->line_edit_none->hide(); /* unused, hide it */ /* - * Quantization. + * Alteration. None, tighten, quantize, jitter, random, and notemap, + * all mutually exclusive. */ - m_quan_group = new QButtonGroup(this); + m_alt_group = new QButtonGroup(this); ui->group_box_quantize->setEnabled(true); - m_quan_group->addButton(ui->btn_quan_none, cast(alteration::none)); - m_quan_group->addButton + + /* + * None + */ + + m_alt_group->addButton(ui->btn_alt_none, cast(alteration::none)); + + /* + * Tighten + */ + + m_alt_group->addButton ( - ui->btn_quan_tighten, cast(alteration::tighten) + ui->btn_alt_tighten, cast(alteration::tighten) ); - m_quan_group->addButton(ui->btn_quan_full, cast(alteration::quantize)); - ui->btn_quan_none->setChecked(true); - ui->line_edit_q_none->hide(); - ui->line_edit_q_tighten->hide(); - ui->line_edit_q_full->hide(); + + /* + * Quantize + */ + + m_alt_group->addButton(ui->btn_alt_full, cast(alteration::quantize)); + + /* + * Jitter + */ + + m_alt_group->addButton(ui->btn_alt_jitter, cast(alteration::jitter)); + + /* + * Random + */ + + m_alt_group->addButton(ui->btn_alt_random, cast(alteration::random)); + + /* + * Note-map and Reverse Note-map + */ + + m_alt_group->addButton(ui->btn_alt_notemap, cast(alteration::notemap)); + m_alt_group->addButton + ( + ui->btn_alt_rev_notemap, cast(alteration::rev_notemap) + ); + + /* + * Enable, disable, or hide the corresponding group items. + * Currently the jitter edit value is used. + * + * ui->line_edit_alt_jitter->hide(); + */ + + ui->btn_alt_none->setChecked(true); + ui->line_edit_alt_none->hide(); /* reveal once code in place */ connect ( - m_quan_group, QT5_HELPER_RADIO_SIGNAL, - [=](int id) { slot_quan_change(id); } /* lambda function */ + m_alt_group, QT5_HELPER_RADIO_SIGNAL, + [=](int id) { slot_alt_change(id); } /* lambda function */ + ); + + /* + * TODO: Add similar slots for Tighten, Full (quantization), + * Random, and maybe a button to load a note-map file. + * + * We want to be able to change from the snap values for quantization. + */ + + value = std::to_string(m_tighten_range); + ui->line_edit_alt_tighten->setText(qt(value)); + connect + ( + ui->line_edit_alt_tighten, SIGNAL(editingFinished()), + this, SLOT(slot_tighten_change()) + ); + value = std::to_string(m_full_range); + ui->line_edit_alt_full->setText(qt(value)); + connect + ( + ui->line_edit_alt_full, SIGNAL(editingFinished()), + this, SLOT(slot_full_change()) + ); + value = std::to_string(m_random_range); + ui->line_edit_alt_random->setText(qt(value)); + connect + ( + ui->line_edit_alt_random, SIGNAL(editingFinished()), + this, SLOT(slot_random_change()) ); value = std::to_string(m_jitter_range); - ui->line_edit_q_jitter->setText(qt(value)); + ui->line_edit_alt_jitter->setText(qt(value)); connect ( - ui->line_edit_q_jitter, SIGNAL(editingFinished()), + ui->line_edit_alt_jitter, SIGNAL(editingFinished()), this, SLOT(slot_jitter_change()) ); + connect + ( + ui->btn_notemap_file, SIGNAL(clicked()), + this, SLOT(slot_notemap_file()) + ); /* * The "read-only" Effect group. The slot here merely keeps them from - * being checked by the user. If we make them readonly they text is + * being checked by the user. If we make them readonly the text is * difficult to read in many Qt themes. */ connect ( - ui->btn_effect_shift, SIGNAL(clicked()), this, SLOT(slot_effect()) + ui->btn_effect_alteration, SIGNAL(clicked()), + this, SLOT(slot_effect_clear()) + ); + connect + ( + ui->btn_effect_shift, SIGNAL(clicked()), + this, SLOT(slot_effect_clear()) + ); + connect + ( + ui->btn_effect_reverse, SIGNAL(clicked()), + this, SLOT(slot_effect_clear()) + ); + connect + ( + ui->btn_effect_shrink, SIGNAL(clicked()), + this, SLOT(slot_effect_clear()) ); connect ( - ui->btn_effect_shrink, SIGNAL(clicked()), this, SLOT(slot_effect()) + ui->btn_effect_expand, SIGNAL(clicked()), + this, SLOT(slot_effect_clear()) ); connect ( - ui->btn_effect_expand, SIGNAL(clicked()), this, SLOT(slot_effect()) + ui->btn_effect_time_sig, SIGNAL(clicked()), + this, SLOT(slot_effect_clear()) ); connect ( - ui->btn_effect_time_sig, SIGNAL(clicked()), this, SLOT(slot_effect()) + ui->btn_effect_truncate, SIGNAL(clicked()), + this, SLOT(slot_effect_clear()) ); /* @@ -292,7 +400,7 @@ qpatternfix::initialize (bool startup) */ ui->btn_change_none->setChecked(true); - ui->btn_quan_none->setChecked(true); + ui->btn_alt_none->setChecked(true); } } @@ -316,23 +424,31 @@ qpatternfix::unmodify (bool reset_fields) if (reset_fields) { std::string temp = std::to_string(track().get_measures()); - ui->line_edit_pick->setText(qt(temp)); + ui->line_edit_measures->setText(qt(temp)); ui->line_edit_scale->setText("1.0"); - ui->btn_effect_shift->setChecked(false); - ui->btn_effect_shrink->setChecked(false); - ui->btn_effect_expand->setChecked(false); ui->btn_reset->setEnabled(false); + + /* + * ui->btn_effect_shift->setChecked(false); + * ui->btn_effect_shrink->setChecked(false); + * ui->btn_effect_expand->setChecked(false); + */ + + slot_effect_clear(); } m_is_modified = false; } void -qpatternfix::slot_effect () +qpatternfix::slot_effect_clear () { + ui->btn_effect_alteration->setChecked(false); ui->btn_effect_shift->setChecked(false); + ui->btn_effect_reverse->setChecked(false); ui->btn_effect_shrink->setChecked(false); ui->btn_effect_expand->setChecked(false); ui->btn_effect_time_sig->setChecked(false); + ui->btn_effect_truncate->setChecked(false); } void @@ -343,31 +459,47 @@ qpatternfix::slot_length_fix (int fixlengthid) modify(); } +/** + * The user can enter either an integer measure count to set the measures + * directly, or a fraction of the form x/y to scale the number of measures. + * It sets the lengthfix::measures item rather than lengthfix::rescale. + * + * Tricky: If setting integer measures, the end effect is scaling, not + * truncating. + */ + void qpatternfix::slot_measure_change () { - QString t = ui->line_edit_pick->text(); + QString t = ui->line_edit_measures->text(); std::string tc = t.toStdString(); double m = string_to_double(tc, 1.0); if (sequence::valid_scale_factor(m, true)) /* applies to measures, too */ { int beats, width; - bool is_time_sig = string_to_time_signature(tc, beats, width); - if (m != m_measures || is_time_sig) + bool is_fraction = string_to_time_signature(tc, beats, width); + bool different = fnotequal(m, m_measures); + if (different || is_fraction) { ui->btn_change_pick->setChecked(true); m_measures = m; m_length_type = lengthfix::measures; - m_time_sig_beats = beats; - m_time_sig_width = width; - m_use_time_sig = is_time_sig; - ui->btn_effect_time_sig->setChecked(is_time_sig); - if (is_time_sig) - { - midipulse max = track().get_max_timestamp(); - midipulse newlength = midipulse(track().get_length() * m); - ui->btn_effect_truncate->setChecked(newlength < max); - } + if (beats > 0 && beats < 96) /* just a sanity check */ + m_time_sig_beats = beats; /* fraction numerator */ + + if (width > 0 && width < 96) /* just a sanity check */ + m_time_sig_width = width; /* fraction denominator */ + + m_use_time_sig = is_fraction; + ui->btn_effect_time_sig->setChecked(is_fraction); + + midipulse curlength = track().get_length(); + midipulse maxlength = track().get_max_timestamp(); + midipulse newlength = midipulse(track().unit_measure() * m); + bool shrunk = newlength < curlength; + bool reduced = newlength < maxlength; + ui->btn_effect_truncate->setChecked(reduced); + ui->btn_effect_shrink->setChecked(shrunk); modify(); } } @@ -398,28 +530,84 @@ qpatternfix::slot_scale_change () } void -qpatternfix::slot_quan_change (int quanid) +qpatternfix::slot_alt_change (int quanid) { - m_quan_type = quantization_cast(quanid); - if (m_quan_type != alteration::none) + alteration quantype = quantization_cast(quanid); + if (m_alt_type != quantype) + { + bool not_none = quantype != alteration::none; + m_alt_type = quantype; + ui->btn_effect_alteration->setChecked(not_none); modify(); + } +} + +void +qpatternfix::slot_tighten_change () +{ + QString t = ui->line_edit_alt_tighten->text(); + std::string tc = t.toStdString(); + int m = string_to_int(tc, 0); + if (m > 0 && m < track().get_ppqn()) /* sanity check */ + { + ui->btn_alt_tighten->setChecked(true); + m_tighten_range = m; + m_alt_type = alteration::tighten; + modify(); + } +} + +void +qpatternfix::slot_full_change () +{ + QString t = ui->line_edit_alt_full->text(); + std::string tc = t.toStdString(); + int m = string_to_int(tc, 0); + if (m > 0 && m < track().get_ppqn()) /* sanity check */ + { + ui->btn_alt_full->setChecked(true); + m_full_range = m; + m_alt_type = alteration::quantize; + modify(); + } +} + +void +qpatternfix::slot_random_change () +{ + QString t = ui->line_edit_alt_random->text(); + std::string tc = t.toStdString(); + int m = string_to_int(tc, 0); + if (m > 0 && m < track().get_ppqn()) /* sanity check */ + { + ui->btn_alt_random->setChecked(true); + m_random_range = m; + m_alt_type = alteration::random; + modify(); + } } void qpatternfix::slot_jitter_change () { - QString t = ui->line_edit_q_jitter->text(); + QString t = ui->line_edit_alt_jitter->text(); std::string tc = t.toStdString(); int m = string_to_int(tc, 0); if (m > 0 && m < track().get_ppqn()) /* sanity check */ { - ui->btn_quan_jitter->setChecked(true); + ui->btn_alt_jitter->setChecked(true); m_jitter_range = m; - m_quan_type = alteration::jitter; + m_alt_type = alteration::jitter; modify(); } } +void +qpatternfix::slot_notemap_file () +{ + printf("Current note-map file: '%s'\n", m_notemap_file.c_str()); +} + void qpatternfix::slot_align_change (int state) { @@ -492,14 +680,16 @@ qpatternfix::slot_set () fixeffect efx; fixparameters fp = /* value structure */ { - m_length_type, m_quan_type, m_jitter_range, + m_length_type, m_alt_type, + m_tighten_range, m_full_range, m_random_range, m_jitter_range, m_align_left, m_reverse, m_reverse_in_place, m_save_note_length, m_use_time_sig, m_time_sig_beats, m_time_sig_width, m_measures, m_scale_factor, efx }; - bool success = perf().fix_sequence(track().seq_number(), fp); + bool success = perf().fix_pattern(track().seq_number(), fp); if (success) { + bool alteration = m_alt_type != alteration::none; bool bitshifted = bit_test(efx, fixeffect::shifted); bool bitreversed = bit_test(efx, fixeffect::reversed); bool bitshrunk = bit_test(efx, fixeffect::shrunk); @@ -518,9 +708,10 @@ qpatternfix::slot_set () else temp = double_to_string(m_measures); - ui->line_edit_pick->setText(qt(temp)); + ui->line_edit_measures->setText(qt(temp)); temp = double_to_string(m_scale_factor); ui->line_edit_scale->setText(qt(temp)); + ui->btn_effect_alteration->setChecked(alteration); ui->btn_effect_shift->setChecked(bitshifted); ui->btn_effect_shrink->setChecked(bitshrunk); ui->btn_effect_expand->setChecked(bitexpanded); @@ -542,8 +733,15 @@ qpatternfix::slot_reset () m_time_sig_beats = m_time_sig_width = 0; m_scale_factor = 1.0; m_length_type = lengthfix::none; - m_quan_type = alteration::none; + m_alt_type = alteration::none; + m_tighten_range = track().snap() / 2; + m_full_range = track().snap(); + m_random_range = usr().randomization_amount(); + m_jitter_range = usr().jitter_range(track().get_ppqn() / 4); + m_reverse_notemap = false; + m_notemap_file = rc().notemap_filename(); initialize(false); + slot_effect_clear(); unmodify(); /* change fields */ set_dirty(); /* for redrawing */ if (m_was_clean) diff --git a/seq_qt5/src/qseqeditframe64.cpp b/seq_qt5/src/qseqeditframe64.cpp index 358b3866..b72e424d 100644 --- a/seq_qt5/src/qseqeditframe64.cpp +++ b/seq_qt5/src/qseqeditframe64.cpp @@ -25,7 +25,7 @@ * \library seq66 application * \author Chris Ahlstrom * \date 2018-06-15 - * \updates 2024-12-01 + * \updates 2024-12-08 * \license GNU GPLv2 or above * * The data pane is the drawing-area below the seqedit's event area, and @@ -2706,6 +2706,9 @@ qseqeditframe64::tighten_notes () } /** + * Jitter the selected notes, preceded by pushing the current events + * onto the undo stack. + * * Includes Aftertouch events. */ @@ -2720,7 +2723,7 @@ void qseqeditframe64::randomize_notes () { int r = usr().randomization_amount(); - track().randomize_selected_notes(r); + track().randomize_notes(r); } /** diff --git a/seq_qt5/src/qseqroll.cpp b/seq_qt5/src/qseqroll.cpp index 91adaf49..31c12027 100644 --- a/seq_qt5/src/qseqroll.cpp +++ b/seq_qt5/src/qseqroll.cpp @@ -1800,7 +1800,7 @@ qseqroll::keyPressEvent (QKeyEvent * event) * the note velocity quite naturally. */ - if (track().randomize_selected_notes()) /* pushes too */ + if (track().randomize_notes()) /* pushes too */ done = mark_modified(); break;