From ab0e2aded1a8880e742fb8e1ee9a9ce57a2c4e38 Mon Sep 17 00:00:00 2001 From: Tim Janik Date: Wed, 14 Feb 2024 01:01:20 +0100 Subject: [PATCH 1/9] ase/memory.cc: avoid constexpr std::string use that needs clang++-17 Signed-off-by: Tim Janik --- ase/memory.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ase/memory.cc b/ase/memory.cc index 2789bbfe..6ece2ab1 100644 --- a/ase/memory.cc +++ b/ase/memory.cc @@ -567,9 +567,9 @@ class CStringTable { StrPtrMap quarks_; std::vector strings_; std::shared_mutex mutex_; - static constexpr String empty_string; CStringTable() { + static String empty_string; strings_ = { &empty_string }; // ID==0 quarks_[&empty_string] = 0; } @@ -608,7 +608,7 @@ CStringTable::lookup (uint quark) noexcept const std::unique_lock ulock (mutex_); if (quark < strings_.size()) [[likely]] return *strings_[quark]; - return empty_string; + return *strings_[0]; // empty_string; } /// Assign a std::string to a CString, after deduplication, its memory is never released. From f4d776fc36c7d2754c6668c88f061dcc4eb74aac Mon Sep 17 00:00:00 2001 From: Stefan Westerfeld Date: Tue, 13 Feb 2024 16:27:56 +0100 Subject: [PATCH 2/9] ASE: midievent: avoid negative frame offsets in midi events Use unsigned data type for midi event offset, see issue #26. Signed-off-by: Stefan Westerfeld --- ase/midievent.cc | 21 ++++++++++++--------- ase/midievent.hh | 4 ++-- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/ase/midievent.cc b/ase/midievent.cc index 7b18adf7..d062682e 100644 --- a/ase/midievent.cc +++ b/ase/midievent.cc @@ -50,32 +50,32 @@ MidiEvent::to_string () const switch (type) { case PARAM_VALUE: - return string_format ("%+4d ch=%-2u %s param=%d pvalue=%.5f", + return string_format ("%4u ch=%-2u %s param=%d pvalue=%.5f", frame, channel, "PARAM_VALUE", param, pvalue); case NOTE_OFF: if (!et) et = "NOTE_OFF"; /* fall-through */ case NOTE_ON: if (!et) et = "NOTE_ON"; /* fall-through */ case AFTERTOUCH: if (!et) et = "AFTERTOUCH"; - return string_format ("%+4d ch=%-2u %-10s pitch=%d vel=%f tune=%f id=%x", + return string_format ("%4u ch=%-2u %-10s pitch=%d vel=%f tune=%f id=%x", frame, channel, et, key, velocity, tuning, noteid); case CONTROL_CHANGE: if (!et) et = "CONTROL_CHANGE"; - return string_format ("%+4d ch=%-2u %s control=%d value=%f (%02x) {%u}", + return string_format ("%4u ch=%-2u %s control=%d value=%f (%02x) {%u}", frame, channel, et, param, value, cval, fragment); case PROGRAM_CHANGE: if (!et) et = "PROGRAM_CHANGE"; - return string_format ("%+4d ch=%-2u %s program=%d", + return string_format ("%4u ch=%-2u %s program=%d", frame, channel, et, param); case CHANNEL_PRESSURE: if (!et) et = "CHANNEL_PRESSURE"; /* fall-through */ case PITCH_BEND: if (!et) et = "PITCH_BEND"; - return string_format ("%+4d ch=%-2u %s value=%+f", + return string_format ("%4u ch=%-2u %s value=%+f", frame, channel, et, value); case SYSEX: if (!et) et = "SYSEX"; - return string_format ("%+4d %s (unhandled)", frame, et); + return string_format ("%4u %s (unhandled)", frame, et); } static_assert (sizeof (MidiEvent) >= 2 * sizeof (uint64_t)); const uint64_t *uu = reinterpret_cast (this); - return string_format ("%+4d MidiEvent-%u (%08x %08x)", frame, type, uu[0], uu[1]); + return string_format ("%4u MidiEvent-%u (%08x %08x)", frame, type, uu[0], uu[1]); } MidiEvent @@ -190,7 +190,10 @@ MidiEventOutput::append (int16_t frame, const MidiEvent &event) bool MidiEventOutput::append_unsorted (int16_t frame, const MidiEvent &event) { - const int64_t last_event_stamp = !events_.empty() ? events_.back().frame : -2048; + // we discard timing information by ignoring negative frame offsets here (#26) + // when we implement recording, we might want to preserve the exact timestamp + frame = std::max (frame, 0); + const int64_t last_event_stamp = !events_.empty() ? events_.back().frame : 0; events_.push_back (event); events_.back().frame = frame; return frame < last_event_stamp; @@ -209,7 +212,7 @@ MidiEventOutput::ensure_order () int64_t MidiEventOutput::last_frame () const { - return !events_.empty() ? events_.back().frame : -2048; + return !events_.empty() ? events_.back().frame : 0; } } // Ase diff --git a/ase/midievent.hh b/ase/midievent.hh index 6d684215..6c464e6f 100644 --- a/ase/midievent.hh +++ b/ase/midievent.hh @@ -49,8 +49,8 @@ enum class MidiMessage : int32_t { /// MidiEvent data structure. struct MidiEvent { using enum MidiEventType; - static_assert (AUDIO_BLOCK_MAX_RENDER_SIZE <= 2048); // -2048…+2047 fits frame - int frame : 12; ///< Offset into current block, delayed if negative + static_assert (AUDIO_BLOCK_MAX_RENDER_SIZE <= 4096); // 0…+4095 fits frame + uint frame : 12; ///< Offset into current block, delayed if negative uint channel : 4; ///< 0…15 for standard events MidiEventType type; ///< MidiEvent type, one of the MidiEventType members union { From d634c2ea65408790770e820f7ee37e1155a1fe35 Mon Sep 17 00:00:00 2001 From: Stefan Westerfeld Date: Tue, 13 Feb 2024 16:29:12 +0100 Subject: [PATCH 3/9] ASE: clapplugin.cc: midi event frame offsets are now always positive (#26) Signed-off-by: Stefan Westerfeld --- ase/clapplugin.cc | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/ase/clapplugin.cc b/ase/clapplugin.cc index 48f9e9b1..1f483c31 100644 --- a/ase/clapplugin.cc +++ b/ase/clapplugin.cc @@ -555,21 +555,21 @@ ClapAudioProcessor::convert_clap_events (const clap_process_t &process, const bo case MidiMessage::NOTE_OFF: case MidiMessage::AFTERTOUCH: if (as_clapnotes && ev.type == MidiEvent::AFTERTOUCH) { - expr = setup_expression (&input_events_[j++], MAX (ev.frame, 0), 0); + expr = setup_expression (&input_events_[j++], ev.frame, 0); expr->expression_id = CLAP_NOTE_EXPRESSION_PRESSURE; expr->note_id = ev.noteid; expr->channel = ev.channel; expr->key = ev.key; expr->value = ev.velocity; } else if (as_clapnotes) { - evnote = setup_evnote (&input_events_[j++], MAX (ev.frame, 0), 0); + evnote = setup_evnote (&input_events_[j++], ev.frame, 0); evnote->header.type = ev.type == MidiEvent::NOTE_ON ? CLAP_EVENT_NOTE_ON : CLAP_EVENT_NOTE_OFF; evnote->note_id = ev.noteid; evnote->channel = ev.channel; evnote->key = ev.key; evnote->velocity = ev.velocity; } else { - midi1 = setup_midi1 (&input_events_[j++], MAX (ev.frame, 0), 0); + midi1 = setup_midi1 (&input_events_[j++], ev.frame, 0); midi1->data[0] = uint8_t (ev.type) | (ev.channel & 0xf); midi1->data[1] = ev.key; midi1->data[2] = std::min (uint8_t (ev.velocity * 127), uint8_t (127)); @@ -577,33 +577,33 @@ ClapAudioProcessor::convert_clap_events (const clap_process_t &process, const bo break; case MidiMessage::ALL_NOTES_OFF: if (as_clapnotes) { - evnote = setup_evnote (&input_events_[j++], MAX (ev.frame, 0), 0); + evnote = setup_evnote (&input_events_[j++], ev.frame, 0); evnote->header.type = CLAP_EVENT_NOTE_CHOKE; evnote->note_id = -1; evnote->channel = -1; evnote->key = -1; evnote->velocity = 0; } else { - midi1 = setup_midi1 (&input_events_[j++], MAX (ev.frame, 0), 0); + midi1 = setup_midi1 (&input_events_[j++], ev.frame, 0); midi1->data[0] = 0xB0 | (ev.channel & 0xf); midi1->data[1] = 123; midi1->data[2] = 0; } break; case MidiMessage::CONTROL_CHANGE: - midi1 = setup_midi1 (&input_events_[j++], MAX (ev.frame, 0), 0); + midi1 = setup_midi1 (&input_events_[j++], ev.frame, 0); midi1->data[0] = 0xB0 | (ev.channel & 0xf); midi1->data[1] = ev.param; midi1->data[2] = ev.cval; break; case MidiMessage::CHANNEL_PRESSURE: - midi1 = setup_midi1 (&input_events_[j++], MAX (ev.frame, 0), 0); + midi1 = setup_midi1 (&input_events_[j++], ev.frame, 0); midi1->data[0] = 0xD0 | (ev.channel & 0xf); midi1->data[1] = std::min (uint8_t (ev.velocity * 127), uint8_t (127)); midi1->data[2] = 0; break; case MidiMessage::PITCH_BEND: - midi1 = setup_midi1 (&input_events_[j++], MAX (ev.frame, 0), 0); + midi1 = setup_midi1 (&input_events_[j++], ev.frame, 0); midi1->data[0] = 0xE0 | (ev.channel & 0xf); midi1->data[1] = std::min (uint8_t (ev.velocity * 127), uint8_t (127)); midi1->data[2] = 0; From 05013a22d690c4f4f1ade9e74474bd474047244d Mon Sep 17 00:00:00 2001 From: Stefan Westerfeld Date: Tue, 13 Feb 2024 16:29:36 +0100 Subject: [PATCH 4/9] ASE: midilib.cc: midi event frame offsets are now always positive (#26) Signed-off-by: Stefan Westerfeld --- ase/midilib.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ase/midilib.cc b/ase/midilib.cc index ad47ca97..4310a9dd 100644 --- a/ase/midilib.cc +++ b/ase/midilib.cc @@ -146,7 +146,7 @@ class MidiProducerImpl : public MidiProducerIface { TickEvent tnote = future_stack.back(); future_stack.pop_back(); const int64 frame = transport.sample_from_tick (tnote.tick - begin_tick); - assert_paranoid (frame >= -2048 && frame <= 2047); + assert_paranoid (frame >= 0 && frame <= 4095); MDEBUG ("POP: t=%d ev=%s f=%d\n", tnote.tick, tnote.event.to_string(), frame); evout.append_unsorted (frame, tnote.event); } @@ -171,7 +171,7 @@ class MidiProducerImpl : public MidiProducerIface { if (etick < end_tick) { const int64 frame = transport.sample_from_tick (etick - begin_tick); - assert_paranoid (frame >= -2048 && frame <= 2047); + assert_paranoid (frame >= 0 && frame <= 4095); // interleave with earlier MIDI through events evout.append_unsorted (frame, event); MDEBUG ("NOW: t=%d ev=%s f=%d\n", etick, event.to_string(), frame); From d051061a34743cefaac713e8a4ace43739f1bf76 Mon Sep 17 00:00:00 2001 From: Stefan Westerfeld Date: Tue, 13 Feb 2024 16:31:01 +0100 Subject: [PATCH 5/9] devices/blepsynth/blepsynth.cc: midi event frame offsets are now unsigned Signed-off-by: Stefan Westerfeld --- devices/blepsynth/blepsynth.cc | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/devices/blepsynth/blepsynth.cc b/devices/blepsynth/blepsynth.cc index ef8374e5..c5bb5442 100644 --- a/devices/blepsynth/blepsynth.cc +++ b/devices/blepsynth/blepsynth.cc @@ -996,11 +996,9 @@ class BlepSynth : public AudioProcessor { MidiEventInput evinput = midi_event_input(); for (const auto &ev : evinput) { - uint frame = std::max (ev.frame, 0); // TODO: should be unsigned anyway, issue #26 - // process any audio that is before the event - render_audio (left_out + offset, right_out + offset, frame - offset); - offset = frame; + render_audio (left_out + offset, right_out + offset, ev.frame - offset); + offset = ev.frame; switch (ev.message()) { From c4ee7f45cc4fcacf90ad41ec47d950ca10985ce5 Mon Sep 17 00:00:00 2001 From: Stefan Westerfeld Date: Tue, 13 Feb 2024 16:31:22 +0100 Subject: [PATCH 6/9] devices/liquidsfz/liquidsfz.cc: midi event frame offsets are now unsigned Signed-off-by: Stefan Westerfeld --- devices/liquidsfz/liquidsfz.cc | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/devices/liquidsfz/liquidsfz.cc b/devices/liquidsfz/liquidsfz.cc index 4ba88d7f..decdf111 100644 --- a/devices/liquidsfz/liquidsfz.cc +++ b/devices/liquidsfz/liquidsfz.cc @@ -182,14 +182,13 @@ class LiquidSFZ : public AudioProcessor { MidiEventInput evinput = midi_event_input(); for (const auto &ev : evinput) { - const int time_stamp = std::max (ev.frame, 0); switch (ev.message()) { case MidiMessage::NOTE_OFF: - synth_.add_event_note_off (time_stamp, ev.channel, ev.key); + synth_.add_event_note_off (ev.frame, ev.channel, ev.key); break; case MidiMessage::NOTE_ON: - synth_.add_event_note_on (time_stamp, ev.channel, ev.key, std::clamp (irintf (ev.velocity * 127), 0, 127)); + synth_.add_event_note_on (ev.frame, ev.channel, ev.key, std::clamp (irintf (ev.velocity * 127), 0, 127)); break; case MidiMessage::ALL_NOTES_OFF: case MidiMessage::ALL_SOUND_OFF: From c4483c7440444ae4a4dbf0785ea929fe21a63f65 Mon Sep 17 00:00:00 2001 From: Stefan Westerfeld Date: Tue, 13 Feb 2024 16:31:42 +0100 Subject: [PATCH 7/9] devices/saturation/saturation.cc: midi event frame offsets are now unsigned Signed-off-by: Stefan Westerfeld --- devices/saturation/saturation.cc | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/devices/saturation/saturation.cc b/devices/saturation/saturation.cc index fdbdd8e2..b175535c 100644 --- a/devices/saturation/saturation.cc +++ b/devices/saturation/saturation.cc @@ -91,11 +91,9 @@ class Saturation : public AudioProcessor { MidiEventInput evinput = midi_event_input(); for (const auto &ev : evinput) { - uint frame = std::max (ev.frame, 0); // TODO: should be unsigned anyway, issue #26 - // process any audio that is before the event - render_audio (left_in + offset, right_in + offset, left_out + offset, right_out + offset, frame - offset); - offset = frame; + render_audio (left_in + offset, right_in + offset, left_out + offset, right_out + offset, ev.frame - offset); + offset = ev.frame; switch (ev.message()) { From 966f87f159a82f8d3e2a4e8dc5987050e3674f57 Mon Sep 17 00:00:00 2001 From: Stefan Westerfeld Date: Tue, 13 Feb 2024 16:41:33 +0100 Subject: [PATCH 8/9] ASE: midievent.hh: fix clang tidy problem (add typename) Signed-off-by: Stefan Westerfeld --- ase/midievent.hh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ase/midievent.hh b/ase/midievent.hh index 6c464e6f..6c760ee3 100644 --- a/ase/midievent.hh +++ b/ase/midievent.hh @@ -118,7 +118,7 @@ class MidiEventReader : QueueMultiplexer::const using Base = QueueMultiplexer::const_iterator>; ASE_CLASS_NON_COPYABLE (MidiEventReader); public: - using iterator = Base::iterator; + using iterator = typename Base::iterator; using Base::assign; size_t events_pending () const { return this->count_pending(); } iterator begin () { return this->Base::begin(); } From 25c0f8acecd880ea6f152cdb12580330dbdb7f31 Mon Sep 17 00:00:00 2001 From: Stefan Westerfeld Date: Tue, 13 Feb 2024 16:43:43 +0100 Subject: [PATCH 9/9] devices/liquidsfz/liquidsfz.cc: avoid using printf (use printerr instead) Signed-off-by: Stefan Westerfeld --- devices/liquidsfz/liquidsfz.cc | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/devices/liquidsfz/liquidsfz.cc b/devices/liquidsfz/liquidsfz.cc index decdf111..32db0b37 100644 --- a/devices/liquidsfz/liquidsfz.cc +++ b/devices/liquidsfz/liquidsfz.cc @@ -35,10 +35,9 @@ class LiquidSFZLoader { if (want_sfz_ != have_sfz_) { - printf ("LiquidSFZ: loading %s...", want_sfz_.c_str()); - fflush (stdout); + printerr ("LiquidSFZ: loading %s...", want_sfz_.c_str()); bool result = synth_.load (want_sfz_); - printf ("%s\n", result ? "OK" : "FAIL"); + printerr ("%s\n", result ? "OK" : "FAIL"); // TODO: handle load error have_sfz_ = want_sfz_; @@ -51,7 +50,7 @@ class LiquidSFZLoader state_.store (STATE_IDLE); } } - printf ("run() done\n"); + printerr ("LiquidSFZ: run() done\n"); } public: LiquidSFZLoader (Synth &synth) : @@ -59,14 +58,14 @@ class LiquidSFZLoader { thread_ = std::thread (&LiquidSFZLoader::run, this); want_sfz_.reserve (4096); // avoid allocations in audio thread - printf ("LiquidSFZLoader()\n"); + printerr ("LiquidSFZLoader()\n"); } ~LiquidSFZLoader() { quit_.store (1); sem_.post(); thread_.join(); - printf ("~LiquidSFZLoader()\n"); + printerr ("~LiquidSFZLoader()\n"); } // called from audio thread bool