From 894efb2d7404902fc74108edcdc978326a5e7f22 Mon Sep 17 00:00:00 2001 From: Chris Ahlstrom Date: Wed, 7 Sep 2022 14:49:35 -0400 Subject: [PATCH] For issue #100, added current tick value to each output event and to the ringbuffer data, seems to work, need further verification. --- configure | 20 +-- contrib/midi/simpleblastbeat.midi | Bin 433 -> 433 bytes libseq66/include/play/sequence.hpp | 3 +- libseq66/src/midi/jack_assistant.cpp | 4 +- libseq66/src/play/sequence.cpp | 6 +- seq_rtmidi/include/midi_jack_data.hpp | 5 +- seq_rtmidi/include/midi_jack_info.hpp | 36 +++++- seq_rtmidi/include/rtmidi_types.hpp | 17 ++- seq_rtmidi/src/midi_jack.cpp | 177 +++++++++++++++++++------- seq_rtmidi/src/midi_jack_info.cpp | 9 +- seq_rtmidi/src/rtmidi_types.cpp | 124 +++++++++++++++++- 11 files changed, 332 insertions(+), 69 deletions(-) diff --git a/configure b/configure index 52ae74403..b5dab0977 100755 --- a/configure +++ b/configure @@ -1,7 +1,7 @@ #! /bin/sh # From configure.ac Revision: 0.96. # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.69 for Seq66 0.99.0. +# Generated by GNU Autoconf 2.69 for Seq66 0.99.1. # # Report bugs to . # @@ -591,8 +591,8 @@ MAKEFLAGS= # Identity of this package. PACKAGE_NAME='Seq66' PACKAGE_TARNAME='seq66' -PACKAGE_VERSION='0.99.0' -PACKAGE_STRING='Seq66 0.99.0' +PACKAGE_VERSION='0.99.1' +PACKAGE_STRING='Seq66 0.99.1' PACKAGE_BUGREPORT='ahlstromcj@gmail.com' PACKAGE_URL='' @@ -1446,7 +1446,7 @@ if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures Seq66 0.99.0 to adapt to many kinds of systems. +\`configure' configures Seq66 0.99.1 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1521,7 +1521,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of Seq66 0.99.0:";; + short | recursive ) echo "Configuration of Seq66 0.99.1:";; esac cat <<\_ACEOF @@ -1665,7 +1665,7 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -Seq66 configure 0.99.0 +Seq66 configure 0.99.1 generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. @@ -2155,7 +2155,7 @@ cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by Seq66 $as_me 0.99.0, which was +It was created by Seq66 $as_me 0.99.1, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ @@ -3090,7 +3090,7 @@ fi # Define the identity of the package. PACKAGE='seq66' - VERSION='0.99.0' + VERSION='0.99.1' cat >>confdefs.h <<_ACEOF @@ -21224,7 +21224,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by Seq66 $as_me 0.99.0, which was +This file was extended by Seq66 $as_me 0.99.1, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -21290,7 +21290,7 @@ _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ -Seq66 config.status 0.99.0 +Seq66 config.status 0.99.1 configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" diff --git a/contrib/midi/simpleblastbeat.midi b/contrib/midi/simpleblastbeat.midi index 2b804b604abf4ab2dd32ee087c1f0ec95f4d93d9..695049aaaf0aeeaa7a04927c6b10f3003a099182 100644 GIT binary patch delta 11 TcmdnUypegr4n~HFJAVKG8g~Tr delta 11 TcmdnUypegr4o1d_JAVKG8hZrw diff --git a/libseq66/include/play/sequence.hpp b/libseq66/include/play/sequence.hpp index b1e0fc524..faf554831 100644 --- a/libseq66/include/play/sequence.hpp +++ b/libseq66/include/play/sequence.hpp @@ -1950,7 +1950,8 @@ class sequence midibyte status, midibyte cc, int divide, bool linked = false ); bool change_ppqn (int p); - void put_event_on_bus (event & ev); +// void put_event_on_bus (event & ev); + void put_event_on_bus (event ev); void reset_loop (); void set_trigger_offset (midipulse trigger_offset); void adjust_trigger_offsets_to_length (midipulse newlen); diff --git a/libseq66/src/midi/jack_assistant.cpp b/libseq66/src/midi/jack_assistant.cpp index 88c82e3ba..e08b21af3 100644 --- a/libseq66/src/midi/jack_assistant.cpp +++ b/libseq66/src/midi/jack_assistant.cpp @@ -279,7 +279,7 @@ jack_dummy_callback (jack_nframes_t nframes, void * arg) int jack_transport_callback (jack_nframes_t /*nframes*/, void * arg) { - jack_assistant * j = static_cast(arg); + jack_assistant * j = reinterpret_cast(arg); if (not_nullptr(j)) { jack_position_t pos; @@ -1840,6 +1840,8 @@ jack_assistant::show_position (const jack_position_t & pos) /** * A member wrapper function for the new free function create_jack_client(). + * This function is used for creating a JACK transport client and a JACK + * sequencer (MIDI) client. * * \param name * Provides the name of the client, used in the call to diff --git a/libseq66/src/play/sequence.cpp b/libseq66/src/play/sequence.cpp index cca275430..7884b5913 100644 --- a/libseq66/src/play/sequence.cpp +++ b/libseq66/src/play/sequence.cpp @@ -5547,7 +5547,8 @@ sequence::to_string () const */ void -sequence::put_event_on_bus (event & ev) +// sequence::put_event_on_bus (event & ev) +sequence::put_event_on_bus (event ev) { midibyte note = ev.get_note(); bool skip = false; @@ -5563,7 +5564,10 @@ sequence::put_event_on_bus (event & ev) --m_playing_notes[note]; } if (! skip) + { + ev.set_timestamp(m_parent->get_tick()); /* issue #100 */ master_bus()->play_and_flush(m_true_bus, &ev, midi_channel(ev)); + } } /** diff --git a/seq_rtmidi/include/midi_jack_data.hpp b/seq_rtmidi/include/midi_jack_data.hpp index 45c7810d9..79718a72a 100644 --- a/seq_rtmidi/include/midi_jack_data.hpp +++ b/seq_rtmidi/include/midi_jack_data.hpp @@ -27,7 +27,7 @@ * \library seq66 application * \author Chris Ahlstrom * \date 2017-01-02 - * \updates 2022-02-18 + * \updates 2022-09-06 * \license See above. * * GitHub issue #165: enabled a build and run with no JACK support. @@ -147,7 +147,8 @@ struct midi_jack_data bool valid_buffer () const { - return not_nullptr(m_jack_buffsize) && not_nullptr(m_jack_buffmessage); + return not_nullptr(m_jack_buffmessage); + /* not_nullptr(m_jack_buffsize) && */ } #if defined SEQ66_MIDI_PORT_REFRESH diff --git a/seq_rtmidi/include/midi_jack_info.hpp b/seq_rtmidi/include/midi_jack_info.hpp index 8ab60ab54..6dc204a8d 100644 --- a/seq_rtmidi/include/midi_jack_info.hpp +++ b/seq_rtmidi/include/midi_jack_info.hpp @@ -27,7 +27,7 @@ * \library seq66 application * \author Chris Ahlstrom * \date 2017-01-01 - * \updates 2022-06-24 + * \updates 2022-09-06 * \license See above. * * We need to have a way to get all of the JACK information of @@ -97,6 +97,30 @@ class midi_jack_info final : public midi_info jack_client_t * m_jack_client; + /** + * jack_nframes_t jack_get_buffer_size(jack_client_t *) + * + * Returns the current maximum size that is passed to the process callback. + * It should only be used before the client has been activated. + * This size may change, clients that depend on it must register a + * bufsize callback to be notified if it does. + * + * Part of issue #100. + */ + + jack_nframes_t m_jack_buffer_size; + + /** + * jack_nframes_t jack_get_sample_rate(jack_client_t *) + * + * Returns the sample rate of the JACK system as set by the user when jackd + * started. + * + * Part of issue #100. + */ + + jack_nframes_t m_jack_sample_rate; + public: midi_jack_info () = delete; @@ -112,6 +136,16 @@ class midi_jack_info final : public midi_info return m_jack_client; } + int jack_buffer_size () const + { + return int(m_jack_buffer_size); + } + + int jack_sample_rate () const + { + return int(m_jack_sample_rate); + } + virtual bool api_get_midi_event (event * inev) override; virtual bool api_connect () override; virtual int api_poll_for_midi () override; diff --git a/seq_rtmidi/include/rtmidi_types.hpp b/seq_rtmidi/include/rtmidi_types.hpp index ae33682bc..5c4b86ad1 100644 --- a/seq_rtmidi/include/rtmidi_types.hpp +++ b/seq_rtmidi/include/rtmidi_types.hpp @@ -27,7 +27,7 @@ * \library seq66 application * \author Gary P. Scavone; severe refactoring by Chris Ahlstrom * \date 2016-11-20 - * \updates 2022-06-30 + * \updates 2022-09-07 * \license See above. * * The lack of hiding of these types within a class is a little to be @@ -50,6 +50,12 @@ #define SEQ66_RTMIDI_VERSION "2.1.1" /* the revision at fork time */ +/* + * EXPERIMENTAL. Doesn't break playback ! + */ + +#define USE_ISSUE_100_FIX + /* * Do not document the namespace; it breaks Doxygen. */ @@ -156,6 +162,11 @@ class midi_message public: midi_message (double ts = 0.0); + midi_message (const midibyte * mbs, size_t sz); + + static midipulse extract_timestamp (const midibyte * mbs, size_t sz); + + void copy (midibyte * mbs, size_t sz); midibyte & operator [] (size_t i) { @@ -194,6 +205,10 @@ class midi_message m_bytes.push_back(b); } + bool push_timestamp (midipulse b); + midipulse pop_timestamp (); + midipulse extract_timestamp () const; + double timestamp () const { return m_timestamp; diff --git a/seq_rtmidi/src/midi_jack.cpp b/seq_rtmidi/src/midi_jack.cpp index e4f828af7..c6365715f 100644 --- a/seq_rtmidi/src/midi_jack.cpp +++ b/seq_rtmidi/src/midi_jack.cpp @@ -24,7 +24,7 @@ * \library seq66 application * \author Gary P. Scavone; severe refactoring by Chris Ahlstrom * \date 2016-11-14 - * \updates 2022-04-08 + * \updates 2022-09-07 * \license See above. * * Written primarily by Alexander Svetalkin, with updates for delta time by @@ -237,8 +237,8 @@ namespace seq66 * ENODATA = 61: No data available. * ENOBUFS = 105: No buffer space available can happen. * - * \param frameno - * The frame number to be processed. + * \param framect + * The number of frames to be processes * * \param arg * A pointer to the midi_jack_data structure to be processed. @@ -248,11 +248,11 @@ namespace seq66 */ int -jack_process_rtmidi_input (jack_nframes_t frameno, void * arg) +jack_process_rtmidi_input (jack_nframes_t framect, void * arg) { midi_jack_data * jackdata = reinterpret_cast(arg); rtmidi_in_data * rtindata = jackdata->m_jack_rtmidiin; - void * buf = ::jack_port_get_buffer(jackdata->m_jack_port, frameno); + void * buf = ::jack_port_get_buffer(jackdata->m_jack_port, framect); int evcount = ::jack_midi_get_event_count(buf); bool overflow = false; for (int j = 0; j < evcount; ++j) @@ -275,7 +275,7 @@ jack_process_rtmidi_input (jack_nframes_t frameno, void * arg) } jackdata->m_jack_lasttime = jtime; - midi_message message(delta_jtime); + midi_message message(delta_jtime); /* issue #100 */ int eventsize = int(jmevent.size); for (int i = 0; i < eventsize; ++i) message.push(jmevent.buffer[i]); @@ -309,6 +309,29 @@ jack_process_rtmidi_input (jack_nframes_t frameno, void * arg) return 0; } +#if defined USE_ISSUE_100_FIX + +static jack_nframes_t +jack_frame_offset +( + jack_nframes_t n, midipulse tick, const jack_position_t & pos +) +{ + static jack_nframes_t s_frame_rate = 48000; + static double s_ticks_per_beat = 1920; + static double s_beats_per_minute = 0; + static double s_factor = 1.0; + if (s_beats_per_minute != pos.beats_per_minute) + { + s_frame_rate = pos.frame_rate; + s_ticks_per_beat = pos.ticks_per_beat; + s_beats_per_minute = pos.beats_per_minute; + s_factor = (s_frame_rate * 60) / + (s_ticks_per_beat * s_beats_per_minute); + } + return jack_nframes_t(int(tick * s_factor) % n); +} + /** * Defines the JACK process output callback. It is the JACK process callback * for a MIDI output port (a midi_out_jack object associated with, for @@ -335,58 +358,112 @@ jack_process_rtmidi_input (jack_nframes_t frameno, void * arg) * tests, we are getting 1024 frames, and the code seems to work without that * loop. * - * \param frameno - * The frame number to be processed. + * A for-loop over the number of frames? See discussion above. + * + * Why are we reading here? That's where our app has dumped the next set + * of MIDI events to output. + * + * \param framect + * The number of frames to be processes * * \param arg - * A pointer to the JackMIDIData structure to be processed. + * A pointer to the midi_jack_data object, which holds basic information + * about the JACK client. * * \return * Returns 0. */ int -jack_process_rtmidi_output (jack_nframes_t frameno, void * arg) +jack_process_rtmidi_output (jack_nframes_t framect, void * arg) { - const size_t s_offset = 0; midi_jack_data * jackdata = reinterpret_cast(arg); - void * buf = ::jack_port_get_buffer(jackdata->m_jack_port, frameno); - ::jack_midi_clear_buffer(buf); /* no nullptr test */ + jack_port_t * jackport = jackdata->m_jack_port; + jack_ringbuffer_t * buffsz = jackdata->m_jack_buffsize; + jack_ringbuffer_t * buffmsg = jackdata->m_jack_buffmessage; + int space = 0; + const size_t sz = sizeof(space); + char * sp = reinterpret_cast(&space); + void * buf = ::jack_port_get_buffer(jackport, framect); + jack_client_t * client = jackdata->m_jack_client; + jack_position_t pos; + /* jack_transport_state_t */ (void) ::jack_transport_query(client, &pos); - /* - * A for-loop over the number of frames? See discussion above. - * Why are we reading here? That's where our app has dumped the next set - * of MIDI events to output. - */ - - while (jack_ringbuffer_read_space(jackdata->m_jack_buffsize) > 0) + ::jack_midi_clear_buffer(buf); /* no nullptr test */ + for (;;) { - int space; - (void) ::jack_ringbuffer_read - ( - jackdata->m_jack_buffsize, (char *) &space, sizeof space - ); + size_t msgsz = ::jack_ringbuffer_read_space(buffsz) > 0; + if (msgsz > 0) + { + (void) ::jack_ringbuffer_read(buffsz, sp, sz); + if (space > 0) + { + char * mbuf = new (std::nothrow) char[space]; + if (not_nullptr(mbuf)) + { + int ct = ::jack_ringbuffer_read(buffmsg, mbuf, size_t(space)); + midi_message mmsg(reinterpret_cast(mbuf), ct); + midipulse ts = mmsg.pop_timestamp(); + jack_nframes_t offset = jack_frame_offset(framect, ts, pos); + jack_midi_data_t * md = + ::jack_midi_event_reserve(buf, offset, space - 4); + + if (not_nullptr(md)) + { + midibyte * mididata = reinterpret_cast(md); + mmsg.copy(mididata, space - 4); + } + else + async_safe_errprint("JACK event reserve null pointer"); + } + } + } + else + break; + } + return 0; +} - /* - * s_offset is always 0. Using frameno instead of s_offset causes - * notes not to be played. Because this is a write operation? - */ +#else - jack_midi_data_t * md = ::jack_midi_event_reserve(buf, s_offset, space); - if (not_nullptr(md)) +int +jack_process_rtmidi_output (jack_nframes_t framect, void * arg) +{ + midi_jack_data * jackdata = reinterpret_cast(arg); + jack_port_t * jackport = jackdata->m_jack_port; + jack_ringbuffer_t * buffsz = jackdata->m_jack_buffsize; + jack_ringbuffer_t * buffmsg = jackdata->m_jack_buffmessage; + int space = 0; + const size_t sz = sizeof(space); + char * sp = reinterpret_cast(&space); + void * buf = ::jack_port_get_buffer(jackport, framect); + ::jack_midi_clear_buffer(buf); /* no nullptr test */ + for (;;) + { + size_t msgsz = ::jack_ringbuffer_read_space(buffsz) > 0; + if (msgsz > 0) { - char * mididata = reinterpret_cast(md); - (void) ::jack_ringbuffer_read /* copy into mididata */ - ( - jackdata->m_jack_buffmessage, mididata, size_t(space) - ); + (void) ::jack_ringbuffer_read(buffsz, sp, sz); + jack_midi_data_t * md = ::jack_midi_event_reserve(buf, 0, space); + if (not_nullptr(md)) + { + char * mididata = reinterpret_cast(md); + (void) ::jack_ringbuffer_read /* ignore byte count */ + ( + buffmsg, mididata, size_t(space) + ); + } + else + async_safe_errprint("JACK event reserve null pointer"); } else - async_safe_errprint("JACK Event Reserve null pointer"); + break; } return 0; } +#endif // defined USE_ISSUE_100_FIX + /** * This callback is to shut down JACK by clearing the jack_assistant :: * m_jack_running flag. @@ -977,11 +1054,12 @@ midi_jack::api_deinit_in () void midi_jack::api_play (const event * e24, midibyte channel) { + midipulse ts = e24->timestamp(); midibyte status = e24->get_status(channel); midibyte d0, d1; e24->get_data(d0, d1); - midi_message message; + midi_message message(ts); /* issue #100 */ message.push(status); message.push(d0); if (e24->is_two_bytes()) @@ -991,7 +1069,7 @@ midi_jack::api_play (const event * e24, midibyte channel) { if (! send_message(message)) { - errprint("JACK Play failed"); + errprint("JACK play failed"); } } } @@ -1015,14 +1093,25 @@ midi_jack::send_message (const midi_message & message) bool result = nbytes > 0; if (result) { +#if defined USE_OLD_CODE int count1 = ::jack_ringbuffer_write /* write the message bytes */ ( - jack_data().m_jack_buffmessage, message.array(), nbytes // message.count() + jack_data().m_jack_buffmessage, message.array(), nbytes ); int count2 = ::jack_ringbuffer_write /* write raw message size ! */ ( jack_data().m_jack_buffsize, (char *) &nbytes, sizeof nbytes ); +#else + int count1 = ::jack_ringbuffer_write /* write raw message size ! */ + ( + jack_data().m_jack_buffsize, (char *) &nbytes, sizeof nbytes + ); + int count2 = ::jack_ringbuffer_write /* write the message bytes */ + ( + jack_data().m_jack_buffmessage, message.array(), nbytes + ); +#endif result = (count1 > 0) && (count2 > 0); } return result; @@ -1044,7 +1133,7 @@ midi_jack::send_message (const midi_message & message) void midi_jack::api_sysex (const event * e24) { - midi_message message; + midi_message message(e24->timestamp()); const event::sysex & data = e24->get_sysex(); int data_size = e24->sysex_size(); for (int offset = 0; offset < data_size; ++offset) @@ -1054,7 +1143,7 @@ midi_jack::api_sysex (const event * e24) { if (! send_message(message)) { - errprint("JACK SysEx failed"); + errprint("JACK sysex failed"); } } } @@ -1181,13 +1270,13 @@ midi_jack::api_clock (midipulse tick) void midi_jack::send_byte (midibyte evbyte) { - midi_message message; + midi_message message; // e24->timestamp()); message.push(evbyte); if (jack_data().valid_buffer()) { if (! send_message(message)) { - errprint("JACK Send byte failed"); + errprint("JACK send byte failed"); } } } diff --git a/seq_rtmidi/src/midi_jack_info.cpp b/seq_rtmidi/src/midi_jack_info.cpp index 3f04a904e..be5fd77e7 100644 --- a/seq_rtmidi/src/midi_jack_info.cpp +++ b/seq_rtmidi/src/midi_jack_info.cpp @@ -24,7 +24,7 @@ * \library seq66 application * \author Chris Ahlstrom * \date 2017-01-01 - * \updates 2022-05-15 + * \updates 2022-09-06 * \license See above. * * This class is meant to collect a whole bunch of JACK information about @@ -159,7 +159,9 @@ midi_jack_info::midi_jack_info ) : midi_info (appname, ppqn, bpm), m_jack_ports (), - m_jack_client (nullptr) /* inited for connect() */ + m_jack_client (nullptr), /* inited for connect() */ + m_jack_buffer_size (0), + m_jack_sample_rate (0) { silence_jack_info(); m_jack_client = connect(); @@ -805,8 +807,11 @@ midi_jack_info::api_connect () bool result = not_nullptr(client_handle()); if (result) { + m_jack_buffer_size = jack_get_buffer_size(client_handle()); int rc = ::jack_activate(client_handle()); result = rc == 0; + if (result) + m_jack_sample_rate = jack_get_sample_rate(client_handle()); } if (result && rc().jack_auto_connect()) /* issue #60 */ { diff --git a/seq_rtmidi/src/rtmidi_types.cpp b/seq_rtmidi/src/rtmidi_types.cpp index 0a845956e..6d19faaf0 100644 --- a/seq_rtmidi/src/rtmidi_types.cpp +++ b/seq_rtmidi/src/rtmidi_types.cpp @@ -24,13 +24,15 @@ * \library seq66 application * \author Gary P. Scavone; severe refactoring by Chris Ahlstrom * \date 2016-12-01 - * \updates 2022-03-13 + * \updates 2022-09-07 * \license See above. * * Provides some basic types for the (heavily-factored) rtmidi library, very * loosely based on Gary Scavone's RtMidi library. */ +#include /* std::memcpy() */ + #include "rtmidi_types.hpp" /* seq66::rtmidi, etc. */ #include "util/basic_macros.hpp" /* errprintfunc() macro, etc. */ @@ -42,18 +44,128 @@ namespace seq66 { /* - * class midimessage + * class midi_message */ /** - * Constructs an empty MIDI message. + * Constructs an empty MIDI message. Well, empty except for the timestamp + * bytes. */ -midi_message::midi_message (double ts) : +midi_message::midi_message (double ts) : // WHY DOUBLE? m_bytes (), m_timestamp (ts) { - // Empty body +#if defined USE_ISSUE_100_FIX + (void) push_timestamp(midipulse(ts)); +#endif +} + +/** + * Also sets the timestamp member. + */ + +midi_message::midi_message (const midibyte * mbs, size_t sz) : + m_bytes (), +#if defined USE_ISSUE_100_FIX + m_timestamp (extract_timestamp(mbs, sz)) +#else + m_timestamp (0) +#endif +{ + for (size_t i = 0; i < sz; ++i) + { + m_bytes.push_back(*mbs++); + } +} + +void +midi_message::copy (midibyte * mbs, size_t sz) +{ + if (sz > 0) + { + const char * source = array(); + (void) std::memcpy(mbs, source, sz); + } +} + +/** + * These function handle adding a time-stamp to the message as bytes. + * The format of the midi_message is now: + * + * - 4 bytes of a long integer timestamp. + * - The rest of the message bytes. + * + * These implementations are based on midifile::write_long() and + * midifile::read_long(). + * + * Remember that the bytes are pushed to the back of the vector. + * + * The pop_timestamp() function removes the timestamp bytes, leaving the + * vector 4 bytes shorter, which will leave it with at least 1 byte. + * + * \return + * Returns true if the message was empty. Otherwise, we cannot push a + * timestamp. + */ + +bool +midi_message::push_timestamp (midipulse b) +{ + bool result = empty(); + if (result) + { + push((b & 0xFF000000) >> 24); + push((b & 0x00FF0000) >> 16); + push((b & 0x0000FF00) >> 8); + push((b & 0x000000FF)); + } + return result; +} + +midipulse +midi_message::pop_timestamp () +{ + midipulse result = extract_timestamp(); + container temp; + int c = count(); + for (int i = 4; i < c; ++i) + temp.push_back(m_bytes[i]); + + m_bytes = temp; + return result; +} + +midipulse +midi_message::extract_timestamp () const +{ + midipulse result = 0; + if (count() > 4) + { + result = m_bytes[0] << 24; + result += m_bytes[1] << 16; + result += m_bytes[2] << 8; + result += m_bytes[3]; + } + return result; +} + +/** + * Static function. + */ + +midipulse +midi_message::extract_timestamp (const midibyte * mbs, size_t sz) +{ + midipulse result = 0; + if (sz >= 4) + { + result = mbs[0] << 24; + result += mbs[1] << 16; + result += mbs[2] << 8; + result += mbs[3]; + } + return result; } /** @@ -70,7 +182,7 @@ midi_message::show () const } else { - fprintf(stderr, "midi_message:\n"); + fprintf(stderr, "midi_message (ts %ld):", long(timestamp())); for (auto c : m_bytes) { fprintf(stderr, " 0x%2x", int(c));