Skip to content

Commit

Permalink
Fixed bugs in note detection, attempted to allow tempo dragging.
Browse files Browse the repository at this point in the history
  • Loading branch information
ahlstromcj committed Sep 23, 2023
1 parent c4df3c5 commit a3cca6a
Show file tree
Hide file tree
Showing 7 changed files with 115 additions and 87 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# README for Seq66 0.99.9 2023-09-21
# README for Seq66 0.99.9 2023-09-23

__Seq66__: MIDI sequencer/live-looper with a hardware-sampler grid interface;
pattern banks, triggers, and playlists for song management; scale and chord
Expand Down Expand Up @@ -88,6 +88,7 @@ Windows, and using a conventional source tarball.
* Fixed nasty segfault opening new file while Editor tab open.
* Fixed bug: port-mapping Remap and Restart did not work due to
timing.
* Fixed bug in detecting Note-related messages.
* Fixed error in "quiet" startup that would cause immediate exit.
* Related to issue #115: Added ability to select a line in the data
pane and grab a handle to change its value.
Expand Down
1 change: 1 addition & 0 deletions libseq66/include/midi/calculations.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,7 @@ extern midibyte beat_log2 (int value);
extern midibpm tempo_us_from_bytes (const midibyte tt[3]);
extern bool tempo_us_to_bytes (midibyte t[3], midibpm tempo_us);
extern midibyte tempo_to_note_value (midibpm tempo);
extern midibpm note_value_to_tempo (midibyte tempo);
extern midibpm fix_tempo (midibpm bpm);
extern unsigned short combine_bytes (midibyte b0, midibyte b1);
extern midibpm note_value_to_tempo (midibyte note);
Expand Down
52 changes: 27 additions & 25 deletions libseq66/include/midi/event.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
* \library seq66 application
* \author Chris Ahlstrom
* \date 2015-07-24
* \updates 2023-09-06
* \updates 2023-09-23
* \license GNU GPLv2 or above
*
* This module also declares/defines the various constants, status-byte
Expand All @@ -39,12 +39,14 @@
* Meta events. First, we use the existing event::sysex to hold
* this data.
*
* The MIDI protocol consists of MIDI events that carry four types of messages:
* The MIDI protocol consists of MIDI events that carry four types of
* messages:
*
* - Voice messages. 0x80 to 0xEF; includes channel information.
* - System common messages. 0xF0 (SysEx) to 0xF7 (End of SysEx)
* - System realtime messages. 0xF8 to 0xFF.
* - Meta messages. 0xFF is the flag, followed by type, length, and data.
* - Meta messages. 0xFF is the flag, followed by type, length, and
* data.
*/

#include <string> /* used in to_string() */
Expand Down Expand Up @@ -590,9 +592,23 @@ class event
return mask_status(m) == EVENT_CONTROL_CHANGE;
}

static bool is_note_on_msg (midibyte m)
/**
* Static test for messages that involve notes and velocity: Note On,
* Note Off, and Aftertouch.
*
* \param m
* The channel status or message byte to be tested, and the channel
* bits are masked off before testing. Actually, no longer
* necessary, we have a faster test, since these three events have
* values in an easy range to check.
*
* \return
* Returns true if the byte represents a MIDI note message.
*/

static bool is_note_msg (midibyte m)
{
return m >= EVENT_NOTE_ON || m < EVENT_AFTERTOUCH;
return m >= EVENT_NOTE_OFF && m < EVENT_CONTROL_CHANGE;
}

/**
Expand All @@ -608,7 +624,12 @@ class event

static bool is_strict_note_msg (midibyte m)
{
return m >= EVENT_NOTE_OFF || m < EVENT_AFTERTOUCH;
return m >= EVENT_NOTE_OFF && m < EVENT_AFTERTOUCH;
}

static bool is_note_on_msg (midibyte m)
{
return m >= EVENT_NOTE_ON && m < EVENT_AFTERTOUCH;
}

/**
Expand Down Expand Up @@ -690,25 +711,6 @@ class event
);
}

/**
* Static test for messages that involve notes and velocity: Note On,
* Note Off, and Aftertouch.
*
* \param m
* The channel status or message byte to be tested, and the channel
* bits are masked off before testing. Actually, no longer
* necessary, we have a faster test, since these three events have
* values in an easy range to check.
*
* \return
* Returns true if the byte represents a MIDI note message.
*/

