LCOV - code coverage report
Current view: top level - src/playback/qml/MuseScore/Playback - playbacktoolbarmodel.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 0.0 % 170 0
Test Date: 2026-11-05 04:07:02

            Line data    Source code
       1              : /*
       2              :  * SPDX-License-Identifier: GPL-3.0-only
       3              :  * MuseScore-Studio-CLA-applies
       4              :  *
       5              :  * MuseScore Studio
       6              :  * Music Composition & Notation
       7              :  *
       8              :  * Copyright (C) 2021 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              : #include "playbacktoolbarmodel.h"
      23              : 
      24              : #include <vector>
      25              : 
      26              : #include "global/containers.h"
      27              : #include "types/translatablestring.h"
      28              : 
      29              : #include "ui/view/musicalsymbolcodes.h"
      30              : #include "playback/playbacktypes.h"
      31              : #include "playback/playbackcommands.h"
      32              : 
      33              : #include "ui/toolconfig.h"
      34              : 
      35              : using namespace mu::playback;
      36              : using namespace mu::engraving;
      37              : using namespace muse;
      38              : using namespace muse::ui;
      39              : using namespace muse::uicomponents;
      40              : using namespace muse::audio;
      41              : 
      42              : static const QString PLAY_TOGGLE_ID("play-toggle");
      43              : 
      44            0 : static const ToolConfig& defaultPlaybackToolConfig()
      45              : {
      46            0 :     static ToolConfig config;
      47            0 :     if (!config.isValid()) {
      48            0 :         config.items = {
      49              :             { REWIND_COMMAND, true },
      50              :             { "play-toggle", true }, // virtual code
      51              :             { LOOP_TOGGLE_COMMAND, true },
      52              :             { LOOP_IN_COMMAND, true },
      53              :             { LOOP_OUT_COMMAND, true },
      54              :             { METRONOME_TOGGLE_COMMAND, true },
      55            0 :         };
      56              :     }
      57            0 :     return config;
      58            0 : }
      59              : 
      60            0 : PlaybackToolBarModel::PlaybackToolBarModel(QObject* parent)
      61            0 :     : AbstractMenuModel(parent)
      62              : {
      63            0 : }
      64              : 
      65            0 : void PlaybackToolBarModel::load()
      66              : {
      67            0 :     AbstractMenuModel::load();
      68            0 :     updateActions();
      69              : 
      70            0 :     connect(this, &PlaybackToolBarModel::isToolbarFloatingChanged, this, &PlaybackToolBarModel::updateActions);
      71              : 
      72            0 :     setupConnections();
      73            0 : }
      74              : 
      75            0 : void PlaybackToolBarModel::setupConnections()
      76              : {
      77            0 :     playbackController()->isPlayAllowedChanged().onReceive(this, [this](bool) {
      78            0 :         emit isPlayAllowedChanged();
      79            0 :     });
      80              : 
      81            0 :     playbackController()->isPlayingChanged().onReceive(this, [this](bool) {
      82            0 :         updatePlayItem();
      83            0 :     });
      84              : 
      85            0 :     playbackController()->playbackPositionChanged().onReceive(this, [this](secs_t secs) {
      86            0 :         updatePlayPosition(secs);
      87            0 :     });
      88              : 
      89            0 :     playbackController()->totalPlayTimeChanged().onNotify(this, [this]() {
      90            0 :         emit maxPlayTimeChanged();
      91            0 :         secs_t pos = playbackController()->playbackPosition();
      92            0 :         updatePlayPosition(pos);
      93            0 :     });
      94              : 
      95            0 :     playbackController()->currentTempoChanged().onNotify(this, [this]() {
      96            0 :         emit tempoChanged();
      97            0 :     });
      98            0 : }
      99              : 
     100            0 : void PlaybackToolBarModel::updateActions()
     101              : {
     102            0 :     MenuItemList result;
     103              :     MenuItemList settingsItems = {
     104            0 :         makeMenuItem(MIDI_TOGGLE_COMMAND),
     105            0 :         makeInputPitchMenu(),
     106            0 :         makeSeparator(),
     107            0 :         makeMenuItem(REPEATS_TOGGLE_COMMAND),
     108            0 :         makeMenuItem(CHORDSYMBOLS_TOGGLE_COMMAND),
     109            0 :         makeMenuItem(HEAR_PLAYBACK_WHEN_EDITING_TOGGLE_COMMAND),
     110            0 :         makeMenuItem(PAN_TOGGLE_COMMAND),
     111            0 :         makeMenuItem(COUNTIN_TOGGLE_COMMAND),
     112            0 :     };
     113              : 
     114            0 :     if (!m_isToolbarFloating) {
     115            0 :         settingsItems << makeSeparator();
     116              :     }
     117              : 
     118              :     //! NOTE At the moment no customization ability
     119            0 :     ToolConfig config = defaultPlaybackToolConfig();
     120            0 :     for (const ToolConfig::Item& item : config.items) {
     121            0 :         if (item.intent == PLAY_TOGGLE_ID) {
     122            0 :             result << makePlayItem();
     123              :         } else {
     124            0 :             const rcommand::Command command(item.intent);
     125            0 :             if (isAdditionalCommand(command) && !m_isToolbarFloating) {
     126              :                 //! NOTE In this case, we want to see the actions' description instead of the title
     127            0 :                 settingsItems << makeMenuItem(command);
     128              :             } else {
     129            0 :                 result << makeMenuItem(command);
     130              :             }
     131            0 :         }
     132              :     }
     133              : 
     134            0 :     MenuItem* settingsItem = makeMenu(TranslatableString("action", "Playback settings"), settingsItems);
     135            0 :     settingsItem->setIcon(ui::IconCode::Code::SETTINGS_COG);
     136            0 :     result << settingsItem;
     137              : 
     138            0 :     setItems(result);
     139            0 : }
     140              : 
     141            0 : MenuItem* PlaybackToolBarModel::makeInputPitchMenu()
     142              : {
     143              :     MenuItemList items = {
     144            0 :         makeMenuItem(MIDI_INPUT_WRITTEN_PITCH_COMMAND),
     145            0 :         makeMenuItem(MIDI_INPUT_SOUNDING_PITCH_COMMAND),
     146            0 :     };
     147              : 
     148            0 :     MenuItem* menu = makeMenu(muse::TranslatableString("playback", "MIDI input pitch"), items);
     149            0 :     menu->setIcon(ui::IconCode::Code::MUSIC_NOTES);
     150            0 :     return menu;
     151            0 : }
     152              : 
     153            0 : bool PlaybackToolBarModel::isAdditionalCommand(const muse::rcommand::Command& command) const
     154              : {
     155              :     static const std::vector<muse::rcommand::Command> additionalCommands = {
     156              :         LOOP_IN_COMMAND,
     157              :         LOOP_OUT_COMMAND,
     158            0 :     };
     159              : 
     160            0 :     return muse::contains(additionalCommands, command);
     161            0 : }
     162              : 
     163            0 : bool PlaybackToolBarModel::isPlayAllowed() const
     164              : {
     165            0 :     return playbackController()->isPlayAllowed();
     166              : }
     167              : 
     168            0 : bool PlaybackToolBarModel::isToolbarFloating() const
     169              : {
     170            0 :     return m_isToolbarFloating;
     171              : }
     172              : 
     173            0 : void PlaybackToolBarModel::setIsToolbarFloating(bool floating)
     174              : {
     175            0 :     if (floating == m_isToolbarFloating) {
     176            0 :         return;
     177              :     }
     178              : 
     179            0 :     m_isToolbarFloating = floating;
     180            0 :     emit isToolbarFloatingChanged(floating);
     181              : }
     182              : 
     183            0 : QTime PlaybackToolBarModel::maxPlayTime() const
     184              : {
     185            0 :     return timeFromSeconds(totalPlayTime());
     186              : }
     187              : 
     188            0 : QTime PlaybackToolBarModel::playTime() const
     189              : {
     190            0 :     return timeFromSeconds(m_playbackPositionSecs);
     191              : }
     192              : 
     193            0 : void PlaybackToolBarModel::setPlayTime(const QTime& time)
     194              : {
     195            0 :     rewind(timeToSeconds(time));
     196            0 : }
     197              : 
     198            0 : qreal PlaybackToolBarModel::playPosition() const
     199              : {
     200            0 :     const secs_t totalSecs = totalPlayTime();
     201            0 :     if (totalSecs.is_zero()) {
     202            0 :         return 0.0;
     203              :     }
     204              : 
     205            0 :     const qreal position = static_cast<qreal>(m_playbackPositionSecs) / static_cast<qreal>(totalSecs);
     206            0 :     return position;
     207              : }
     208              : 
     209            0 : void PlaybackToolBarModel::setPlayPosition(qreal position)
     210              : {
     211            0 :     rewind(totalPlayTime() * position);
     212            0 : }
     213              : 
     214            0 : secs_t PlaybackToolBarModel::totalPlayTime() const
     215              : {
     216            0 :     return playbackController()->totalPlayTime();
     217              : }
     218              : 
     219            0 : MeasureBeat PlaybackToolBarModel::measureBeat() const
     220              : {
     221            0 :     return playbackController()->currentBeat();
     222              : }
     223              : 
     224            0 : muse::uicomponents::MenuItem* PlaybackToolBarModel::makePlayItem()
     225              : {
     226            0 :     rcommand::Command command = playbackController()->isPlaying() ? PAUSE_COMMAND : PLAY_COMMAND;
     227            0 :     MenuItem* item = makeMenuItem(command);
     228            0 :     item->setId(PLAY_TOGGLE_ID);
     229            0 :     return item;
     230            0 : }
     231              : 
     232            0 : void PlaybackToolBarModel::updatePlayItem()
     233              : {
     234            0 :     MenuItem& item = findItem(PLAY_TOGGLE_ID);
     235            0 :     if (item.isValid()) {
     236            0 :         rcommand::Command command = playbackController()->isPlaying() ? PAUSE_COMMAND : PLAY_COMMAND;
     237            0 :         item.setCommandInfo(commandsRegister()->commandInfo(command));
     238            0 :         item.setId(PLAY_TOGGLE_ID);
     239            0 :     }
     240            0 : }
     241              : 
     242            0 : void PlaybackToolBarModel::updatePlayPosition(secs_t secs)
     243              : {
     244            0 :     if (m_playbackPositionSecs == secs) {
     245            0 :         return;
     246              :     }
     247              : 
     248            0 :     m_playbackPositionSecs = secs;
     249            0 :     emit playPositionChanged();
     250              : }
     251              : 
     252            0 : void PlaybackToolBarModel::rewind(secs_t secs)
     253              : {
     254            0 :     if (m_playbackPositionSecs == secs || secs > totalPlayTime()) {
     255            0 :         return;
     256              :     }
     257              : 
     258            0 :     dispatchCommand(REWIND_COMMAND, { { "position", Val(secs) } });
     259            0 : }
     260              : 
     261            0 : void PlaybackToolBarModel::rewindToBeat(const MeasureBeat& beat)
     262              : {
     263            0 :     secs_t secs = playbackController()->beatToSecs(beat.measureIndex, static_cast<int>(beat.beat));
     264            0 :     rewind(secs);
     265            0 : }
     266              : 
     267            0 : int PlaybackToolBarModel::measureNumber() const
     268              : {
     269            0 :     return measureBeat().measureIndex + 1;
     270              : }
     271              : 
     272            0 : void PlaybackToolBarModel::setMeasureNumber(int measureNumber)
     273              : {
     274            0 :     int measureIndex = measureNumber - 1;
     275            0 :     MeasureBeat measureBeat = this->measureBeat();
     276              : 
     277            0 :     if (measureIndex == measureBeat.measureIndex) {
     278            0 :         return;
     279              :     }
     280              : 
     281            0 :     measureBeat.measureIndex = measureIndex;
     282            0 :     rewindToBeat(measureBeat);
     283              : }
     284              : 
     285            0 : int PlaybackToolBarModel::maxMeasureNumber() const
     286              : {
     287            0 :     return measureBeat().maxMeasureIndex + 1;
     288              : }
     289              : 
     290            0 : int PlaybackToolBarModel::beatNumber() const
     291              : {
     292            0 :     return static_cast<int>(measureBeat().beat) + 1;
     293              : }
     294              : 
     295            0 : void PlaybackToolBarModel::setBeatNumber(int beatNumber)
     296              : {
     297            0 :     int beatIndex = beatNumber - 1;
     298            0 :     MeasureBeat measureBeat = this->measureBeat();
     299              : 
     300            0 :     if (beatIndex == static_cast<int>(measureBeat.beat)) {
     301            0 :         return;
     302              :     }
     303              : 
     304            0 :     measureBeat.beat = beatIndex;
     305            0 :     rewindToBeat(measureBeat);
     306              : }
     307              : 
     308            0 : void PlaybackToolBarModel::setTempoMultiplier(qreal multiplier)
     309              : {
     310            0 :     if (muse::RealIsEqual(multiplier, tempoMultiplier())) {
     311            0 :         return;
     312              :     }
     313              : 
     314            0 :     playbackController()->setTempoMultiplier(multiplier);
     315            0 :     emit tempoChanged();
     316              : }
     317              : 
     318            0 : int PlaybackToolBarModel::maxBeatNumber() const
     319              : {
     320            0 :     return measureBeat().maxBeatIndex + 1;
     321              : }
     322              : 
     323            0 : QVariant PlaybackToolBarModel::tempo() const
     324              : {
     325              :     static const std::unordered_map<DurationType, MusicalSymbolCodes::Code> DURATION_TO_ICON {
     326              :         { DurationType::V_WHOLE, MusicalSymbolCodes::Code::SEMIBREVE },
     327              :         { DurationType::V_HALF, MusicalSymbolCodes::Code::MINIM },
     328              :         { DurationType::V_QUARTER, MusicalSymbolCodes::Code::CROTCHET },
     329              :         { DurationType::V_EIGHTH, MusicalSymbolCodes::Code::QUAVER },
     330              :         { DurationType::V_16TH, MusicalSymbolCodes::Code::SEMIQUAVER },
     331            0 :     };
     332              : 
     333            0 :     const notation::Tempo& tempo = playbackController()->currentTempo();
     334            0 :     const MusicalSymbolCodes::Code noteIcon = muse::value(DURATION_TO_ICON, tempo.duration,
     335            0 :                                                           MusicalSymbolCodes::Code::CROTCHET);
     336              : 
     337            0 :     QVariantMap obj;
     338            0 :     obj["noteSymbol"] = musicalSymbolToString(noteIcon, tempo.withDot);
     339            0 :     obj["value"] = tempo.valueBpm;
     340              : 
     341            0 :     return obj;
     342            0 : }
     343              : 
     344            0 : qreal PlaybackToolBarModel::tempoMultiplier() const
     345              : {
     346            0 :     return playbackController()->tempoMultiplier();
     347              : }
        

Generated by: LCOV version 2.0-1