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;