static bool is_note_msg (midibyte m)
{
return m >= EVENT_NOTE_OFF && m < EVENT_CONTROL_CHANGE;
}

/**
* This static member function is used in the midifile module and in the
* is_note_off_recorded() member function.
Expand Down
60 changes: 25 additions & 35 deletions libseq66/src/midi/calculations.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1198,12 +1198,35 @@ midibyte
tempo_to_note_value (midibpm tempovalue)
{
double slope = double(max_midi_value());
slope /= usr().midi_bpm_maximum() - usr().midi_bpm_minimum();
double minimum = usr().midi_bpm_minimum();
slope /= usr().midi_bpm_maximum() - minimum;

int note = int(slope * (tempovalue - usr().midi_bpm_minimum()) + 0.5);
int note = int(slope * (tempovalue - minimum) + 0.5);
return clamp_midibyte_value(note);
}

/**
* From the above, we can derive:
*
\verbatim
(B1 - B0) N
B = ------------ + B0
127
\endverbatim
*
*/

midibpm
note_value_to_tempo (midibyte notevalue)
{
double denominator = double(max_midi_value());
double minimum = usr().midi_bpm_minimum();
midibpm result = notevalue * (usr().midi_bpm_maximum() - minimum);
result /= denominator;
result += minimum;
return result;
}

/**
* Fixes the tempo value, truncating it to the number of digits of tempo
* precision (0, 1, or 2) specified by "bpm_precision" in the "usr" file.
Expand Down Expand Up @@ -1270,39 +1293,6 @@ combine_bytes (midibyte b0, midibyte b1)
return short_14bit * 48;
}

/**
* The inverse of tempo_to_note_value().
*
\verbatim
(N - N0) (B1 - B0)
B = B0 + --------------------
N1 - N0
\endverbatim
*
\verbatim
(B1 - B0)
B = B0 + N -----------
127
\endverbatim
*
* \param note
* The note value used for displaying the tempo in the seqdata pane, the
* perfroll, and in a mainwnd slot.
*
* \return
* Returns the tempo in beats/minute.
*/

midibpm
note_value_to_tempo (midibyte note)
{
double slope = usr().midi_bpm_maximum() - usr().midi_bpm_minimum();
slope *= double(note);
slope /= double(max_midi_value());
slope += usr().midi_bpm_minimum();
return slope;
}

/**
* Formalizes the rescaling of ticks base on changing the PPQN. For speed
* the parameters are all assumed to be valid. The PPQN values supported
Expand Down
27 changes: 20 additions & 7 deletions libseq66/src/midi/event.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -448,13 +448,26 @@ event::is_desired (midibyte status, midibyte cc) const
bool
event::is_data_in_handle_range (midibyte target) const
{
static const midibyte delta = 2; /* seq32 provision */
static const midibyte max = c_midibyte_value_max - delta;
midibyte datum = is_one_byte() ? d0() : d1() ;
bool result = target >= delta && target <= max;
if (result)
result = datum >= (target - delta) && datum <= (target + delta);

bool result = false;
if (is_tempo())
{
static const midibpm s_delta = 10;
midibpm t = tempo();
midibpm tdesired = note_value_to_tempo(target);
result = t >= (tdesired - s_delta) && t <= (tdesired + s_delta);
}
else
{
static const midibyte s_delta = 2; /* seq32 provision */
static const midibyte max = c_midibyte_value_max - s_delta;
midibyte datum = is_one_byte() ? d0() : d1() ;
result = target >= s_delta && target <= max;
if (result)
{
result = datum >= (target - s_delta) &&
datum <= (target + s_delta);
}
}
return result;
}

Expand Down
32 changes: 27 additions & 5 deletions libseq66/src/midi/eventlist.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1943,10 +1943,29 @@ eventlist::select_events
}

