Zrythm v2.0.0-DEV
a highly automated and intuitive digital audio workstation
Loading...
Searching...
No Matches
midi_playback_cache.h
1// SPDX-FileCopyrightText: © 2025 Alexandros Theodotou <alex@zrythm.org>
2// SPDX-License-Identifier: LicenseRef-ZrythmLicense
3
4#pragma once
5
6#include "juce_wrapper.h"
7
8namespace zrythm::dsp
9{
11{
12public:
13 using IntervalType = std::pair<int64_t, int64_t>;
14
15 void clear ()
16 {
17 event_sequences_.clear ();
18 merged_events_.clear ();
19 }
20
21 void remove_sequences_matching_interval (IntervalType interval)
22 {
23 const auto [target_start, target_end] = interval;
24
25 std::erase_if (event_sequences_, [&] (const auto &entry) {
26 const auto &[existing_interval, seq] = entry;
27 const auto [existing_start, existing_end] = existing_interval;
28 // Remove sequences that overlap with any part of the target interval
29 return !(existing_end < target_start || existing_start > target_end);
30 });
31 }
32
33 void
34 add_sequence (IntervalType interval, const juce::MidiMessageSequence &sequence)
35 {
36 const auto [start_time, end_time] = interval;
37
38 // Validate that all events are within the interval
39 for (const auto * event : sequence)
40 {
41 const double event_time = event->message.getTimeStamp ();
42 if (
43 event_time < static_cast<double> (start_time)
44 || event_time > static_cast<double> (end_time))
45 {
46 throw std::invalid_argument (
47 "MIDI sequence contains events outside the specified interval");
48 }
49 }
50
51 // Ensure the sequence has no unended notes
52 juce::MidiMessageSequence validated_sequence (sequence);
53 validated_sequence.updateMatchedPairs ();
54
55 // Check for unended notes and add note-off events at the interval end time
56 for (const auto * event : validated_sequence)
57 {
58 if (event->message.isNoteOn () && event->noteOffObject == nullptr)
59 {
60 // Add a note-off event at the end of the interval
61 validated_sequence.addEvent (
62 juce::MidiMessage::noteOff (
63 event->message.getChannel (), event->message.getNoteNumber ()),
64 static_cast<double> (end_time));
65 }
66 }
67
68 validated_sequence.updateMatchedPairs ();
69 event_sequences_[interval] = validated_sequence;
70 }
71
72 void finalize_changes ()
73 {
74 merged_events_.clear ();
75 for (const auto &[interval, seq] : event_sequences_)
76 {
77 merged_events_.addSequence (seq, 0);
78 merged_events_.updateMatchedPairs ();
79 }
80 }
81
82 const auto &cached_events () const { return merged_events_; }
83
84private:
88 std::map<IntervalType, juce::MidiMessageSequence> event_sequences_;
89 juce::MidiMessageSequence merged_events_;
90};
91}