diff --git a/src/appshell/view/appmenumodel.cpp b/src/appshell/view/appmenumodel.cpp index 18e2956043d46..79f3501d371fa 100644 --- a/src/appshell/view/appmenumodel.cpp +++ b/src/appshell/view/appmenumodel.cpp @@ -422,6 +422,7 @@ MenuItem* AppMenuModel::makeDiagnosticsMenu() MenuItemList items { makeMenuItem("diagnostic-save-diagnostic-files"), + makeMenuItem("playback-reload-cache"), makeMenu(TranslatableString("appshell/menu/diagnostics", "&System"), systemItems, "menu-system") }; diff --git a/src/engraving/dom/cmd.cpp b/src/engraving/dom/cmd.cpp index 0e0ccba4ac5d2..ddeafdf188040 100644 --- a/src/engraving/dom/cmd.cpp +++ b/src/engraving/dom/cmd.cpp @@ -4300,6 +4300,12 @@ void Score::cmdRealizeChordSymbols(bool literal, Voicing voicing, HDuration dura note->setNval(nval, tick); } + if (!seg->isChordRestType()) { + Segment* newCrSeg = seg->measure()->undoGetSegment(SegmentType::ChordRest, seg->tick()); + newCrSeg->setTicks(seg->ticks()); + seg = newCrSeg; + } + setChord(this, seg, h->track(), chord, duration); //add chord using template delete chord; } diff --git a/src/engraving/dom/edit.cpp b/src/engraving/dom/edit.cpp index 956806a07723a..5e14b4cf3ef80 100644 --- a/src/engraving/dom/edit.cpp +++ b/src/engraving/dom/edit.cpp @@ -2971,6 +2971,7 @@ void Score::deleteItem(EngravingItem* el) case ElementType::STEM_SLASH: // cannot delete this elements case ElementType::HOOK: + case ElementType::GUITAR_BEND_TEXT: LOGD("cannot remove %s", el->typeName()); break; diff --git a/src/engraving/dom/fret.cpp b/src/engraving/dom/fret.cpp index cc8e4c7409555..d5b9b7f13007e 100644 --- a/src/engraving/dom/fret.cpp +++ b/src/engraving/dom/fret.cpp @@ -896,6 +896,15 @@ PropertyValue FretDiagram::propertyDefault(Pid pid) const return EngravingItem::propertyDefault(pid); } +void FretDiagram::setTrack(track_idx_t val) +{ + EngravingItem::setTrack(val); + + if (m_harmony) { + m_harmony->setTrack(val); + } +} + //--------------------------------------------------------- // endEditDrag //--------------------------------------------------------- diff --git a/src/engraving/dom/fret.h b/src/engraving/dom/fret.h index efdf9da41252d..9497cbd73d442 100644 --- a/src/engraving/dom/fret.h +++ b/src/engraving/dom/fret.h @@ -235,6 +235,8 @@ class FretDiagram final : public EngravingItem bool setProperty(Pid propertyId, const PropertyValue&) override; PropertyValue propertyDefault(Pid) const override; + void setTrack(track_idx_t val) override; + String accessibleInfo() const override; String screenReaderInfo() const override; diff --git a/src/engraving/dom/guitarbend.h b/src/engraving/dom/guitarbend.h index 73716cc8f74dd..2cc78b23fc1fa 100644 --- a/src/engraving/dom/guitarbend.h +++ b/src/engraving/dom/guitarbend.h @@ -251,6 +251,8 @@ class GuitarBendText final : public TextBase public: GuitarBendText(GuitarBendSegment* parent); GuitarBendText* clone() const override { return new GuitarBendText(*this); } + + bool isEditable() const override { return false; } }; } // namespace mu::engraving diff --git a/src/engraving/dom/harmony.cpp b/src/engraving/dom/harmony.cpp index 79832a26e02f1..b57472368cd08 100644 --- a/src/engraving/dom/harmony.cpp +++ b/src/engraving/dom/harmony.cpp @@ -849,9 +849,14 @@ Fraction Harmony::ticksTillNext(int utick, bool stopAtMeasureEnd) const { Segment* seg = getParentSeg(); Fraction duration = seg->ticks(); - Segment* cur = seg->next(); - auto rsIt = score()->repeatList().findRepeatSegmentFromUTick(utick); + const RepeatList& repeats = score()->repeatList(); + auto rsIt = repeats.findRepeatSegmentFromUTick(utick); + if (rsIt == repeats.cend()) { + return duration; + } + + Segment* cur = seg->next(); Measure const* currentMeasure = seg->measure(); Measure const* endMeasure = (stopAtMeasureEnd) ? currentMeasure : (*rsIt)->lastMeasure(); Harmony const* nextHarmony = nullptr; @@ -892,9 +897,9 @@ Fraction Harmony::ticksTillNext(int utick, bool stopAtMeasureEnd) const // End of repeatSegment or search boundary reached if (stopAtMeasureEnd) { break; - } else { + } else if (!nextHarmony) { // move to next RepeatSegment - if (++rsIt != score()->repeatList().end()) { + if (++rsIt != repeats.end()) { currentMeasure = (*rsIt)->firstMeasure(); endMeasure = (*rsIt)->lastMeasure(); cur = currentMeasure->first(); @@ -903,6 +908,12 @@ Fraction Harmony::ticksTillNext(int utick, bool stopAtMeasureEnd) const } } while ((nextHarmony == nullptr) && (cur != nullptr)); + if (nextHarmony && rsIt != repeats.end()) { + int tickOffset = (*rsIt)->utick - (*rsIt)->tick; + int nextHarmonyUtick = nextHarmony->tick().ticks() + tickOffset; + duration = Fraction::fromTicks(nextHarmonyUtick - utick); + } + return duration; } @@ -1062,9 +1073,12 @@ const ChordDescription* Harmony::getDescription(const String& name, const Parsed const RealizedHarmony& Harmony::getRealizedHarmony() const { - Fraction tick = this->tick(); const Staff* st = staff(); + IF_ASSERT_FAILED(st) { + return m_realizedHarmony; + } + const Fraction tick = this->tick(); const CapoParams& capo = st->capo(tick); int offset = 0; @@ -1080,7 +1094,7 @@ const RealizedHarmony& Harmony::getRealizedHarmony() const //Adjust for Nashville Notation, might be temporary // TODO: set dirty on add/remove of keysig if (m_harmonyType == HarmonyType::NASHVILLE && !m_realizedHarmony.valid()) { - Key key = staff()->key(tick); + Key key = st->key(tick); //parse root int rootTpc = function2Tpc(m_function, key); diff --git a/src/engraving/dom/repeatlist.cpp b/src/engraving/dom/repeatlist.cpp index a0efba53f7af9..cd0f5a44c3031 100644 --- a/src/engraving/dom/repeatlist.cpp +++ b/src/engraving/dom/repeatlist.cpp @@ -298,10 +298,14 @@ int RepeatList::utime2utick(double secs) const /// std::vector::const_iterator RepeatList::findRepeatSegmentFromUTick(int utick) const { - return std::lower_bound(this->cbegin(), this->cend(), utick, [](RepeatSegment const* rs, int utick) { - // Skip RS where endtick is less than us - return utick > (rs->utick + rs->len()); - }); + for (auto it = cbegin(); it != cend(); ++it) { + const RepeatSegment* seg = *it; + if (utick >= seg->utick && utick < seg->utick + seg->len()) { + return it; + } + } + + return cend(); } //--------------------------------------------------------- diff --git a/src/engraving/dom/score.cpp b/src/engraving/dom/score.cpp index 3631ddb196060..6ea3ae83e3767 100644 --- a/src/engraving/dom/score.cpp +++ b/src/engraving/dom/score.cpp @@ -554,7 +554,7 @@ void Score::rebuildTempoAndTimeSigMaps(Measure* measure, std::optionaladdTimeSig(ts); } } - } else if (segment.isChordRestType()) { + } else if (segment.isChordRestType() || segment.isTimeTickType()) { if (!isMaster()) { continue; } diff --git a/src/engraving/dom/types.h b/src/engraving/dom/types.h index 1352ea4d5bb6e..c9aecfd973c1d 100644 --- a/src/engraving/dom/types.h +++ b/src/engraving/dom/types.h @@ -553,6 +553,23 @@ struct ScoreChangesRange { { return isValidBoundary() || !changedTypes.empty(); } + + void clear() + { + *this = ScoreChangesRange(); + } + + void combine(const ScoreChangesRange& r) + { + tickFrom = std::min(tickFrom, r.tickFrom); + tickTo = std::max(tickTo, r.tickTo); + staffIdxFrom = std::min(staffIdxFrom, r.staffIdxFrom); + staffIdxTo = std::max(staffIdxTo, r.staffIdxTo); + changedItems.insert(r.changedItems.begin(), r.changedItems.end()); + changedTypes.insert(r.changedTypes.begin(), r.changedTypes.end()); + changedPropertyIdSet.insert(r.changedPropertyIdSet.begin(), r.changedPropertyIdSet.end()); + changedStyleIdSet.insert(r.changedStyleIdSet.begin(), r.changedStyleIdSet.end()); + } }; } // namespace mu::engraving diff --git a/src/engraving/playback/playbackeventsrenderer.cpp b/src/engraving/playback/playbackeventsrenderer.cpp index d5f4446cdc153..15fc98f31812a 100644 --- a/src/engraving/playback/playbackeventsrenderer.cpp +++ b/src/engraving/playback/playbackeventsrenderer.cpp @@ -116,6 +116,11 @@ void PlaybackEventsRenderer::renderChordSymbol(const Harmony* chordSymbol, return; } + const Staff* staff = chordSymbol->staff(); + IF_ASSERT_FAILED(staff) { + return; + } + const RealizedHarmony& realized = chordSymbol->getRealizedHarmony(); const RealizedHarmony::PitchMap& notes = realized.notes(); @@ -130,7 +135,7 @@ void PlaybackEventsRenderer::renderChordSymbol(const Harmony* chordSymbol, voice_layer_idx_t voiceIdx = static_cast(chordSymbol->voice()); staff_layer_idx_t staffIdx = static_cast(chordSymbol->staffIdx()); - Key key = chordSymbol->staff()->key(chordSymbol->tick()); + Key key = staff->key(chordSymbol->tick()); ArticulationMap articulations = makeStandardArticulationMap(profile, eventTimestamp, duration); @@ -161,6 +166,11 @@ void PlaybackEventsRenderer::renderChordSymbol(const Harmony* chordSymbol, const return; } + const Staff* staff = chordSymbol->staff(); + IF_ASSERT_FAILED(staff) { + return; + } + const RealizedHarmony& realized = chordSymbol->getRealizedHarmony(); const RealizedHarmony::PitchMap& notes = realized.notes(); @@ -168,8 +178,7 @@ void PlaybackEventsRenderer::renderChordSymbol(const Harmony* chordSymbol, const voice_layer_idx_t voiceIdx = static_cast(chordSymbol->voice()); staff_layer_idx_t staffIdx = static_cast(chordSymbol->staffIdx()); - - Key key = chordSymbol->staff()->key(chordSymbol->tick()); + Key key = staff->key(chordSymbol->tick()); ArticulationMap articulations = makeStandardArticulationMap(profile, actualTimestamp, actualDuration); diff --git a/src/engraving/playback/playbackmodel.cpp b/src/engraving/playback/playbackmodel.cpp index 5b5e38f506e6f..fff670a286faf 100644 --- a/src/engraving/playback/playbackmodel.cpp +++ b/src/engraving/playback/playbackmodel.cpp @@ -385,6 +385,10 @@ void PlaybackModel::processSegment(const int tickPositionOffset, const Segment* collectChangesTracks(trackId, trackChanges); } + if (segment->isTimeTickType()) { + return; // optimization: search only for annotations + } + for (const EngravingItem* item : segment->elist()) { if (!item || !item->isChordRest() || !item->part()) { continue; @@ -457,7 +461,7 @@ void PlaybackModel::processMeasureRepeat(const int tickPositionOffset, const Mea bool isFirstSegmentOfRepeatedMeasure = true; for (const Segment* seg = referringMeasure->first(); seg; seg = seg->next()) { - if (!seg->isChordRestType()) { + if (!seg->isChordRestType() && !seg->isTimeTickType()) { continue; } @@ -497,7 +501,7 @@ void PlaybackModel::updateEvents(const int tickFrom, const int tickTo, const tra bool isFirstSegmentOfMeasure = true; for (Segment* segment = measure->first(); segment; segment = segment->next()) { - if (!segment->isChordRestType()) { + if (!segment->isChordRestType() && !segment->isTimeTickType()) { continue; } diff --git a/src/framework/audio/internal/worker/eventaudiosource.cpp b/src/framework/audio/internal/worker/eventaudiosource.cpp index 39ebb56abe111..994d59c424ab4 100644 --- a/src/framework/audio/internal/worker/eventaudiosource.cpp +++ b/src/framework/audio/internal/worker/eventaudiosource.cpp @@ -68,6 +68,10 @@ void EventAudioSource::setIsActive(const bool active) return; } + if (m_synth->isActive() == active) { + return; + } + m_synth->setIsActive(active); m_synth->flushSound(); } diff --git a/src/framework/audio/internal/worker/mixer.cpp b/src/framework/audio/internal/worker/mixer.cpp index 9401bede65fa9..1b432f5270576 100644 --- a/src/framework/audio/internal/worker/mixer.cpp +++ b/src/framework/audio/internal/worker/mixer.cpp @@ -83,7 +83,12 @@ RetVal Mixer::addChannel(const TrackId trackId, ITrackAudioInpu return; } + ITrackAudioInputPtr source = std::static_pointer_cast(channel->source()); + if (channel->muted()) { + if (source) { + source->setIsActive(false); + } if (m_nonMutedTrackCount != 0) { m_nonMutedTrackCount--; } @@ -92,8 +97,8 @@ RetVal Mixer::addChannel(const TrackId trackId, ITrackAudioInpu m_nonMutedTrackCount++; - ITrackAudioInputPtr source = std::static_pointer_cast(channel->source()); if (source) { + source->setIsActive(isActive()); source->seek(currentTime()); } }); @@ -206,26 +211,25 @@ samples_t Mixer::process(float* outBuffer, samples_t samplesPerChannel) prepareAuxBuffers(outBufferSize); - samples_t masterChannelSampleCount = 0; - for (auto& pair : tracksData) { - const std::vector& trackBuffer = pair.second; - - bool outBufferIsSilent = false; - mixOutputFromChannel(outBuffer, trackBuffer.data(), samplesPerChannel, outBufferIsSilent); - masterChannelSampleCount = std::max(samplesPerChannel, masterChannelSampleCount); + auto channelIt = m_trackChannels.find(pair.first); + if (channelIt == m_trackChannels.cend()) { + continue; + } - if (!outBufferIsSilent) { + const MixerChannelPtr channel = channelIt->second; + if (!channel->isSilent()) { m_isSilence = false; } else if (m_isSilence) { continue; } - const AuxSendsParams& auxSends = m_trackChannels.at(pair.first)->outputParams().auxSends; - writeTrackToAuxBuffers(trackBuffer.data(), auxSends, samplesPerChannel); + const std::vector& trackBuffer = pair.second; + mixOutputFromChannel(outBuffer, trackBuffer.data(), samplesPerChannel); + writeTrackToAuxBuffers(trackBuffer.data(), channel->outputParams().auxSends, samplesPerChannel); } - if (m_masterParams.muted || masterChannelSampleCount == 0 || m_isSilence) { + if (m_masterParams.muted || samplesPerChannel == 0 || m_isSilence) { notifyNoAudioSignal(); return 0; } @@ -239,7 +243,7 @@ samples_t Mixer::process(float* outBuffer, samples_t samplesPerChannel) } } - return masterChannelSampleCount; + return samplesPerChannel; } void Mixer::processTrackChannels(size_t outBufferSize, size_t samplesPerChannel, TracksData& outTracksData) @@ -272,7 +276,7 @@ void Mixer::processTrackChannels(size_t outBufferSize, size_t samplesPerChannel, continue; } - if (pair.second->muted()) { + if (pair.second->muted() && pair.second->isSilent()) { pair.second->notifyNoAudioSignal(); continue; } @@ -290,7 +294,7 @@ void Mixer::processTrackChannels(size_t outBufferSize, size_t samplesPerChannel, continue; } - if (pair.second->muted()) { + if (pair.second->muted() && pair.second->isSilent()) { pair.second->notifyNoAudioSignal(); continue; } @@ -321,8 +325,16 @@ void Mixer::setIsActive(bool arg) AbstractAudioSource::setIsActive(arg); - for (const auto& channel : m_trackChannels) { - channel.second->setIsActive(arg); + for (auto& channel : m_trackChannels) { + if (!channel.second->muted()) { + channel.second->setIsActive(arg); + } + } + + for (auto& aux : m_auxChannelInfoList) { + if (!aux.channel->muted()) { + aux.channel->setIsActive(arg); + } } } @@ -429,14 +441,12 @@ void Mixer::setTracksToProcessWhenIdle(std::unordered_set&& trackIds) m_tracksToProcessWhenIdle = std::move(trackIds); } -void Mixer::mixOutputFromChannel(float* outBuffer, const float* inBuffer, unsigned int samplesCount, bool& outBufferIsSilent) +void Mixer::mixOutputFromChannel(float* outBuffer, const float* inBuffer, unsigned int samplesCount) const { IF_ASSERT_FAILED(outBuffer && inBuffer) { return; } - outBufferIsSilent = true; - if (m_masterParams.muted) { return; } @@ -448,10 +458,6 @@ void Mixer::mixOutputFromChannel(float* outBuffer, const float* inBuffer, unsign size_t idx = samplePos + audioChNum; float sample = inBuffer[idx]; outBuffer[idx] += sample; - - if (outBufferIsSilent && !RealIsNull(sample)) { - outBufferIsSilent = false; - } } } } @@ -516,8 +522,9 @@ void Mixer::processAuxChannels(float* buffer, samples_t samplesPerChannel) float* auxBuffer = aux.buffer.data(); aux.channel->process(auxBuffer, samplesPerChannel); - static bool isSilent = false; - mixOutputFromChannel(buffer, auxBuffer, samplesPerChannel, isSilent); + if (!aux.channel->isSilent()) { + mixOutputFromChannel(buffer, auxBuffer, samplesPerChannel); + } } } @@ -527,14 +534,11 @@ void Mixer::completeOutput(float* buffer, samples_t samplesPerChannel) return; } - m_isSilence = true; - float totalSquaredSum = 0.f; float volume = muse::db_to_linear(m_masterParams.volume); for (audioch_t audioChNum = 0; audioChNum < m_audioChannelsCount; ++audioChNum) { float singleChannelSquaredSum = 0.f; - gain_t totalGain = dsp::balanceGain(m_masterParams.balance, audioChNum) * volume; for (samples_t s = 0; s < samplesPerChannel; ++s) { @@ -543,10 +547,6 @@ void Mixer::completeOutput(float* buffer, samples_t samplesPerChannel) float resultSample = buffer[idx] * totalGain; buffer[idx] = resultSample; - if (m_isSilence && !RealIsNull(resultSample)) { - m_isSilence = false; - } - float squaredSample = resultSample * resultSample; totalSquaredSum += squaredSample; singleChannelSquaredSum += squaredSample; @@ -556,6 +556,7 @@ void Mixer::completeOutput(float* buffer, samples_t samplesPerChannel) m_audioSignalNotifier.updateSignalValues(audioChNum, rms); } + m_isSilence = RealIsNull(totalSquaredSum); m_audioSignalNotifier.notifyAboutChanges(); if (!m_limiter->isActive()) { diff --git a/src/framework/audio/internal/worker/mixer.h b/src/framework/audio/internal/worker/mixer.h index b1f66f086f697..4719c87d64886 100644 --- a/src/framework/audio/internal/worker/mixer.h +++ b/src/framework/audio/internal/worker/mixer.h @@ -82,7 +82,7 @@ class Mixer : public AbstractAudioSource, public Injectable, public async::Async using TracksData = std::map >; void processTrackChannels(size_t outBufferSize, size_t samplesPerChannel, TracksData& outTracksData); - void mixOutputFromChannel(float* outBuffer, const float* inBuffer, unsigned int samplesCount, bool& outBufferIsSilent); + void mixOutputFromChannel(float* outBuffer, const float* inBuffer, unsigned int samplesCount) const; void prepareAuxBuffers(size_t outBufferSize); void writeTrackToAuxBuffers(const float* trackBuffer, const AuxSendsParams& auxSends, samples_t samplesPerChannel); void processAuxChannels(float* buffer, samples_t samplesPerChannel); diff --git a/src/framework/audio/internal/worker/mixerchannel.cpp b/src/framework/audio/internal/worker/mixerchannel.cpp index 19521a261de88..ed7104a790d49 100644 --- a/src/framework/audio/internal/worker/mixerchannel.cpp +++ b/src/framework/audio/internal/worker/mixerchannel.cpp @@ -192,11 +192,13 @@ samples_t MixerChannel::process(float* buffer, samples_t samplesPerChannel) samples_t processedSamplesCount = samplesPerChannel; - if (m_audioSource && !m_params.muted) { - processedSamplesCount = m_audioSource->process(buffer, samplesPerChannel); + if (m_audioSource) { + if (!m_params.muted || !m_isSilent) { + processedSamplesCount = m_audioSource->process(buffer, samplesPerChannel); + } } - if (processedSamplesCount == 0 || m_params.muted) { + if (processedSamplesCount == 0 || (m_params.muted && m_isSilent)) { std::fill(buffer, buffer + samplesPerChannel * audioChannelsCount(), 0.f); notifyNoAudioSignal(); @@ -215,7 +217,7 @@ samples_t MixerChannel::process(float* buffer, samples_t samplesPerChannel) return processedSamplesCount; } -void MixerChannel::completeOutput(float* buffer, unsigned int samplesCount) const +void MixerChannel::completeOutput(float* buffer, unsigned int samplesCount) { unsigned int channelsCount = audioChannelsCount(); float volume = muse::db_to_linear(m_params.volume); @@ -241,6 +243,7 @@ void MixerChannel::completeOutput(float* buffer, unsigned int samplesCount) cons m_audioSignalNotifier.updateSignalValues(audioChNum, rms); } + m_isSilent = RealIsNull(totalSquaredSum); m_audioSignalNotifier.notifyAboutChanges(); if (!m_compressor->isActive()) { @@ -251,6 +254,11 @@ void MixerChannel::completeOutput(float* buffer, unsigned int samplesCount) cons m_compressor->process(totalRms, buffer, channelsCount, samplesCount); } +bool MixerChannel::isSilent() const +{ + return m_isSilent; +} + void MixerChannel::notifyNoAudioSignal() { unsigned int channelsCount = audioChannelsCount(); diff --git a/src/framework/audio/internal/worker/mixerchannel.h b/src/framework/audio/internal/worker/mixerchannel.h index 5b2fbd6a0c607..aec8c0d6b6a2f 100644 --- a/src/framework/audio/internal/worker/mixerchannel.h +++ b/src/framework/audio/internal/worker/mixerchannel.h @@ -48,6 +48,8 @@ class MixerChannel : public ITrackAudioOutput, public Injectable, public async:: bool muted() const; async::Notification mutedChanged() const; + bool isSilent() const; + void notifyNoAudioSignal(); const AudioOutputParams& outputParams() const override; @@ -65,7 +67,7 @@ class MixerChannel : public ITrackAudioOutput, public Injectable, public async:: samples_t process(float* buffer, samples_t samplesPerChannel) override; private: - void completeOutput(float* buffer, unsigned int samplesCount) const; + void completeOutput(float* buffer, unsigned int samplesCount); TrackId m_trackId = -1; @@ -78,6 +80,8 @@ class MixerChannel : public ITrackAudioOutput, public Injectable, public async:: dsp::CompressorPtr m_compressor = nullptr; + bool m_isSilent = true; + async::Notification m_mutedChanged; mutable async::Channel m_paramsChanges; mutable AudioSignalsNotifier m_audioSignalNotifier; diff --git a/src/framework/audio/internal/worker/sequenceplayer.cpp b/src/framework/audio/internal/worker/sequenceplayer.cpp index 25cc91911336a..f8af3021e852e 100644 --- a/src/framework/audio/internal/worker/sequenceplayer.cpp +++ b/src/framework/audio/internal/worker/sequenceplayer.cpp @@ -38,7 +38,7 @@ SequencePlayer::SequencePlayer(IGetTracks* getTracks, IClockPtr clock, const mod }); m_clock->statusChanged().onReceive(this, [this](const PlaybackStatus status) { - setAllTracksActive(status == PlaybackStatus::Running); + audioEngine()->mixer()->setIsActive(status == PlaybackStatus::Running); }); } @@ -48,7 +48,7 @@ void SequencePlayer::play() audioEngine()->setMode(RenderMode::RealTimeMode); m_clock->start(); - setAllTracksActive(true); + audioEngine()->mixer()->setIsActive(true); } void SequencePlayer::seek(const secs_t newPosition) @@ -66,7 +66,7 @@ void SequencePlayer::stop() audioEngine()->setMode(RenderMode::IdleMode); m_clock->stop(); - setAllTracksActive(false); + audioEngine()->mixer()->setIsActive(false); } void SequencePlayer::pause() @@ -75,7 +75,7 @@ void SequencePlayer::pause() audioEngine()->setMode(RenderMode::IdleMode); m_clock->pause(); - setAllTracksActive(false); + audioEngine()->mixer()->setIsActive(false); } void SequencePlayer::resume() @@ -84,7 +84,7 @@ void SequencePlayer::resume() audioEngine()->setMode(RenderMode::RealTimeMode); m_clock->resume(); - setAllTracksActive(true); + audioEngine()->mixer()->setIsActive(true); } msecs_t SequencePlayer::duration() const @@ -147,19 +147,6 @@ Channel SequencePlayer::playbackStatusChanged() const return m_clock->statusChanged(); } -void SequencePlayer::setAllTracksActive(bool active) -{ - IF_ASSERT_FAILED(m_getTracks) { - return; - } - - for (const auto& pair : m_getTracks->allTracks()) { - if (pair.second->inputHandler) { - pair.second->inputHandler->setIsActive(active); - } - } -} - void SequencePlayer::seekAllTracks(const msecs_t newPositionMsecs) { IF_ASSERT_FAILED(m_getTracks) { diff --git a/src/framework/audio/internal/worker/sequenceplayer.h b/src/framework/audio/internal/worker/sequenceplayer.h index 03ff97b8a0f6f..8580a2350ec9e 100644 --- a/src/framework/audio/internal/worker/sequenceplayer.h +++ b/src/framework/audio/internal/worker/sequenceplayer.h @@ -58,7 +58,6 @@ class SequencePlayer : public ISequencePlayer, public Injectable, public async:: async::Channel playbackPositionChanged() const override; private: - void setAllTracksActive(bool active); void seekAllTracks(const msecs_t newPositionMsecs); IGetTracks* m_getTracks = nullptr; diff --git a/src/framework/vst/internal/synth/vstsynthesiser.cpp b/src/framework/vst/internal/synth/vstsynthesiser.cpp index 7a39d96b6b63f..4594afd3d4bfb 100644 --- a/src/framework/vst/internal/synth/vstsynthesiser.cpp +++ b/src/framework/vst/internal/synth/vstsynthesiser.cpp @@ -202,6 +202,7 @@ samples_t VstSynthesiser::process(float* buffer, samples_t samplesPerChannel) const msecs_t nextMsecs = samplesToMsecs(samplesPerChannel, m_sampleRate); const VstSequencer::EventSequenceMap sequences = m_sequencer.movePlaybackForward(nextMsecs); + samples_t sampleOffset = 0; samples_t processedSamples = 0; @@ -242,5 +243,5 @@ samples_t VstSynthesiser::processSequence(const VstSequencer::EventSequence& seq return 0; } - return m_vstAudioClient->process(buffer, samples); + return m_vstAudioClient->process(buffer, samples, m_sequencer.playbackPosition()); } diff --git a/src/framework/vst/internal/vstaudioclient.cpp b/src/framework/vst/internal/vstaudioclient.cpp index 3346857a18590..49b236d58af15 100644 --- a/src/framework/vst/internal/vstaudioclient.cpp +++ b/src/framework/vst/internal/vstaudioclient.cpp @@ -113,7 +113,8 @@ void VstAudioClient::setVolumeGain(const muse::audio::gain_t newVolumeGain) m_volumeGain = newVolumeGain; } -muse::audio::samples_t VstAudioClient::process(float* output, samples_t samplesPerChannel) +muse::audio::samples_t VstAudioClient::process(float* output, muse::audio::samples_t samplesPerChannel, + muse::audio::msecs_t playbackPosition) { IAudioProcessorPtr processor = pluginProcessor(); if (!processor || !output) { @@ -131,6 +132,8 @@ muse::audio::samples_t VstAudioClient::process(float* output, samples_t samplesP //! but never bigger than the maxSamplesPerBlock m_processData.numSamples = samplesPerChannel; + m_processContext.projectTimeSamples = (playbackPosition / 1000000.f) * m_samplesInfo.sampleRate; + if (samplesPerChannel > m_samplesInfo.maxSamplesPerBlock) { setMaxSamplesPerBlock(samplesPerChannel); } @@ -422,7 +425,7 @@ bool VstAudioClient::fillOutputBufferInstrument(samples_t sampleCount, float* ou float sample = bus.channelBuffers32[audioChannelIndex][sampleIndex]; output[offset + audioChannelIndex] += sample * m_volumeGain; - if (isSilence && !RealIsNull(sample)) { + if (isSilence && sample != 0.f) { isSilence = false; } } @@ -450,7 +453,7 @@ bool VstAudioClient::fillOutputBufferFx(samples_t sampleCount, float* output) float sample = bus.channelBuffers32[audioChannelIndex][sampleIndex]; output[offset + audioChannelIndex] = sample * m_volumeGain; - if (isSilence && !RealIsNull(sample)) { + if (isSilence && sample != 0.f) { isSilence = false; } } diff --git a/src/framework/vst/internal/vstaudioclient.h b/src/framework/vst/internal/vstaudioclient.h index eccd696dfc06c..fd5110505889b 100644 --- a/src/framework/vst/internal/vstaudioclient.h +++ b/src/framework/vst/internal/vstaudioclient.h @@ -42,7 +42,7 @@ class VstAudioClient bool handleParamChange(const ParamChangeEvent& param); void setVolumeGain(const muse::audio::gain_t newVolumeGain); - muse::audio::samples_t process(float* output, muse::audio::samples_t samplesPerChannel); + muse::audio::samples_t process(float* output, muse::audio::samples_t samplesPerChannel, muse::audio::msecs_t playbackPosition = 0); void flush(); void allNotesOff(); diff --git a/src/instrumentsscene/view/abstractlayoutpaneltreeitem.cpp b/src/instrumentsscene/view/abstractlayoutpaneltreeitem.cpp index 96f3e78afd07f..f20d4946792c7 100644 --- a/src/instrumentsscene/view/abstractlayoutpaneltreeitem.cpp +++ b/src/instrumentsscene/view/abstractlayoutpaneltreeitem.cpp @@ -164,6 +164,10 @@ void AbstractLayoutPanelTreeItem::removeChildren(int row, int count, bool delete } } +void AbstractLayoutPanelTreeItem::onScoreChanged(const mu::engraving::ScoreChangesRange&) +{ +} + AbstractLayoutPanelTreeItem* AbstractLayoutPanelTreeItem::parentItem() const { return m_parent; diff --git a/src/instrumentsscene/view/abstractlayoutpaneltreeitem.h b/src/instrumentsscene/view/abstractlayoutpaneltreeitem.h index 2eb967771debb..ed98a1582023e 100644 --- a/src/instrumentsscene/view/abstractlayoutpaneltreeitem.h +++ b/src/instrumentsscene/view/abstractlayoutpaneltreeitem.h @@ -91,6 +91,8 @@ class AbstractLayoutPanelTreeItem : public QObject virtual void removeChildren(int row, int count = 1, bool deleteChild = false); + virtual void onScoreChanged(const mu::engraving::ScoreChangesRange& changes); + AbstractLayoutPanelTreeItem* parentItem() const; void setParentItem(AbstractLayoutPanelTreeItem* parent); diff --git a/src/instrumentsscene/view/layoutpaneltreemodel.cpp b/src/instrumentsscene/view/layoutpaneltreemodel.cpp index cd019f51bfbf2..39ab9ca899dcf 100644 --- a/src/instrumentsscene/view/layoutpaneltreemodel.cpp +++ b/src/instrumentsscene/view/layoutpaneltreemodel.cpp @@ -270,11 +270,20 @@ void LayoutPanelTreeModel::setupStavesConnections(const muse::ID& stavesPartId) }); } -void LayoutPanelTreeModel::listenNotationSelectionChanged() +void LayoutPanelTreeModel::setupNotationConnections() { m_notation->interaction()->selectionChanged().onNotify(this, [this]() { updateSelectedRows(); }); + + m_notation->undoStack()->changesChannel().onReceive(this, [this](const mu::engraving::ScoreChangesRange& changes) { + if (!m_layoutPanelVisible) { + m_scoreChangesCache.combine(changes); + return; + } + + onScoreChanged(changes); + }); } void LayoutPanelTreeModel::updateSelectedRows() @@ -308,6 +317,17 @@ void LayoutPanelTreeModel::updateSelectedRows() } } +void LayoutPanelTreeModel::onScoreChanged(const mu::engraving::ScoreChangesRange& changes) +{ + if (!m_rootItem) { + return; + } + + for (AbstractLayoutPanelTreeItem* item : m_rootItem->childItems()) { + item->onScoreChanged(changes); + } +} + void LayoutPanelTreeModel::clear() { TRACEFUNC; @@ -363,7 +383,7 @@ void LayoutPanelTreeModel::load() endResetModel(); setupPartsConnections(); - listenNotationSelectionChanged(); + setupNotationConnections(); updateIsAddingSystemMarkingsAvailable(); @@ -407,6 +427,11 @@ void LayoutPanelTreeModel::setLayoutPanelVisible(bool visible) if (visible) { updateSelectedRows(); + + if (m_scoreChangesCache.isValid()) { + onScoreChanged(m_scoreChangesCache); + m_scoreChangesCache.clear(); + } } } @@ -1009,7 +1034,8 @@ void LayoutPanelTreeModel::updateSystemObjectLayers() m_shouldUpdateSystemObjectLayers = false; // Create copy, because we're going to modify them - std::vector newSystemObjectStaves = m_masterNotation->notation()->parts()->systemObjectStaves(); + const INotationPartsPtr notationParts = m_masterNotation->notation()->parts(); + std::vector newSystemObjectStaves = notationParts->systemObjectStaves(); QList children = m_rootItem->childItems(); // Remove old system object layers @@ -1036,6 +1062,8 @@ void LayoutPanelTreeModel::updateSystemObjectLayers() endRemoveRows(); } + SystemObjectGroupsByStaff systemObjects = collectSystemObjectGroups(notationParts->systemObjectStaves()); + // Update position of existing layers if changed for (SystemObjectsLayerTreeItem* layerItem : existingSystemObjectLayers) { const PartTreeItem* partItem = findPartItemByStaff(layerItem->staff()); @@ -1052,16 +1080,10 @@ void LayoutPanelTreeModel::updateSystemObjectLayers() endMoveRows(); } - layerItem->updateSystemObjects(); - } - - if (newSystemObjectStaves.empty()) { - return; + layerItem->setSystemObjects(systemObjects[layerItem->staff()]); } // Create new system object layers - SystemObjectGroupsByStaff systemObjects = collectSystemObjectGroups(newSystemObjectStaves); - for (const Staff* staff : newSystemObjectStaves) { for (const PartTreeItem* partItem : partItems) { if (staff->part() != partItem->part()) { diff --git a/src/instrumentsscene/view/layoutpaneltreemodel.h b/src/instrumentsscene/view/layoutpaneltreemodel.h index 996994a415de7..2c3011456c8ed 100644 --- a/src/instrumentsscene/view/layoutpaneltreemodel.h +++ b/src/instrumentsscene/view/layoutpaneltreemodel.h @@ -141,8 +141,10 @@ private slots: void setupPartsConnections(); void setupStavesConnections(const muse::ID& stavesPartId); - void listenNotationSelectionChanged(); + void setupNotationConnections(); + void updateSelectedRows(); + void onScoreChanged(const mu::engraving::ScoreChangesRange& changes); void clear(); void deleteItems(); @@ -186,8 +188,9 @@ private slots: using NotationKey = QString; QHash > m_sortedPartIdList; - bool m_layoutPanelVisible = true; + mu::engraving::ScoreChangesRange m_scoreChangesCache; + bool m_layoutPanelVisible = true; bool m_shouldUpdateSystemObjectLayers = false; bool m_dragInProgress = false; diff --git a/src/instrumentsscene/view/parttreeitem.cpp b/src/instrumentsscene/view/parttreeitem.cpp index 4b0e5721eb41d..78014da44878f 100644 --- a/src/instrumentsscene/view/parttreeitem.cpp +++ b/src/instrumentsscene/view/parttreeitem.cpp @@ -30,6 +30,8 @@ using namespace muse; PartTreeItem::PartTreeItem(IMasterNotationPtr masterNotation, INotationPtr notation, QObject* parent) : AbstractLayoutPanelTreeItem(LayoutPanelItemType::PART, masterNotation, notation, parent), Injectable(iocCtxForQmlObject(this)) { + setIsSelectable(true); + listenVisibilityChanged(); } @@ -54,7 +56,6 @@ void PartTreeItem::init(const notation::Part* masterPart) setSettingsEnabled(partExists); setIsExpandable(partExists); setIsRemovable(partExists); - setIsSelectable(partExists); m_part = part; m_isInited = true; diff --git a/src/instrumentsscene/view/systemobjectslayertreeitem.cpp b/src/instrumentsscene/view/systemobjectslayertreeitem.cpp index 3a25618479373..174a94be3706c 100644 --- a/src/instrumentsscene/view/systemobjectslayertreeitem.cpp +++ b/src/instrumentsscene/view/systemobjectslayertreeitem.cpp @@ -88,20 +88,13 @@ SystemObjectsLayerTreeItem::SystemObjectsLayerTreeItem(IMasterNotationPtr master void SystemObjectsLayerTreeItem::init(const Staff* staff, const SystemObjectGroups& systemObjects) { - m_systemObjectGroups = systemObjects; - setStaff(staff); + setSystemObjects(systemObjects); bool isTopLayer = staff->score()->staff(0) == staff; setIsRemovable(!isTopLayer); setIsSelectable(!isTopLayer); - updateState(); - - notation()->undoStack()->changesChannel().onReceive(this, [this](const ChangesRange& changes) { - onUndoStackChanged(changes); - }); - connect(this, &AbstractLayoutPanelTreeItem::isVisibleChanged, this, [this](bool isVisible) { onVisibleChanged(isVisible); }); @@ -127,9 +120,9 @@ void SystemObjectsLayerTreeItem::setStaff(const Staff* staff) } } -void SystemObjectsLayerTreeItem::updateSystemObjects() +void SystemObjectsLayerTreeItem::setSystemObjects(const SystemObjectGroups& systemObjects) { - m_systemObjectGroups = collectSystemObjectGroups(m_staff); + m_systemObjectGroups = systemObjects; updateState(); } @@ -144,7 +137,7 @@ bool SystemObjectsLayerTreeItem::canAcceptDrop(const QVariant&) const return m_staffIdx != 0; // all except the first } -void SystemObjectsLayerTreeItem::onUndoStackChanged(const mu::engraving::ScoreChangesRange& changes) +void SystemObjectsLayerTreeItem::onScoreChanged(const mu::engraving::ScoreChangesRange& changes) { if (muse::contains(changes.changedPropertyIdSet, Pid::TRACK)) { updateStaff(); diff --git a/src/instrumentsscene/view/systemobjectslayertreeitem.h b/src/instrumentsscene/view/systemobjectslayertreeitem.h index 11411e8e707cf..ad6beefc1d51a 100644 --- a/src/instrumentsscene/view/systemobjectslayertreeitem.h +++ b/src/instrumentsscene/view/systemobjectslayertreeitem.h @@ -23,14 +23,13 @@ #pragma once #include "abstractlayoutpaneltreeitem.h" -#include "async/asyncable.h" #include "notation/inotationparts.h" #include "layoutpanelutils.h" namespace mu::instrumentsscene { -class SystemObjectsLayerTreeItem : public AbstractLayoutPanelTreeItem, public muse::async::Asyncable +class SystemObjectsLayerTreeItem : public AbstractLayoutPanelTreeItem { Q_OBJECT @@ -41,13 +40,13 @@ class SystemObjectsLayerTreeItem : public AbstractLayoutPanelTreeItem, public mu const mu::engraving::Staff* staff() const; void setStaff(const mu::engraving::Staff* staff); - void updateSystemObjects(); + void setSystemObjects(const SystemObjectGroups& systemObjects); Q_INVOKABLE QString staffId() const; Q_INVOKABLE bool canAcceptDrop(const QVariant& item) const override; private: - void onUndoStackChanged(const mu::engraving::ScoreChangesRange& changes); + void onScoreChanged(const mu::engraving::ScoreChangesRange& changes) override; void onVisibleChanged(bool isVisible); bool addSystemObject(mu::engraving::EngravingItem* obj); diff --git a/src/notation/inotationplayback.h b/src/notation/inotationplayback.h index edc51847db1f7..86e161951a3d5 100644 --- a/src/notation/inotationplayback.h +++ b/src/notation/inotationplayback.h @@ -38,6 +38,7 @@ class INotationPlayback virtual ~INotationPlayback() = default; virtual void init() = 0; + virtual void reload() = 0; virtual const engraving::InstrumentTrackId& metronomeTrackId() const = 0; virtual engraving::InstrumentTrackId chordSymbolsTrackId(const muse::ID& partId) const = 0; diff --git a/src/notation/internal/masternotationparts.cpp b/src/notation/internal/masternotationparts.cpp index 468eaf3d10107..1c9c744ff69d0 100644 --- a/src/notation/internal/masternotationparts.cpp +++ b/src/notation/internal/masternotationparts.cpp @@ -94,8 +94,7 @@ void MasterNotationParts::setParts(const PartInstrumentList& partList, const Sco impl->setBracketsAndBarlines(); } - updatePartList(); - updateSystemObjectStaves(); + updatePartsAndSystemObjectStaves(); endGlobalEdit(); } diff --git a/src/notation/internal/notationparts.cpp b/src/notation/internal/notationparts.cpp index b108f2b3fcbda..2ca23a3a8d4e0 100644 --- a/src/notation/internal/notationparts.cpp +++ b/src/notation/internal/notationparts.cpp @@ -242,8 +242,7 @@ void NotationParts::setParts(const PartInstrumentList& parts, const ScoreOrder& updateSoloist(parts); sortParts(parts); setBracketsAndBarlines(); - updatePartList(); - updateSystemObjectStaves(); + updatePartsAndSystemObjectStaves(); apply(); } @@ -330,8 +329,7 @@ void NotationParts::listenUndoStackChanges() return; } - updatePartList(); - updateSystemObjectStaves(); + updatePartsAndSystemObjectStaves(); m_undoStack->changesChannel().onReceive(this, [this](const ChangesRange& range) { if (range.changedTypes.empty()) { @@ -346,23 +344,14 @@ void NotationParts::listenUndoStackChanges() for (ElementType type : TYPES_TO_CHECK) { if (muse::contains(range.changedTypes, type)) { - updatePartList(); - updateSystemObjectStaves(); + updatePartsAndSystemObjectStaves(); return; } } }); } -void NotationParts::updatePartList() -{ - if (m_parts != score()->parts()) { - m_parts = score()->parts(); - m_partChangedNotifier.changed(); - } -} - -void NotationParts::updateSystemObjectStaves() +void NotationParts::updatePartsAndSystemObjectStaves() { const auto systemObjectStavesWithTopStaff = [this]() { std::vector result; @@ -375,10 +364,18 @@ void NotationParts::updateSystemObjectStaves() return result; }; + const bool partsChanged = m_parts != score()->parts(); + m_parts = score()->parts(); + std::vector newSystemObjectStaves = systemObjectStavesWithTopStaff(); + const bool systemObjectStavesChanged = m_systemObjectStaves != newSystemObjectStaves; + m_systemObjectStaves = std::move(newSystemObjectStaves); + + if (partsChanged) { + m_partChangedNotifier.changed(); + } - if (m_systemObjectStaves != newSystemObjectStaves) { - m_systemObjectStaves = std::move(newSystemObjectStaves); + if (systemObjectStavesChanged) { m_systemObjectStavesChanged.notify(); } } diff --git a/src/notation/internal/notationparts.h b/src/notation/internal/notationparts.h index 27e9c30fca771..891dfb6330a9a 100644 --- a/src/notation/internal/notationparts.h +++ b/src/notation/internal/notationparts.h @@ -101,9 +101,7 @@ class NotationParts : public INotationParts, public muse::async::Asyncable friend class MasterNotationParts; void listenUndoStackChanges(); - - void updatePartList(); - void updateSystemObjectStaves(); + void updatePartsAndSystemObjectStaves(); void doSetScoreOrder(const ScoreOrder& order); void doRemoveParts(const std::vector& parts); diff --git a/src/notation/internal/notationplayback.cpp b/src/notation/internal/notationplayback.cpp index 38f5b8e034e8e..cbd1710ff358b 100644 --- a/src/notation/internal/notationplayback.cpp +++ b/src/notation/internal/notationplayback.cpp @@ -105,6 +105,11 @@ void NotationPlayback::init() }); } +void NotationPlayback::reload() +{ + m_playbackModel.reload(); +} + const engraving::InstrumentTrackId& NotationPlayback::metronomeTrackId() const { return m_playbackModel.metronomeTrackId(); diff --git a/src/notation/internal/notationplayback.h b/src/notation/internal/notationplayback.h index 8d369ce605ce2..fd3a17c0eedf5 100644 --- a/src/notation/internal/notationplayback.h +++ b/src/notation/internal/notationplayback.h @@ -43,6 +43,7 @@ class NotationPlayback : public INotationPlayback, public muse::async::Asyncable NotationPlayback(IGetScore* getScore, muse::async::Notification notationChanged, const muse::modularity::ContextPtr& iocCtx); void init() override; + void reload() override; const engraving::InstrumentTrackId& metronomeTrackId() const override; engraving::InstrumentTrackId chordSymbolsTrackId(const muse::ID& partId) const override; diff --git a/src/notation/internal/notationplaybackstub.cpp b/src/notation/internal/notationplaybackstub.cpp index 283ea6c396f68..26d14e3e03604 100644 --- a/src/notation/internal/notationplaybackstub.cpp +++ b/src/notation/internal/notationplaybackstub.cpp @@ -36,6 +36,10 @@ void NotationPlaybackStub::init() { } +void NotationPlaybackStub::reload() +{ +} + const engraving::InstrumentTrackId& NotationPlaybackStub::metronomeTrackId() const { static const engraving::InstrumentTrackId dummy; diff --git a/src/notation/internal/notationplaybackstub.h b/src/notation/internal/notationplaybackstub.h index ab21e87b4a146..14c6279132989 100644 --- a/src/notation/internal/notationplaybackstub.h +++ b/src/notation/internal/notationplaybackstub.h @@ -30,6 +30,7 @@ class NotationPlaybackStub : public INotationPlayback NotationPlaybackStub(); void init() override; + void reload() override; const engraving::InstrumentTrackId& metronomeTrackId() const override; engraving::InstrumentTrackId chordSymbolsTrackId(const muse::ID& partId) const override; diff --git a/src/playback/internal/playbackcontroller.cpp b/src/playback/internal/playbackcontroller.cpp index c6ab7233fa563..d04a73b14b002 100644 --- a/src/playback/internal/playbackcontroller.cpp +++ b/src/playback/internal/playbackcontroller.cpp @@ -107,6 +107,7 @@ void PlaybackController::init() dispatcher()->reg(this, COUNT_IN_CODE, this, &PlaybackController::toggleCountIn); dispatcher()->reg(this, PLAYBACK_SETUP, this, &PlaybackController::openPlaybackSetupDialog); dispatcher()->reg(this, TOGGLE_HEAR_PLAYBACK_WHEN_EDITING_CODE, this, &PlaybackController::toggleHearPlaybackWhenEditing); + dispatcher()->reg(this, "playback-reload-cache", this, &PlaybackController::reloadPlaybackCache); globalContext()->currentNotationChanged().onNotify(this, [this]() { onNotationChanged(); @@ -571,7 +572,11 @@ void PlaybackController::onPartChanged(const Part* part) void PlaybackController::onSelectionChanged() { - INotationSelectionPtr selection = this->selection(); + const INotationSelectionPtr selection = this->selection(); + if (!selection || !m_player) { + return; + } + bool selectionTypeChanged = m_isRangeSelection && !selection->isRange(); m_isRangeSelection = selection->isRange(); @@ -590,7 +595,7 @@ void PlaybackController::onSelectionChanged() return; } - currentPlayer()->resetLoop(); + m_player->resetLoop(); seekRangeSelection(); updateSoloMuteStates(); @@ -820,6 +825,14 @@ void PlaybackController::toggleHearPlaybackWhenEditing() configuration()->setPlayNotesWhenEditing(!wasPlayNotesWhenEditing); } +void PlaybackController::reloadPlaybackCache() +{ + INotationPlaybackPtr playback = notationPlayback(); + if (playback) { + playback->reload(); + } +} + void PlaybackController::openPlaybackSetupDialog() { interactive()->open("musescore://playback/soundprofilesdialog"); diff --git a/src/playback/internal/playbackcontroller.h b/src/playback/internal/playbackcontroller.h index be1a42229a920..968e2383ee1cc 100644 --- a/src/playback/internal/playbackcontroller.h +++ b/src/playback/internal/playbackcontroller.h @@ -174,6 +174,8 @@ class PlaybackController : public IPlaybackController, public muse::actions::Act void toggleLoopPlayback(); void toggleHearPlaybackWhenEditing(); + void reloadPlaybackCache(); + void openPlaybackSetupDialog(); void addLoopBoundary(notation::LoopBoundaryType type); diff --git a/src/playback/internal/playbackuiactions.cpp b/src/playback/internal/playbackuiactions.cpp index 90025e1919ea3..21476030c60d7 100644 --- a/src/playback/internal/playbackuiactions.cpp +++ b/src/playback/internal/playbackuiactions.cpp @@ -168,6 +168,14 @@ const UiActionList PlaybackUiActions::m_loopBoundaryActions = { ), }; +const UiActionList PlaybackUiActions::m_diagnosticActions = { + UiAction("playback-reload-cache", + mu::context::UiCtxAny, + mu::context::CTX_ANY, + TranslatableString("action", "Reload playback cache") + ) +}; + PlaybackUiActions::PlaybackUiActions(std::shared_ptr controller) : m_controller(controller) { @@ -199,6 +207,7 @@ const UiActionList& PlaybackUiActions::actionsList() const alist.insert(alist.end(), m_midiInputPitchActions.cbegin(), m_midiInputPitchActions.cend()); alist.insert(alist.end(), m_settingsActions.cbegin(), m_settingsActions.cend()); alist.insert(alist.end(), m_loopBoundaryActions.cbegin(), m_loopBoundaryActions.cend()); + alist.insert(alist.end(), m_diagnosticActions.cbegin(), m_diagnosticActions.cend()); } return alist; } diff --git a/src/playback/internal/playbackuiactions.h b/src/playback/internal/playbackuiactions.h index f3ab0f98b63cb..8427059982405 100644 --- a/src/playback/internal/playbackuiactions.h +++ b/src/playback/internal/playbackuiactions.h @@ -60,6 +60,7 @@ class PlaybackUiActions : public muse::ui::IUiActionsModule, public muse::async: static const muse::ui::UiActionList m_midiInputPitchActions; static const muse::ui::UiActionList m_settingsActions; static const muse::ui::UiActionList m_loopBoundaryActions; + static const muse::ui::UiActionList m_diagnosticActions; std::shared_ptr m_controller; muse::async::Channel m_actionEnabledChanged;