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 : }
|