/**
* Selects the seqdata event handle if in range.
* Selects the seqdata event handle if in range. One issue in adjusting data
* is Pitch events, which have two components [d0() and d1()] which must be
* combined. Another issue is tempo events, where the value is converted to a
* note-velocity in order to display it in the data pane.
*
* One issue in adjusting data is Pitch events, which have two
* components [d0() and d1()] which must be combined.
* \param tick_s
* Provides the starting tick, which is some small amount below the tick
* represented by the mouse position.
*
* \param tick_f
* Provides the finishing tick, which is some small amount above the
* tick represented by the mouse position.
*
* \param astatus
* Provides the type of event, such as Note-On/Off, Pitchbend, or Tempo.
*
* \param cc
* For control events, represents the control code. Does not apply to
* Notes, Tempo, Pitchbend. For Meta events, cc is the type of Meta
* event.
*
* \param data
* Currently represents the note value.
*/

int
Expand All @@ -1964,12 +1983,15 @@ eventlist::select_event_handle
if (count_selected_events(astatus, cc) > 0)
have_selected_note_ons = true;
}
else if (event::is_tempo_status(cc))
{
}
for (auto & er : m_events)
{
if (event_in_range(er, astatus, tick_s, tick_f)) /* in time-range */
if (event_in_range(er, astatus, tick_s, tick_f)) /* in time-range */
{
bool isctrl = event::is_controller_msg(astatus);
if (isctrl && er.is_desired(astatus, cc, data)) /* in data-range */
if (isctrl && er.is_desired(astatus, cc, data)) /* in range */
{
unselect_all(); /* or unmark() */
er.select(); /* or mark()??? */
Expand Down
27 changes: 13 additions & 14 deletions seq_qt5/src/qseqdata.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,14 @@ qseqdata::paintEvent (QPaintEvent * qpep)
int event_height = event::is_one_byte_msg(m_status) ? d0 : d1 ;
event_height = height() - byte_height(m_dataarea_y, event_height);
pen.setWidth(2);

bool its_close = false;
if (! selected && m_mouse_tick >= 0)
{
midipulse delta = std::labs(tick - m_mouse_tick);
if (delta <= m_handle_delta)
its_close = true;
}
if (data_event)
{
#if defined SEQ66_REQUIRE_SEQ_CHANNEL_MATCH /* too much! */
Expand Down Expand Up @@ -272,14 +280,6 @@ qseqdata::paintEvent (QPaintEvent * qpep)
event_x -= 3;
painter.drawLine(event_x, event_height, event_x, height());
snprintf(digits, sizeof digits, "%3d", d1);

bool its_close = false;
if (! selected && m_mouse_tick >= 0)
{
midipulse delta = std::labs(tick - m_mouse_tick);
if (delta <= m_handle_delta)
its_close = true;
}
if (selected || its_close)
{
painter.drawEllipse
Expand All @@ -288,7 +288,6 @@ qseqdata::paintEvent (QPaintEvent * qpep)
s_handle_d, s_handle_d
);
}

if (! is_pitchbend())
{
QString val = digits;
Expand All @@ -310,6 +309,9 @@ qseqdata::paintEvent (QPaintEvent * qpep)

snprintf(digits, sizeof digits, "%3d", int(cev->tempo()));
brush.setColor(selected ? sel_color() : tempo_color());
if (its_close)
pen.setColor(Qt::yellow);

painter.setBrush(brush);
painter.setPen(pen);
painter.drawEllipse
Expand Down Expand Up @@ -382,8 +384,7 @@ qseqdata::paintEvent (QPaintEvent * qpep)
text += "/";
text += std::to_string(d);

pen.setColor(Qt::white);
pen.setColor(Qt::black);
pen.setColor(Qt::white); // pen.setColor(Qt::black);
painter.setPen(pen);
painter.drawText(pos, y_offset, qt(text));
}
Expand Down Expand Up @@ -483,7 +484,6 @@ qseqdata::mousePressEvent (QMouseEvent * event)
* Check if this tick range would select an event.
*/


track().push_undo();
old_rect().clear(); /* reset dirty redraw box */
}
Expand Down Expand Up @@ -544,8 +544,7 @@ qseqdata::mouseMoveEvent (QMouseEvent * event)
track().adjust_event_handle(m_status, m_dataarea_y - current_y());
update();
}
else
if (m_line_adjust)
else if (m_line_adjust)
{
#if defined SEQ66_TRACK_DATA_EDITING_MOVEMENTS
int adj_x_min, adj_x_max, adj_y_min, adj_y_max;
Expand Down

0 comments on commit a3cca6a

Please sign in to comment.