LCOV - code coverage report
Current view: top level - src/notation/internal - notationmidiinput.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 0.0 % 294 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              : 
      23              : #include "notationmidiinput.h"
      24              : 
      25              : #include <QGuiApplication>
      26              : 
      27              : #include "containers.h"
      28              : #include "defer.h"
      29              : 
      30              : #include "midi/miditypes.h"
      31              : 
      32              : #include "engraving/dom/factory.h"
      33              : #include "engraving/dom/note.h"
      34              : #include "engraving/dom/score.h"
      35              : #include "engraving/dom/segment.h"
      36              : #include "engraving/editing/noteinput.h"
      37              : #include "engraving/editing/transaction/transaction.h"
      38              : 
      39              : #include "notation/inotationnoteinput.h"
      40              : #include "notation/inotationselection.h" // IWYU pragma: keep
      41              : 
      42              : #include "playback/playbackcommands.h"
      43              : #include "notationscene/notationcommands.h"
      44              : 
      45              : #include "log.h"
      46              : 
      47              : using namespace mu::notation;
      48              : using namespace mu::engraving;
      49              : 
      50              : static constexpr int PROCESS_INTERVAL = 20;
      51              : 
      52            0 : static mu::playback::IPlaybackController::PlayParams makeNoteOnParams(bool infiniteDuration)
      53              : {
      54            0 :     mu::playback::IPlaybackController::PlayParams params;
      55            0 :     params.flushSound = !infiniteDuration;
      56              : 
      57            0 :     if (infiniteDuration) {
      58            0 :         params.duration = muse::mpe::INFINITE_DURATION; // play note on only
      59              :     }
      60              : 
      61            0 :     return params;
      62              : }
      63              : 
      64            0 : static mu::playback::IPlaybackController::PlayParams makeNoteOffParams()
      65              : {
      66            0 :     mu::playback::IPlaybackController::PlayParams params;
      67            0 :     params.flushSound = false;
      68            0 :     params.duration = 0; // note off only
      69              : 
      70            0 :     return params;
      71              : }
      72              : 
      73            0 : NotationMidiInput::NotationMidiInput(IGetScore* getScore, INotationInteractionPtr notationInteraction,
      74            0 :                                      INotationUndoStackPtr undoStack, const muse::modularity::ContextPtr& iocCtx)
      75            0 :     : muse::Contextable(iocCtx), m_getScore(getScore),
      76            0 :     m_notationInteraction(notationInteraction), m_undoStack(undoStack)
      77              : {
      78            0 :     QObject::connect(&m_processTimer, &QTimer::timeout, [this]() { doProcessEvents(); });
      79              : 
      80            0 :     m_realtimeTimer.setTimerType(Qt::PreciseTimer);
      81            0 :     QObject::connect(&m_realtimeTimer, &QTimer::timeout, [this]() { doRealtimeAdvance(); });
      82              : 
      83            0 :     m_extendNoteTimer.setTimerType(Qt::PreciseTimer);
      84            0 :     m_extendNoteTimer.setSingleShot(true);
      85            0 :     QObject::connect(&m_extendNoteTimer, &QTimer::timeout, [this]() { doExtendCurrentNote(); });
      86            0 : }
      87              : 
      88            0 : void NotationMidiInput::onMidiEventReceived(const muse::midi::Event& event)
      89              : {
      90            0 :     if (event.isChannelVoice20()) {
      91            0 :         auto events = event.toMIDI10();
      92            0 :         for (auto& midi10event : events) {
      93            0 :             onMidiEventReceived(midi10event);
      94              :         }
      95              : 
      96            0 :         return;
      97            0 :     }
      98              : 
      99              :     const static std::unordered_set<muse::midi::Event::Opcode> ACCEPTED_OPCODES {
     100              :         muse::midi::Event::Opcode::NoteOn,
     101              :         muse::midi::Event::Opcode::NoteOff,
     102              :         muse::midi::Event::Opcode::ControlChange,
     103              :         muse::midi::Event::Opcode::PitchBend,
     104            0 :     };
     105              : 
     106            0 :     if (muse::contains(ACCEPTED_OPCODES, event.opcode())) {
     107            0 :         m_eventsQueue.push_back(event);
     108              : 
     109            0 :         if (!m_processTimer.isActive()) {
     110            0 :             m_processTimer.start(PROCESS_INTERVAL);
     111              :         }
     112              :     }
     113              : }
     114              : 
     115            0 : muse::async::Channel<std::vector<const Note*> > NotationMidiInput::notesReceived() const
     116              : {
     117            0 :     return m_notesReceivedChannel;
     118              : }
     119              : 
     120            0 : void NotationMidiInput::onRealtimeAdvance()
     121              : {
     122            0 :     if (!isNoteInputMode()) {
     123            0 :         return;
     124              :     }
     125              : 
     126            0 :     if (isRealtimeManual()) {
     127            0 :         m_allowRealtimeRests = true;
     128            0 :         enableMetronome();
     129            0 :         doRealtimeAdvance();
     130            0 :     } else if (isRealtimeAuto()) {
     131            0 :         if (m_realtimeTimer.isActive()) {
     132            0 :             stopRealtime();
     133            0 :             disableMetronome();
     134              :         } else {
     135            0 :             m_allowRealtimeRests = true;
     136            0 :             enableMetronome();
     137            0 :             runRealtime();
     138              :         }
     139              :     }
     140              : }
     141              : 
     142            0 : Score* NotationMidiInput::score() const
     143              : {
     144            0 :     IF_ASSERT_FAILED(m_getScore) {
     145            0 :         return nullptr;
     146              :     }
     147              : 
     148            0 :     return m_getScore->score();
     149              : }
     150              : 
     151            0 : void NotationMidiInput::doProcessEvents()
     152              : {
     153            0 :     DEFER {
     154            0 :         m_eventsQueue.clear();
     155            0 :         m_processTimer.stop();
     156            0 :     };
     157              : 
     158            0 :     if (m_eventsQueue.empty()) {
     159            0 :         return;
     160              :     }
     161              : 
     162            0 :     const Score* sc = score();
     163            0 :     if (!sc || sc->noStaves()) {
     164            0 :         return;
     165              :     }
     166              : 
     167            0 :     std::vector<const Note*> notesOn;
     168            0 :     std::vector<int> notesOff;
     169            0 :     ControllerEventMap controllers;
     170              : 
     171            0 :     startNoteInputIfNeed();
     172            0 :     bool isNoteInput = isNoteInputMode();
     173            0 :     bool isSoundPreview = !isNoteInput;
     174              : 
     175            0 :     if (isNoteInput && isInputByDuration()) {
     176            0 :         addNoteEventsToInputState();
     177            0 :         return;
     178              :     }
     179              : 
     180            0 :     const bool useDurationAndVelocity = isSoundPreview || configuration()->useMidiVelocityAndDurationDuringNoteInput();
     181              : 
     182            0 :     for (size_t i = 0; i < m_eventsQueue.size(); ++i) {
     183            0 :         const muse::midi::Event& event = m_eventsQueue.at(i);
     184            0 :         const muse::midi::Event::Opcode opcode = event.opcode();
     185              : 
     186            0 :         if (opcode == muse::midi::Event::Opcode::ControlChange || opcode == muse::midi::Event::Opcode::PitchBend) {
     187            0 :             controllers[opcode] = event; // keep only last received to prevent spam
     188            0 :             continue;
     189              :         }
     190              : 
     191            0 :         Note* note = isNoteInput ? addNoteToScore(event) : makePreviewNote(event);
     192            0 :         if (note) {
     193            0 :             if (useDurationAndVelocity) {
     194            0 :                 note->setUserVelocity(event.velocity7());
     195            0 :                 m_playingNotes[note->pitch()] = { isSoundPreview, note };
     196              :             }
     197            0 :             notesOn.push_back(note);
     198              :         }
     199              : 
     200            0 :         const bool chord = i != 0;
     201            0 :         const bool noteOn = opcode == muse::midi::Event::Opcode::NoteOn;
     202            0 :         if (!chord && noteOn && !m_realtimeTimer.isActive() && isRealtimeAuto()) {
     203            0 :             m_extendNoteTimer.start(configuration()->delayBetweenNotesInRealTimeModeMilliseconds());
     204            0 :             enableMetronome();
     205            0 :             doRealtimeAdvance();
     206              :         }
     207              : 
     208            0 :         const bool noteOff = opcode == muse::midi::Event::Opcode::NoteOff || event.velocity7() == 0;
     209            0 :         if (useDurationAndVelocity && noteOff) {
     210            0 :             notesOff.push_back(event.note());
     211              :         }
     212              :     }
     213              : 
     214            0 :     if (!controllers.empty()) {
     215            0 :         triggerControllers(controllers);
     216              :     }
     217              : 
     218            0 :     if (!notesOn.empty()) {
     219            0 :         if (isNoteInput) {
     220            0 :             playbackController()->seekElement(notesOn.front(), !useDurationAndVelocity /*flushSound*/);
     221              :         }
     222              : 
     223            0 :         const std::vector<const EngravingItem*> elements(notesOn.begin(), notesOn.end());
     224            0 :         playbackController()->playElements(elements, makeNoteOnParams(useDurationAndVelocity), true);
     225            0 :         m_notesReceivedChannel.send(notesOn);
     226            0 :     }
     227              : 
     228            0 :     if (!notesOff.empty()) {
     229            0 :         releasePlayingNotes(notesOff);
     230              :     }
     231            0 : }
     232              : 
     233            0 : void NotationMidiInput::startNoteInputIfNeed()
     234              : {
     235            0 :     if (isNoteInputMode()) {
     236            0 :         return;
     237              :     }
     238              : 
     239            0 :     const auto containsNoteOn = [this]() -> bool {
     240            0 :         return std::any_of(m_eventsQueue.begin(), m_eventsQueue.end(), [](const muse::midi::Event& e) {
     241            0 :             return e.opcode() == muse::midi::Event::Opcode::NoteOn;
     242            0 :         });
     243            0 :     };
     244              : 
     245            0 :     if (configuration()->startNoteInputAtSelectedNoteRestWhenPressingMidiKey()) {
     246            0 :         if (m_notationInteraction->selection()->elementsSelected(NOTE_REST_TYPES) && containsNoteOn()) {
     247            0 :             commandsDispatcher()->dispatch(TOGGLE_NOTE_INPUT_COMMAND);
     248              :         }
     249              :     }
     250              : }
     251              : 
     252            0 : void NotationMidiInput::addNoteEventsToInputState()
     253              : {
     254            0 :     INotationNoteInputPtr noteInput = m_notationInteraction->noteInput();
     255            0 :     const NoteInputState& state = noteInput->state();
     256            0 :     const staff_idx_t staffIdx = state.staffIdx();
     257            0 :     const bool useWrittenPitch = configuration()->midiUseWrittenPitch().val;
     258            0 :     const bool playPreviewNotes = configuration()->isPlayPreviewNotesInInputByDuration();
     259            0 :     const bool useVelocityAndDuration = playPreviewNotes && configuration()->useMidiVelocityAndDurationDuringNoteInput();
     260              : 
     261            0 :     NoteValList notesOn;
     262            0 :     NoteValList notesOff;
     263              : 
     264            0 :     if (m_holdingNotesInInputByDuration) {
     265            0 :         notesOn = state.notes();
     266              :     }
     267              : 
     268            0 :     ControllerEventMap controllers;
     269              : 
     270            0 :     for (const muse::midi::Event& event : m_eventsQueue) {
     271            0 :         const muse::midi::Event::Opcode opcode = event.opcode();
     272              : 
     273            0 :         if (opcode == muse::midi::Event::Opcode::NoteOn) {
     274            0 :             NoteVal nval = NoteInput::noteVal(score(), event.note(), staffIdx, useWrittenPitch);
     275            0 :             nval.velocityOverride = event.velocity7();
     276            0 :             notesOn.push_back(nval);
     277            0 :             m_holdingNotesInInputByDuration = true;
     278            0 :         } else if (opcode == muse::midi::Event::Opcode::NoteOff) {
     279            0 :             if (useVelocityAndDuration) {
     280            0 :                 notesOff.push_back(NoteInput::noteVal(score(), event.note(), staffIdx, useWrittenPitch));
     281              :             }
     282            0 :             m_holdingNotesInInputByDuration = false;
     283            0 :         } else if (opcode == muse::midi::Event::Opcode::ControlChange || opcode == muse::midi::Event::Opcode::PitchBend) {
     284            0 :             if (playPreviewNotes) {
     285            0 :                 controllers[opcode] = event; // keep only last received to prevent spam
     286              :             }
     287              :         }
     288              :     }
     289              : 
     290            0 :     if (!controllers.empty()) {
     291            0 :         triggerControllers(controllers);
     292              :     }
     293              : 
     294            0 :     if (!notesOff.empty()) {
     295            0 :         playbackController()->playNotes(notesOff, staffIdx, state.segment(), makeNoteOffParams());
     296              :     }
     297              : 
     298            0 :     if (!notesOn.empty() && notesOn != state.notes()) {
     299            0 :         noteInput->setRestMode(false);
     300            0 :         noteInput->setInputNotes(notesOn);
     301              : 
     302            0 :         if (playPreviewNotes) {
     303            0 :             playbackController()->playNotes(notesOn, staffIdx, state.segment(), makeNoteOnParams(useVelocityAndDuration));
     304              :         }
     305              :     }
     306            0 : }
     307              : 
     308            0 : Note* NotationMidiInput::addNoteToScore(const muse::midi::Event& e)
     309              : {
     310            0 :     Score* sc = score();
     311              : 
     312            0 :     const int pitch = e.note();
     313              : 
     314            0 :     muse::remove(m_activeMidiPitches, pitch);
     315              : 
     316            0 :     const InputState& is = sc->inputState();
     317            0 :     if (!is.noteEntryMode()) {
     318            0 :         return nullptr;
     319              :     }
     320              : 
     321            0 :     if (!is.isValid()) {
     322            0 :         return nullptr;
     323              :     }
     324              : 
     325            0 :     DEFER {
     326            0 :         m_undoStack->commitChanges();
     327            0 :     };
     328              : 
     329            0 :     m_undoStack->prepareChanges(muse::TranslatableString("undoableAction", "Enter note"));
     330              : 
     331            0 :     if (e.opcode() == muse::midi::Event::Opcode::NoteOff) {
     332            0 :         if (isRealtime()) {
     333            0 :             if (!is.cr()) {
     334            0 :                 return nullptr;
     335              :             }
     336              : 
     337            0 :             const Chord* chord = is.cr()->isChord() ? engraving::toChord(is.cr()) : nullptr;
     338            0 :             if (chord) {
     339            0 :                 Note* n = chord->findNote(pitch);
     340            0 :                 if (n) {
     341            0 :                     sc->deleteItem(n->tieBack());
     342            0 :                     sc->deleteItem(n);
     343              :                 }
     344              :             }
     345              :         }
     346              : 
     347            0 :         return nullptr;
     348              :     }
     349              : 
     350            0 :     bool addToChord = !m_activeMidiPitches.empty();
     351              : 
     352              :     // holding shift while inputting midi will add the new pitch to the prior existing chord
     353            0 :     if (QGuiApplication::queryKeyboardModifiers() & Qt::ShiftModifier) {
     354            0 :         EngravingItem* cr = is.lastSegment()->element(is.track());
     355            0 :         if (cr && cr->isChord()) {
     356            0 :             addToChord = true;
     357              :         }
     358              :     }
     359              : 
     360            0 :     Note* note = NoteInput::addMidiPitch(sc->transactionManager()->currentOrDummyTransaction(), sc, pitch,
     361            0 :                                          addToChord, configuration()->midiUseWrittenPitch().val);
     362              : 
     363            0 :     m_activeMidiPitches.push_back(pitch);
     364              : 
     365            0 :     if (is.cr()) {
     366            0 :         m_notationInteraction->showItem(is.cr());
     367              :     }
     368              : 
     369            0 :     return note;
     370            0 : }
     371              : 
     372            0 : Note* NotationMidiInput::makePreviewNote(const muse::midi::Event& e)
     373              : {
     374            0 :     if (e.opcode() == muse::midi::Event::Opcode::NoteOff || e.velocity7() == 0) {
     375            0 :         return nullptr;
     376              :     }
     377              : 
     378            0 :     Score* score = this->score();
     379            0 :     const InputState& inputState = score->inputState();
     380            0 :     const engraving::DummyParentOr<Segment> seg = engraving::parentOrDummy(inputState.lastSegment(), score->dummy());
     381              : 
     382            0 :     Chord* chord = engraving::Factory::createChord(seg);
     383              : 
     384            0 :     const ChordRest* cr = inputState.cr();
     385            0 :     const staff_idx_t staffIdx = cr ? engraving::track2staff(cr->track()) : 0;
     386              : 
     387            0 :     Note* note = engraving::Factory::createNote(chord);
     388            0 :     note->setStaffIdx(staffIdx);
     389              : 
     390            0 :     engraving::NoteVal nval = NoteInput::noteVal(score, e.note(), staffIdx, configuration()->midiUseWrittenPitch().val);
     391            0 :     note->setNval(nval);
     392              : 
     393            0 :     return note;
     394              : }
     395              : 
     396            0 : void NotationMidiInput::triggerControllers(const ControllerEventMap& events)
     397              : {
     398            0 :     muse::mpe::ControllerChangeEventList controllers;
     399              : 
     400              :     static const std::unordered_map<int, muse::mpe::ControllerChangeEvent::Type> MIDI_CC_TO_EVENT_TYPE {
     401              :         { muse::midi::MODWHEEL_CONTROLLER, muse::mpe::ControllerChangeEvent::Modulation },
     402              :         { muse::midi::SUSTAIN_PEDAL_CONTROLLER, muse::mpe::ControllerChangeEvent::SustainPedalOnOff },
     403            0 :     };
     404              : 
     405            0 :     const InputState& is = score()->inputState();
     406              : 
     407            0 :     for (const auto& pair : events) {
     408            0 :         const muse::midi::Event& e = pair.second;
     409            0 :         muse::mpe::ControllerChangeEvent cc;
     410              : 
     411            0 :         if (pair.first == muse::midi::Event::Opcode::PitchBend) {
     412            0 :             cc.type = muse::mpe::ControllerChangeEvent::PitchBend;
     413            0 :             cc.val = static_cast<float>(e.pitchBend14()) / 16383.f;
     414              :         } else {
     415            0 :             cc.type = muse::value(MIDI_CC_TO_EVENT_TYPE, e.index(), muse::mpe::ControllerChangeEvent::Undefined);
     416            0 :             cc.val = static_cast<float>(e.data()) / 127.f;
     417              :         }
     418              : 
     419            0 :         if (cc.type != muse::mpe::ControllerChangeEvent::Undefined) {
     420            0 :             cc.layerIdx = static_cast<muse::mpe::layer_idx_t>(is.track());
     421            0 :             controllers.push_back(cc);
     422              :         }
     423              :     }
     424              : 
     425            0 :     playbackController()->triggerControllers(controllers, is.staffIdx(), is.tick().ticks());
     426            0 : }
     427              : 
     428            0 : void NotationMidiInput::releasePlayingNotes(const std::vector<int>& pitches)
     429              : {
     430            0 :     std::vector<const EngravingItem*> notesOff;
     431            0 :     std::vector<Note*> notesToDelete;
     432              : 
     433            0 :     const staff_idx_t staffIdx = score()->inputState().staffIdx();
     434            0 :     const bool useWrittenPitch = configuration()->midiUseWrittenPitch().val;
     435              : 
     436            0 :     for (int pitch : pitches) {
     437            0 :         const NoteVal nval = NoteInput::noteVal(score(), pitch, staffIdx, useWrittenPitch);
     438              : 
     439            0 :         auto it = m_playingNotes.find(nval.pitch);
     440            0 :         if (it == m_playingNotes.end()) {
     441            0 :             continue;
     442              :         }
     443              : 
     444            0 :         Note* note = it->second.note;
     445            0 :         note->setUserVelocity(0);
     446              : 
     447            0 :         notesOff.push_back(note);
     448              : 
     449            0 :         if (it->second.isPreview) {
     450            0 :             notesToDelete.push_back(note);
     451              :         }
     452              : 
     453            0 :         m_playingNotes.erase(it);
     454              :     }
     455              : 
     456            0 :     playbackController()->playElements(notesOff, makeNoteOffParams(), true /*isMidi*/);
     457            0 :     muse::DeleteAll(notesToDelete);
     458            0 : }
     459              : 
     460            0 : void NotationMidiInput::enableMetronome()
     461              : {
     462            0 :     bool metronomeEnabled = configuration()->isMetronomeEnabled();
     463            0 :     if (metronomeEnabled) {
     464            0 :         return;
     465              :     }
     466              : 
     467            0 :     commandsDispatcher()->dispatch(mu::playback::METRONOME_TOGGLE_COMMAND);
     468            0 :     m_shouldDisableMetronome = true;
     469              : }
     470              : 
     471            0 : void NotationMidiInput::disableMetronome()
     472              : {
     473            0 :     if (!m_shouldDisableMetronome) {
     474            0 :         return;
     475              :     }
     476              : 
     477            0 :     commandsDispatcher()->dispatch(mu::playback::METRONOME_TOGGLE_COMMAND);
     478              : 
     479            0 :     m_shouldDisableMetronome = false;
     480              : }
     481              : 
     482            0 : void NotationMidiInput::runRealtime()
     483              : {
     484            0 :     m_realtimeTimer.start(configuration()->delayBetweenNotesInRealTimeModeMilliseconds());
     485            0 : }
     486              : 
     487            0 : void NotationMidiInput::stopRealtime()
     488              : {
     489            0 :     m_realtimeTimer.stop();
     490            0 : }
     491              : 
     492            0 : void NotationMidiInput::doRealtimeAdvance()
     493              : {
     494            0 :     if (!isRealtime() || !isNoteInputMode() || (!m_allowRealtimeRests && m_activeMidiPitches.empty())) {
     495            0 :         if (m_realtimeTimer.isActive()) {
     496            0 :             stopRealtime();
     497              :         }
     498              : 
     499            0 :         disableMetronome();
     500              : 
     501            0 :         m_allowRealtimeRests = true;
     502            0 :         return;
     503              :     }
     504              : 
     505            0 :     const InputState& is = m_getScore->score()->inputState();
     506            0 :     playbackController()->playMetronome(is.tick().ticks());
     507              : 
     508            0 :     QTimer::singleShot(100, Qt::PreciseTimer, [this]() {
     509            0 :         m_undoStack->transaction(muse::TranslatableString("undoableAction", "Realtime advance"), [this](Transaction& tx) {
     510            0 :             NoteInput::realtimeAdvance(tx, score(), configuration()->midiUseWrittenPitch().val, m_activeMidiPitches);
     511            0 :         });
     512            0 :     });
     513              : 
     514            0 :     if (isRealtimeManual()) {
     515            0 :         int metronomeDuration = 500;
     516            0 :         QTimer::singleShot(metronomeDuration, Qt::PreciseTimer, [this]() {
     517            0 :             disableMetronome();
     518            0 :         });
     519              :     }
     520              : }
     521              : 
     522            0 : void NotationMidiInput::doExtendCurrentNote()
     523              : {
     524            0 :     if (!isNoteInputMode() || m_realtimeTimer.isActive()) {
     525            0 :         return;
     526              :     }
     527              : 
     528            0 :     m_allowRealtimeRests = false;
     529            0 :     runRealtime();
     530            0 :     doRealtimeAdvance();
     531              : }
     532              : 
     533            0 : NoteInputMethod NotationMidiInput::noteInputMethod() const
     534              : {
     535            0 :     return m_notationInteraction->noteInput()->state().noteEntryMethod();
     536              : }
     537              : 
     538            0 : bool NotationMidiInput::isRealtime() const
     539              : {
     540            0 :     return isRealtimeAuto() || isRealtimeManual();
     541              : }
     542              : 
     543            0 : bool NotationMidiInput::isRealtimeAuto() const
     544              : {
     545            0 :     return noteInputMethod() == NoteInputMethod::REALTIME_AUTO;
     546              : }
     547              : 
     548            0 : bool NotationMidiInput::isRealtimeManual() const
     549              : {
     550            0 :     return noteInputMethod() == NoteInputMethod::REALTIME_MANUAL;
     551              : }
     552              : 
     553            0 : bool NotationMidiInput::isInputByDuration() const
     554              : {
     555            0 :     return noteInputMethod() == NoteInputMethod::BY_DURATION;
     556              : }
     557              : 
     558            0 : bool NotationMidiInput::isNoteInputMode() const
     559              : {
     560            0 :     return m_notationInteraction->noteInput()->isNoteInputMode();
     561              : }
        

Generated by: LCOV version 2.0-1