LCOV - code coverage report
Current view: top level - muse/framework/audio/engine/internal - abstracteventsequencer.h Coverage Total Hit
Test: filtered_coverage.info Lines: 0.0 % 102 0
Test Date: 2026-11-05 04:07:02

            Line data    Source code
       1              : /*
       2              :  * SPDX-License-Identifier: GPL-3.0-only
       3              :  * MuseScore-CLA-applies
       4              :  *
       5              :  * MuseScore Studio
       6              :  * Music Composition & Notation
       7              :  *
       8              :  * Copyright (C) 2025 MuseScore Limited and others
       9              :  *
      10              :  * This program is free software: you can redistribute it and/or modify
      11              :  * it under the terms of the GNU General Public License version 3 as
      12              :  * published by the Free Software Foundation.
      13              :  *
      14              :  * This program is distributed in the hope that it will be useful,
      15              :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      16              :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      17              :  * GNU General Public License for more details.
      18              :  *
      19              :  * You should have received a copy of the GNU General Public License
      20              :  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
      21              :  */
      22              : 
      23              : #pragma once
      24              : 
      25              : #include <map>
      26              : #include <set>
      27              : 
      28              : #include "global/async/asyncable.h"
      29              : #include "mpe/events.h"
      30              : 
      31              : #include "audio/common/audiosanitizer.h"
      32              : #include "audio/common/audiotypes.h"
      33              : 
      34              : namespace muse::audio::engine {
      35              : template<class ... Types>
      36              : class AbstractEventSequencer : public async::Asyncable
      37              : {
      38              : public:
      39              :     using EventType = std::variant<Types...>;
      40              :     using EventSequence = std::vector<EventType>;
      41              :     using EventSequenceMap = std::map<msecs_t, EventSequence>;
      42              : 
      43              :     typedef typename EventSequenceMap::const_iterator SequenceIterator;
      44              :     typedef typename EventSequence::const_iterator EventIterator;
      45              : 
      46            0 :     AbstractEventSequencer()
      47            0 :     {
      48            0 :         resetAllIterators();
      49            0 :     }
      50              : 
      51            0 :     virtual ~AbstractEventSequencer()
      52              :     {
      53            0 :         m_playbackData.mainStream.disconnect(this);
      54            0 :         m_playbackData.offStream.disconnect(this);
      55            0 :     }
      56              : 
      57            0 :     void load(const mpe::PlaybackData& data)
      58              :     {
      59              :         ONLY_AUDIO_ENGINE_THREAD;
      60              : 
      61            0 :         m_playbackData = data;
      62              : 
      63            0 :         m_playbackData.mainStream.onReceive(this, [this](const mpe::PlaybackEventsMap& events,
      64              :                                                          const mpe::DynamicAutomationLayers& dynamics) {
      65            0 :             m_playbackData.originEvents = events;
      66            0 :             m_playbackData.dynamics = dynamics;
      67            0 :             m_shouldUpdateMainStreamEvents = true;
      68              : 
      69            0 :             if (m_isActive || m_updateMainStreamWhenInactive) {
      70            0 :                 updateMainStream();
      71              :             }
      72              :         });
      73              : 
      74            0 :         m_playbackData.offStream.onReceive(this, [this](const mpe::PlaybackEventsMap& events,
      75              :                                                         bool flush) {
      76            0 :             if (flush) {
      77            0 :                 flushOffstream();
      78              :             }
      79              : 
      80            0 :             updateOffStreamEvents(events);
      81              :         });
      82              : 
      83            0 :         updateMainStreamEvents(data.originEvents, data.dynamics);
      84            0 :     }
      85              : 
      86            0 :     const mpe::PlaybackData& playbackData() const
      87              :     {
      88            0 :         return m_playbackData;
      89              :     }
      90              : 
      91            0 :     void updateMainStream()
      92              :     {
      93            0 :         if (m_shouldUpdateMainStreamEvents) {
      94            0 :             updateMainStreamEvents(m_playbackData.originEvents, m_playbackData.dynamics);
      95            0 :             m_shouldUpdateMainStreamEvents = false;
      96              :         }
      97            0 :     }
      98              : 
      99            0 :     void setUpdateMainStreamWhenInactive(bool update)
     100              :     {
     101            0 :         m_updateMainStreamWhenInactive = update;
     102            0 :     }
     103              : 
     104            0 :     void flushOffstream()
     105              :     {
     106            0 :         if (m_offStreamEvents.empty()) {
     107            0 :             return;
     108              :         }
     109              : 
     110            0 :         m_offStreamEvents.clear();
     111            0 :         updateOffSequenceIterator();
     112              : 
     113            0 :         if (m_onOffStreamFlushed) {
     114            0 :             m_onOffStreamFlushed();
     115              :         }
     116              :     }
     117              : 
     118            0 :     void setActive(const bool active)
     119              :     {
     120            0 :         if (m_isActive == active) {
     121            0 :             return;
     122              :         }
     123              : 
     124            0 :         m_isActive = active;
     125              : 
     126            0 :         if (m_isActive) {
     127            0 :             updateMainStream();
     128              :         }
     129              :     }
     130              : 
     131            0 :     bool isActive() const
     132              :     {
     133            0 :         return m_isActive;
     134              :     }
     135              : 
     136            0 :     void setPlaybackPosition(const msecs_t newPlaybackPosition)
     137              :     {
     138              :         ONLY_AUDIO_ENGINE_THREAD;
     139              : 
     140            0 :         m_playbackPosition = newPlaybackPosition;
     141            0 :         resetAllIterators();
     142            0 :     }
     143              : 
     144            0 :     msecs_t playbackPosition() const
     145              :     {
     146              :         ONLY_AUDIO_ENGINE_THREAD;
     147              : 
     148            0 :         return m_playbackPosition;
     149              :     }
     150              : 
     151              :     using OnFlushedCallback = std::function<void ()>;
     152              : 
     153            0 :     void setOnOffStreamFlushed(OnFlushedCallback flushed)
     154              :     {
     155              :         ONLY_AUDIO_ENGINE_THREAD;
     156              : 
     157            0 :         m_onOffStreamFlushed = flushed;
     158            0 :     }
     159              : 
     160              :     void setOnMainStreamFlushed(OnFlushedCallback flushed)
     161              :     {
     162              :         ONLY_AUDIO_ENGINE_THREAD;
     163              : 
     164              :         m_onMainStreamFlushed = flushed;
     165              :     }
     166              : 
     167            0 :     EventSequenceMap movePlaybackForward(const msecs_t nextMsecs)
     168              :     {
     169              :         ONLY_AUDIO_ENGINE_THREAD;
     170              : 
     171            0 :         EventSequenceMap result;
     172              : 
     173            0 :         if (!m_isActive) {
     174            0 :             result.emplace(m_offstreamPosition, EventSequence());
     175              : 
     176            0 :             if (m_currentOffSequenceIt == m_offStreamEvents.cend()) {
     177            0 :                 return result;
     178              :             }
     179              : 
     180            0 :             m_offstreamPosition += nextMsecs;
     181            0 :             handleOffStream(result);
     182              : 
     183            0 :             return result;
     184              :         }
     185              : 
     186              :         // Empty sequence means to continue the previous sequence
     187            0 :         result.emplace(m_playbackPosition, EventSequence());
     188            0 :         m_playbackPosition += nextMsecs;
     189              : 
     190            0 :         if (m_currentMainSequenceIt == m_mainStreamEvents.cend()) {
     191            0 :             return result;
     192              :         }
     193              : 
     194            0 :         handleMainStream(result);
     195              : 
     196            0 :         return result;
     197            0 :     }
     198              : 
     199              : protected:
     200              :     virtual void updateMainStreamEvents(const mpe::PlaybackEventsMap& events, const mpe::DynamicAutomationLayers& dynamics) = 0;
     201              :     virtual void updateOffStreamEvents(const mpe::PlaybackEventsMap& events) = 0;
     202              : 
     203            0 :     void resetAllIterators()
     204              :     {
     205            0 :         updateMainSequenceIterator();
     206            0 :         updateOffSequenceIterator();
     207            0 :     }
     208              : 
     209            0 :     void updateMainSequenceIterator()
     210              :     {
     211            0 :         m_currentMainSequenceIt = m_mainStreamEvents.lower_bound(m_playbackPosition);
     212            0 :     }
     213              : 
     214            0 :     void updateOffSequenceIterator()
     215              :     {
     216            0 :         m_currentOffSequenceIt = m_offStreamEvents.cbegin();
     217            0 :         m_offstreamPosition = 0;
     218            0 :     }
     219              : 
     220            0 :     void handleMainStream(EventSequenceMap& result)
     221              :     {
     222            0 :         while (m_currentMainSequenceIt != m_mainStreamEvents.end()
     223            0 :                && m_currentMainSequenceIt->first <= m_playbackPosition) {
     224            0 :             EventSequence& sequence = result[m_currentMainSequenceIt->first];
     225            0 :             sequence.insert(sequence.end(), m_currentMainSequenceIt->second.cbegin(),
     226            0 :                             m_currentMainSequenceIt->second.cend());
     227            0 :             m_currentMainSequenceIt = std::next(m_currentMainSequenceIt);
     228              :         }
     229            0 :     }
     230              : 
     231            0 :     void handleOffStream(EventSequenceMap& result)
     232              :     {
     233            0 :         if (m_offStreamEvents.empty()) {
     234            0 :             return;
     235              :         }
     236              : 
     237            0 :         while (m_currentOffSequenceIt != m_offStreamEvents.end()
     238            0 :                && m_currentOffSequenceIt->first <= m_offstreamPosition) {
     239            0 :             EventSequence& sequence = result[m_currentOffSequenceIt->first];
     240            0 :             sequence.insert(sequence.end(), std::make_move_iterator(m_currentOffSequenceIt->second.begin()),
     241            0 :                             std::make_move_iterator(m_currentOffSequenceIt->second.end()));
     242            0 :             m_currentOffSequenceIt = m_offStreamEvents.erase(m_currentOffSequenceIt);
     243              :         }
     244              : 
     245            0 :         if (m_currentOffSequenceIt == m_offStreamEvents.end()) {
     246            0 :             m_offstreamPosition = 0;
     247              :         }
     248              :     }
     249              : 
     250              :     mutable msecs_t m_playbackPosition = 0;
     251              :     mutable msecs_t m_offstreamPosition = 0;
     252              : 
     253              :     SequenceIterator m_currentMainSequenceIt;
     254              :     SequenceIterator m_currentOffSequenceIt;
     255              :     EventSequenceMap m_mainStreamEvents;
     256              :     EventSequenceMap m_offStreamEvents;
     257              : 
     258              :     mpe::PlaybackData m_playbackData;
     259              : 
     260              :     bool m_isActive = false;
     261              : 
     262              :     OnFlushedCallback m_onOffStreamFlushed;
     263              :     OnFlushedCallback m_onMainStreamFlushed;
     264              : 
     265              : private:
     266              :     bool m_shouldUpdateMainStreamEvents = false;
     267              :     bool m_updateMainStreamWhenInactive = false;
     268              : };
     269              : }
        

Generated by: LCOV version 2.0-1