LCOV - code coverage report
Current view: top level - src/engraving/api/v1 - instrument.h Coverage Total Hit
Test: filtered_coverage.info Lines: 18.6 % 59 11
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              : #pragma once
      24              : 
      25              : #include "engraving/dom/drumset.h"
      26              : #include "engraving/dom/instrument.h"
      27              : 
      28              : #include <QQmlListProperty>
      29              : 
      30              : // api
      31              : #include "scoreelement.h"
      32              : 
      33              : #include "log.h"
      34              : 
      35              : namespace mu::engraving {
      36              : class Instrument;
      37              : }
      38              : 
      39              : namespace mu::engraving::apiv1 {
      40              : class Instrument;
      41              : 
      42              : //---------------------------------------------------------
      43              : //   Channel
      44              : ///   Provides an access to channel properties. Similar to
      45              : ///   Mixer, changes to some of the playback-related
      46              : ///   properties are not recorded to undo stack and are not
      47              : ///   revertable with standard user-visible "undo" action.
      48              : ///   Changing MIDI patch though is undoable in normal way
      49              : ///   ("dock" type plugins may need to call
      50              : ///   \ref Score.startCmd / \ref Score.endCmd for that to
      51              : ///   work properly).
      52              : ///
      53              : ///   Iterating over all channels in the current score can
      54              : ///   be done as follows:
      55              : ///   \code
      56              : ///   var parts = curScore.parts;
      57              : ///   for (var i = 0; i < parts.length; ++i) {
      58              : ///       var part = parts[i];
      59              : ///       var instrs = part.instruments;
      60              : ///       for (var j = 0; j < instrs.length; ++j) {
      61              : ///           var instr = instrs[j];
      62              : ///           var channels = instr.channels;
      63              : ///           for (var k = 0; k < channels.length; ++k) {
      64              : ///               var channel = channels[k];
      65              : ///               channel.volume = 64; // just for example, changing the channel's volume
      66              : ///           }
      67              : ///       }
      68              : ///   }
      69              : ///   \endcode
      70              : ///   \since MuseScore 3.5
      71              : //---------------------------------------------------------
      72              : 
      73              : class Channel : public QObject
      74              : {
      75              :     Q_OBJECT
      76              : 
      77              :     mu::engraving::InstrChannel* m_channel;
      78              :     mu::engraving::Part* m_part;
      79              : 
      80              :     /// Name of this channel
      81              :     Q_PROPERTY(QString name READ name)
      82              :     /// Whether this channel controls playback of chord symbols
      83              :     /// \since MuseScore 4.6
      84              :     Q_PROPERTY(bool isHarmonyChannel READ isHarmonyChannel)
      85              : 
      86              :     /// Channel volume, from 0 to 127.
      87              :     /// \note Changing this property is **not** revertable with a standard "undo"
      88              :     /// action. Plugins may need to handle reverting this property change if
      89              :     /// necessary.
      90              :     Q_PROPERTY(int volume READ volume WRITE setVolume)
      91              :     /// Channel pan, from 0 to 127.
      92              :     /// \note Changing this property is **not** revertable with a standard "undo"
      93              :     /// action. Plugins may need to handle reverting this property change if
      94              :     /// necessary.
      95              :     Q_PROPERTY(int pan READ pan WRITE setPan)
      96              :     /// Channel chorus, from 0 to 127.
      97              :     /// \note Changing this property is **not** revertable with a standard "undo"
      98              :     /// action. Plugins may need to handle reverting this property change if
      99              :     /// necessary.
     100              :     Q_PROPERTY(int chorus READ chorus WRITE setChorus)
     101              :     /// Channel reverb, from 0 to 127.
     102              :     /// \note Changing this property is **not** revertable with a standard "undo"
     103              :     /// action. Plugins may need to handle reverting this property change if
     104              :     /// necessary.
     105              :     Q_PROPERTY(int reverb READ reverb WRITE setReverb)
     106              :     /// Whether this channel is muted.
     107              :     /// \note Changing this property is **not** revertable with a standard "undo"
     108              :     /// action. Plugins may need to handle reverting this property change if
     109              :     /// necessary.
     110              :     Q_PROPERTY(bool mute READ mute WRITE setMute)
     111              : 
     112              :     /// MIDI program number, from 0 to 127. Changing this property is recorded
     113              :     /// to the program's undo stack and can be reverted with a standard "undo"
     114              :     /// action.
     115              :     Q_PROPERTY(int midiProgram READ midiProgram WRITE setMidiProgram)
     116              :     /// MIDI patch bank number. Changing this property is recorded
     117              :     /// to the program's undo stack and can be reverted with a standard "undo"
     118              :     /// action.
     119              :     Q_PROPERTY(int midiBank READ midiBank WRITE setMidiBank)
     120              : 
     121              :     mu::engraving::InstrChannel* activeChannel();
     122              : 
     123              :     void setMidiBankAndProgram(int bank, int program, bool setUserBankController);
     124              : 
     125              : public:
     126              :     /// \cond MS_INTERNAL
     127            0 :     Channel(mu::engraving::InstrChannel* ch, mu::engraving::Part* p, QObject* parent = nullptr)
     128            0 :         : QObject(parent), m_channel(ch), m_part(p) {}
     129              : 
     130            0 :     QString name() const { return m_channel->name(); }
     131            0 :     bool isHarmonyChannel() const { return m_channel->isHarmonyChannel(); }
     132              : 
     133            0 :     int volume() const { return m_channel->volume(); }
     134            0 :     void setVolume(int val) { activeChannel()->setVolume(qBound(0, val, 127)); }
     135            0 :     int pan() const { return m_channel->pan(); }
     136            0 :     void setPan(int val) { activeChannel()->setPan(qBound(0, val, 127)); }
     137            0 :     int chorus() const { return m_channel->chorus(); }
     138            0 :     void setChorus(int val) { activeChannel()->setChorus(qBound(0, val, 127)); }
     139            0 :     int reverb() const { return m_channel->reverb(); }
     140            0 :     void setReverb(int val) { activeChannel()->setReverb(qBound(0, val, 127)); }
     141              : 
     142            0 :     bool mute() const
     143              :     {
     144            0 :         DEPRECATED;
     145              :         //!@NOTE since MuseScore 4.0 mixer doen/t work via Channel
     146            0 :         return false;
     147              :     }
     148              : 
     149            0 :     void setMute(bool)
     150              :     {
     151            0 :         DEPRECATED;
     152              :         //!@NOTE since MuseScore 4.0 mixer doen/t work via Channel
     153            0 :     }
     154              : 
     155            0 :     int midiProgram() const { return m_channel->program(); }
     156              :     void setMidiProgram(int prog);
     157            0 :     int midiBank() const { return m_channel->bank(); }
     158              :     void setMidiBank(int bank);
     159              :     /// \endcond
     160              : };
     161              : 
     162              : //---------------------------------------------------------
     163              : //   StringData
     164              : ///   \since MuseScore 3.5
     165              : //---------------------------------------------------------
     166              : 
     167              : class StringData : public QObject
     168              : {
     169              :     Q_OBJECT
     170              : 
     171              :     /// List of strings in this instrument.
     172              :     /// \returns A list of objects representing strings. Each
     173              :     /// object has the following fields:
     174              :     /// - \p pitch - pitch of this string on fret 0 (integer).
     175              :     /// - \p open - if \p true, this string is not fretted and
     176              :     ///             always open. If \p false, the string **is**
     177              :     ///             fretted. For example, for classical guitar
     178              :     ///             all strings are fretted, and for them
     179              :     ///             \p open value will always be \p false.
     180              :     Q_PROPERTY(QVariantList strings READ stringList)
     181              : 
     182              :     /// Number of frets in this instrument
     183              :     Q_PROPERTY(int frets READ frets)
     184              : 
     185              :     mu::engraving::StringData m_data;
     186              : 
     187              : public:
     188              :     /// \cond MS_INTERNAL
     189            0 :     StringData(const mu::engraving::StringData* d, QObject* parent = nullptr)
     190            0 :         : QObject(parent), m_data(*d) {}
     191              : 
     192              :     QVariantList stringList() const;
     193            0 :     int frets() const { return m_data.frets(); }
     194              :     /// \endcond
     195              : };
     196              : 
     197              : //---------------------------------------------------------
     198              : //   Drumset
     199              : ///   \since MuseScore 4.6
     200              : //---------------------------------------------------------
     201              : 
     202              : class Drumset : public QObject
     203              : {
     204              :     Q_OBJECT
     205              : 
     206              :     mu::engraving::Drumset* m_drumset;
     207              :     bool m_owned = false;
     208              : 
     209              : public:
     210              :     /// \cond MS_INTERNAL
     211            0 :     Drumset(mu::engraving::Drumset* d, QObject* parent = nullptr)
     212            0 :         : QObject(parent), m_drumset(d) {}
     213              : 
     214            1 :     Drumset(mu::engraving::Drumset* d, bool owned, QObject* parent = nullptr)
     215            1 :         : QObject(parent), m_drumset(d), m_owned(owned) {}
     216              : 
     217            2 :     ~Drumset() override
     218            1 :     {
     219            1 :         if (m_owned) {
     220            1 :             delete m_drumset;
     221              :         }
     222            2 :     }
     223              : 
     224            1 :     mu::engraving::Drumset* drumset() { return m_drumset; }
     225              :     const mu::engraving::Drumset* drumset() const { return m_drumset; }
     226              :     /// \endcond
     227              : 
     228              :     /// Whether the given MIDI pitch corresponds to a note in the drumset.
     229              :     /// \param pitch The pitch to test for.
     230            0 :     Q_INVOKABLE bool isValid(int pitch) { return drumset()->isValid(pitch); }
     231              : 
     232              :     /// The notehead group for the given pitch, corresponding to one of the
     233              :     /// PluginAPI::PluginAPI::NoteHeadGroup values. If the value corresponds to
     234              :     /// NoteHeadGroup.HEAD_CUSTOM, the actual notehead must be found using
     235              :     /// PluginAPI::Drumset::noteHeads(pitch, NoteHeadType)
     236              :     /// \see PluginAPI::PluginAPI::NoteHeadGroup
     237              :     /// \see PluginAPI::Drumset::noteHeads
     238              :     /// \param pitch The pitch to find the notehead group for.
     239            0 :     Q_INVOKABLE int noteHead(int pitch) { return int(drumset()->noteHead(pitch)); }
     240              : 
     241              :     /// The notehead symbol for the given pitch and NoteHeadType, corresponding to
     242              :     /// one of the PluginAPI::PluginAPI::SymId values.
     243              :     /// \see PluginAPI::PluginAPI::SymId
     244              :     /// \see PluginAPI::PluginAPI::NoteHeadType
     245              :     /// \param pitch The pitch to find the notehead symbol for.
     246              :     /// \param type The NoteHeadType to find the notehead symbol for.
     247            0 :     Q_INVOKABLE int noteHeads(int pitch, int type)
     248              :     {
     249            0 :         return int(drumset()->noteHeads(pitch, mu::engraving::NoteHeadType(type)));
     250              :     }
     251              : 
     252              :     /// The line a note with the given pitch would be displayed on.
     253              :     /// \param pitch The pitch of the note to find the line for.
     254            0 :     Q_INVOKABLE int line(int pitch) { return drumset()->line(pitch); }
     255              : 
     256              :     /// The voice a note with the given pitch would be added to.
     257              :     /// \param pitch The pitch of the note to find the voice for.
     258            0 :     Q_INVOKABLE int voice(int pitch) { return drumset()->voice(pitch); }
     259              : 
     260              :     /// The default stem direction a note with the given pitch would have.
     261              :     /// One of the PluginAPI::PluginAPI::Direction values
     262              :     /// \see PluginAPI::PluginAPI::Direction
     263              :     /// \param pitch The pitch to find the stem direction for.
     264            0 :     Q_INVOKABLE int stemDirection(int pitch) { return int(drumset()->stemDirection(pitch)); }
     265              : 
     266              :     /// The name (untranslated) of the drumset note for a given pitch.
     267              :     /// \param pitch The pitch of the note to find the name for.
     268            0 :     Q_INVOKABLE QString name(int pitch) { return drumset()->name(pitch); }
     269              : 
     270              :     /// The translated name of the drumset note for a given pitch.
     271              :     /// \param pitch The pitch of the note to find the name for.
     272            0 :     Q_INVOKABLE QString translatedName(int pitch) { return drumset()->translatedName(pitch); }
     273              : 
     274              :     /// The shortcut of the drumset note for a given pitch.
     275              :     /// \param pitch The pitch of the note to find the shortcut for.
     276            0 :     Q_INVOKABLE QString shortcut(int pitch) { return drumset()->shortcut(pitch); }
     277              : 
     278              :     /// List of variants for a given pitch in this drumset.
     279              :     /// \returns A list of objects representing variants.
     280              :     /// Each object has the following fields:
     281              :     /// - \p pitch - pitch of this variant.
     282              :     /// - \p tremolo - tremolo type of this variant, one of
     283              :     ///             PluginAPI::PLUGIN_API::TremoloType values.
     284              :     /// - \p articulationName - the name of the articulation
     285              :     ///             for this variant.
     286              :     /// \param pitch Pitch to find variants for.
     287              :     Q_INVOKABLE QVariantList variants(int pitch);
     288              : 
     289              :     /// The row the given pitch appears in the percussion panel.
     290              :     /// \param pitch The pitch of the note to find the row for.
     291            0 :     Q_INVOKABLE int panelRow(int pitch) { return drumset()->panelRow(pitch); }
     292              : 
     293              :     /// The column the given pitch appears in the percussion panel.
     294              :     /// \param pitch The pitch of the note to find the column for.
     295            0 :     Q_INVOKABLE int panelColumn(int pitch) { return drumset()->panelColumn(pitch); }
     296              : 
     297              :     /// Tries to find the pitch of a normal notehead at "line". If a
     298              :     /// normal notehead can't be found it will instead return the
     299              :     /// "first valid pitch" (i.e. the lowest used midi note) in the drumset.
     300              :     /// \param line The line to find the default pitch on.
     301            0 :     Q_INVOKABLE int defaultPitchForLine(int line) { return drumset()->defaultPitchForLine(line); }
     302              : 
     303              :     /// The next used pitch from a given starting point.
     304              :     /// \param pitch The pitch from which to find the next used pitch from.
     305            0 :     Q_INVOKABLE int nextPitch(int pitch) { return drumset()->nextPitch(pitch); }
     306              : 
     307              :     /// The previous used pitch from a given starting point.
     308              :     /// \param pitch The pitch from which to find the previous used pitch from.
     309            0 :     Q_INVOKABLE int prevPitch(int pitch) { return drumset()->prevPitch(pitch); }
     310              : 
     311              :     /// Sets the name of the drum note at the given pitch.
     312              :     /// These setters allow modifying a cloned drumset before passing it
     313              :     /// to Score.replaceDrumset(). The Drumset obtained from an Instrument
     314              :     /// is read-only; use Instrument.cloneDrumset() to get a writable copy.
     315              :     /// \param pitch The MIDI pitch (0 to 127).
     316              :     /// \param name The new name for this drum note.
     317              :     /// \since MuseScore 4.7
     318              :     Q_INVOKABLE void setName(int pitch, const QString& name);
     319              : 
     320              :     /// Sets the notehead group for the drum note at the given pitch.
     321              :     /// \param pitch The MIDI pitch (0 to 127).
     322              :     /// \param noteHead The notehead group (NoteHeadGroup value).
     323              :     /// \since MuseScore 4.7
     324              :     Q_INVOKABLE void setNoteHead(int pitch, int noteHead);
     325              : 
     326              :     /// Sets the staff line for the drum note at the given pitch.
     327              :     /// \param pitch The MIDI pitch (0 to 127).
     328              :     /// \param line The staff line for this note.
     329              :     /// \since MuseScore 4.7
     330              :     Q_INVOKABLE void setLine(int pitch, int line);
     331              : 
     332              :     /// Sets the voice for the drum note at the given pitch.
     333              :     /// \param pitch The MIDI pitch (0 to 127).
     334              :     /// \param voice The voice index (0 to 3).
     335              :     /// \since MuseScore 4.7
     336              :     Q_INVOKABLE void setVoice(int pitch, int voice);
     337              : 
     338              :     /// Sets the stem direction for the drum note at the given pitch.
     339              :     /// \param pitch The MIDI pitch (0 to 127).
     340              :     /// \param stemDirection The stem direction (Direction value).
     341              :     /// \since MuseScore 4.7
     342              :     Q_INVOKABLE void setStemDirection(int pitch, int stemDirection);
     343              : 
     344              :     /// Sets the keyboard shortcut for the drum note at the given pitch.
     345              :     /// \param pitch The MIDI pitch (0 to 127).
     346              :     /// \param shortcut The keyboard shortcut character.
     347              :     /// \since MuseScore 4.7
     348              :     Q_INVOKABLE void setShortcut(int pitch, const QString& shortcut);
     349              : 
     350              :     /// Checks whether two drumsets represent the same object.
     351            0 :     Q_INVOKABLE bool is(apiv1::Drumset* other) { return other && drumset() == other->drumset(); }
     352              : };
     353              : 
     354              : //---------------------------------------------------------
     355              : //   ChannelListProperty
     356              : ///   \cond PLUGIN_API \private \endcond
     357              : //---------------------------------------------------------
     358              : 
     359              : class ChannelListProperty : public QQmlListProperty<Channel>
     360              : {
     361              : public:
     362              :     ChannelListProperty(Instrument* i);
     363              : 
     364              :     static qsizetype count(QQmlListProperty<Channel>* l);
     365              :     static Channel* at(QQmlListProperty<Channel>* l, qsizetype i);
     366              : };
     367              : 
     368              : //---------------------------------------------------------
     369              : //   Instrument
     370              : ///   \since MuseScore 3.5
     371              : //---------------------------------------------------------
     372              : 
     373              : class Instrument : public QObject
     374              : {
     375              :     Q_OBJECT
     376              : 
     377              :     /// The MuseScore string identifier
     378              :     /// for this instrument.
     379              :     /// \see \ref apiv1::Part::instrumentId "Part.instrumentId"
     380              :     Q_PROPERTY(QString instrumentId READ instrumentId)
     381              :     /// The string identifier
     382              :     /// ([MusicXML Sound ID](https://www.musicxml.com/for-developers/standard-sounds/))
     383              :     /// for this instrument.
     384              :     /// \see \ref apiv1::Part::musicXmlId "Part.musicXmlId"
     385              :     Q_PROPERTY(QString musicXmlId READ musicXmlId)
     386              :     // mu::engraving::Instrument supports multiple short/long names (for aeolus instruments?)
     387              :     // but in practice only one is actually used. If this gets changed this API could
     388              :     // be expanded.
     389              :     /// The long name for this instrument.
     390              :     Q_PROPERTY(QString longName READ longName)
     391              :     /// The short name for this instrument.
     392              :     Q_PROPERTY(QString shortName READ shortName)
     393              : 
     394              :     /// For fretted instruments, an information about this
     395              :     /// instrument's strings.
     396              :     Q_PROPERTY(apiv1::StringData * stringData READ stringData)
     397              : 
     398              :     /// For unpitched percussion instruments, information about
     399              :     /// this instrument's percussion.
     400              :     /// \since MuseScore 4.6
     401              :     Q_PROPERTY(apiv1::Drumset * drumset READ drumset)
     402              : 
     403              :     /// The list of channels for this instrument.
     404              :     Q_PROPERTY(QQmlListProperty<apiv1::Channel> channels READ channels)
     405              : 
     406              :     mu::engraving::Instrument* m_instrument;
     407              :     mu::engraving::Part* m_part;
     408              : 
     409              : public:
     410              :     /// \cond MS_INTERNAL
     411            1 :     Instrument(mu::engraving::Instrument* i, mu::engraving::Part* p)
     412            1 :         : QObject(), m_instrument(i), m_part(p) {}
     413              : 
     414            1 :     mu::engraving::Instrument* instrument() { return m_instrument; }
     415            0 :     const mu::engraving::Instrument* instrument() const { return m_instrument; }
     416              : 
     417            0 :     mu::engraving::Part* part() { return m_part; }
     418              : 
     419            0 :     QString instrumentId() const { return instrument()->id(); }
     420            0 :     QString musicXmlId() const { return instrument()->musicXmlId(); }
     421              :     QString longName() const;
     422              :     QString shortName() const;
     423              : 
     424            0 :     apiv1::StringData* stringData() { return customWrap<StringData>(instrument()->stringData()); }
     425              : 
     426            0 :     apiv1::Drumset* drumset() { return customWrap<Drumset>(instrument()->drumset()); }
     427              : 
     428              :     ChannelListProperty channels();
     429              :     /// \endcond
     430              : 
     431              :     /// Creates and returns a copy of this instrument's drumset.
     432              :     /// The returned Drumset is owned by the caller and can be modified
     433              :     /// without affecting the original instrument until replaceDrumset is called.
     434              :     /// Returns null if the instrument has no drumset.
     435              :     /// \since MuseScore 4.7
     436              :     Q_INVOKABLE apiv1::Drumset* cloneDrumset();
     437              : 
     438              :     /// Checks whether two instruments represent the same object.
     439            0 :     Q_INVOKABLE bool is(apiv1::Instrument* other) { return other && instrument() == other->instrument(); }
     440              : };
     441              : }
        

Generated by: LCOV version 2.0-1