/* * SPDX-License-Identifier: GPL-3.0-only * MuseScore-Studio-CLA-applies * * MuseScore Studio * Music Composition & Notation * * Copyright (C) 2021 MuseScore Limited and others * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #pragma once #include #include "global/containers.h" #include "clef.h" #include "interval.h" #include "notifier.h" #include "stafflabel.h" #include "stringdata.h" #include "../compat/midi/midicoreevent.h" namespace mu::engraving { class ChannelListener; class Drumset; class InstrumentTemplate; class MasterScore; class Part; class StringData; class Synthesizer; //--------------------------------------------------------- // NamedEventList //--------------------------------------------------------- struct NamedEventList { String name; String descr; std::vector events; bool operator==(const NamedEventList& i) const { return i.name == name && i.events == events; } }; //--------------------------------------------------------- // MidiArticulation //--------------------------------------------------------- struct MidiArticulation { String name; String descr; int velocity = 0; // velocity change: -100% - +100% int gateTime = 0; // gate time change: -100% - +100% MidiArticulation() {} MidiArticulation(const String& n, const String& d, int v, int g) : name(n), descr(d), velocity(v), gateTime(g) {} bool operator==(const MidiArticulation& i) const; }; //--------------------------------------------------------- // Channel //--------------------------------------------------------- class InstrChannel { public: static const char* DEFAULT_NAME; static const char* HARMONY_NAME; static const char* PALM_MUTE_NAME; static const int DEFAULT_COLOR = 0x3399ff; static constexpr char defaultVolume = 100; enum class A : char { HBANK, LBANK, PROGRAM, VOLUME, PAN, CHORUS, REVERB, INIT_COUNT }; enum class Prop : char { VOLUME, PAN, CHORUS, REVERB, NAME, PROGRAM, BANK, COLOR, SYNTI, CHANNEL, USER_BANK_CONTROL }; std::vector& initList() const; String name() const { return m_name; } void setName(const String& value); String synti() const { return m_synti; } void setSynti(const String& value); int color() const { return m_color; } void setColor(int value); char volume() const { return m_volume; } void setVolume(char value); char pan() const { return m_pan; } void setPan(char value); char chorus() const { return m_chorus; } void setChorus(char value); char reverb() const { return m_reverb; } void setReverb(char value); void addToInit(const MidiCoreEvent& e) { m_init.push_back(e); } void setMustUpdateInit(bool arg) { m_mustUpdateInit = arg; } int program() const { return m_program; } void setProgram(int value); int bank() const { return m_bank; } void setBank(int value); int channel() const { return m_channel; } void setChannel(int value); // If the bank controller is set by the user or not bool userBankController() const { return m_userBankController; } void setUserBankController(bool val); bool isHarmonyChannel() const { return m_name == String::fromUtf8(InstrChannel::HARMONY_NAME); } std::vector midiActions; std::vector articulation; InstrChannel(); void updateInitList() const; bool operator==(const InstrChannel& c) const { return (m_name == c.m_name) && (m_channel == c.m_channel); } bool operator!=(const InstrChannel& c) const { return !(*this == c); } void addListener(ChannelListener* l); void removeListener(ChannelListener* l); void switchExpressive(Synthesizer* synth, bool expressive, bool force = false); void setNotifyAboutChangedEnabled(bool arg) { m_notifyAboutChangedEnabled = arg; } private: void firePropertyChanged(InstrChannel::Prop prop) { if (m_notifyAboutChangedEnabled) { m_notifier.notify(prop); } } Notifier m_notifier; bool m_notifyAboutChangedEnabled = true; // this are the indexes of controllers which are always present in // Channel init EventList (maybe zero) String m_name; int m_color = 0; //rgb String m_synti; char m_volume = 0; char m_pan = 0; char m_chorus = 0; char m_reverb = 0; int m_program = 0; // current values as shown in mixer int m_bank = 0; // initialized from "init" int m_channel = 0; // mscore channel number, mapped to midi port/channel // MuseScore General-specific SND flags: //! TODO Needs porting to MU4 bool m_userBankController = false; // if the user has changed the bank controller as opposed to switchExpressive //bool _switchedToExpressive = false; // if the patch has been automatically switched to an expr variant mutable std::vector m_init; mutable bool m_mustUpdateInit = true; }; //--------------------------------------------------------- // ChannelListener //--------------------------------------------------------- class ChannelListener : public Listener { OBJECT_ALLOCATOR(engraving, ChannelListener) public: virtual void propertyChanged(InstrChannel::Prop property) = 0; void setNotifier(InstrChannel* ch) { Listener::setNotifier(nullptr); if (ch) { ch->addListener(this); } } private: void receive(InstrChannel::Prop prop) override { propertyChanged(prop); } }; //--------------------------------------------------------- // PartChannelSettingsLink //--------------------------------------------------------- class PartChannelSettingsLink final : private ChannelListener { public: PartChannelSettingsLink() = default; PartChannelSettingsLink(InstrChannel* main, InstrChannel* bound, bool excerpt); PartChannelSettingsLink(const PartChannelSettingsLink&) = delete; PartChannelSettingsLink(PartChannelSettingsLink&&); PartChannelSettingsLink& operator=(const PartChannelSettingsLink&) = delete; PartChannelSettingsLink& operator=(PartChannelSettingsLink&&); ~PartChannelSettingsLink() {} friend void swap(PartChannelSettingsLink&, PartChannelSettingsLink&); private: static void applyProperty(InstrChannel::Prop p, const InstrChannel* from, InstrChannel* to); void propertyChanged(InstrChannel::Prop p) override; InstrChannel* m_main = nullptr; InstrChannel* m_bound = nullptr; bool m_excerpt = false; }; //--------------------------------------------------------- // Trait //--------------------------------------------------------- enum class TraitType : unsigned char { Unknown, Tuning, Transposition, Course }; struct Trait { String name; TraitType type = TraitType::Unknown; bool isDefault = false; bool isHiddenOnScore = false; bool isValid() const { return !name.isEmpty(); } }; //--------------------------------------------------------- // Instrument //--------------------------------------------------------- class Instrument { public: Instrument(String id = String()); Instrument(const Instrument&); ~Instrument(); NamedEventList* midiAction(const String& s, int channel) const; int channelIdx(const String& s) const; double getVelocityMultiplier(const String& name) const; void updateGateTime(int* gateTime, const String& name) const; String recognizeMusicXmlId() const; String recognizeId() const; int recognizeMidiProgram() const; void operator=(const Instrument&); bool operator==(const Instrument&) const; bool operator!=(const Instrument&) const; bool isDifferentInstrument(const Instrument& i) const; const String& id() const { return m_id; } const String& soundId() const { return m_soundId; } void setSoundId(const String& id) { m_soundId = id; } String family() const; String group() const; void setId(const String& id) { m_id = id; } void setMinPitchP(int v) { m_minPitchP = v; } void setMaxPitchP(int v) { m_maxPitchP = v; } void setMinPitchA(int v) { m_minPitchA = v; } void setMaxPitchA(int v) { m_maxPitchA = v; } Interval transpose() const { return m_transpose; } void setTranspose(const Interval& v) { m_transpose = v; } void setMusicXmlId(const String& musicXmlId) { m_musicXmlId = musicXmlId; } void setDrumset(const Drumset* ds); const Drumset* drumset() const { return m_drumset; } Drumset* drumset() { return m_drumset; } bool useDrumset() const { return m_useDrumset; } void setUseDrumset(bool val); void setAmateurPitchRange(int a, int b) { m_minPitchA = a; m_maxPitchA = b; } void setProfessionalPitchRange(int a, int b) { m_minPitchP = a; m_maxPitchP = b; } InstrChannel* channel(int idx) { return muse::value(m_channel, idx); } const InstrChannel* channel(int idx) const { return muse::value(m_channel, idx); } InstrChannel* playbackChannel(int idx, MasterScore*); const InstrChannel* playbackChannel(int idx, const MasterScore*) const; size_t cleffTypeCount() const; ClefTypeList clefType(size_t staffIdx) const; void setClefType(size_t staffIdx, const ClefTypeList& c); const std::vector& midiActions() const { return m_midiActions; } void addMidiAction(const NamedEventList& l) { m_midiActions.push_back(l); } const std::vector& articulation() const { return m_articulation; } void addMidiArticulation(const MidiArticulation& a) { m_articulation.push_back(a); } const std::vector& channel() const { return m_channel; } void appendChannel(InstrChannel* c) { m_channel.push_back(c); } void removeChannel(InstrChannel* c) { muse::remove(m_channel, c); } void clearChannels() { m_channel.clear(); } void setMidiActions(const std::vector& l) { m_midiActions = l; } void setArticulation(const std::vector& l) { m_articulation = l; } const StringData* stringData() const { return &m_stringData; } void setStringData(const StringData& d) { m_stringData.set(d); } bool hasStrings() const { return m_stringData.strings() > 0; } void setLongName(const String& f) { m_instrumentLabel.setLongName(f); } void setShortName(const String& f) { m_instrumentLabel.setShortName(f); } void setInstrumentName(const InstrumentLabel& n) { m_instrumentLabel = n; } const String& longName() const { return m_instrumentLabel.longName(); } const String& shortName() const { return m_instrumentLabel.shortName(); } const InstrumentLabel& instrumentLabel() const { return m_instrumentLabel; } InstrumentLabel& instrumentLabel() { return m_instrumentLabel; } int number() const { return m_instrumentLabel.number(); } void setNumber(int v) { return m_instrumentLabel.setNumber(v); } const String& transposition() const { return m_instrumentLabel.transposition(); } void setTransposition(const String& s) { m_instrumentLabel.setTransposition(s); } int minPitchP() const; int maxPitchP() const; int minPitchA() const; int maxPitchA() const; String musicXmlId() const; String trackName() const; void setTrackName(const String& s); String nameAsXmlText() const; String nameAsPlainText() const; String abbreviatureAsXmlText() const; String abbreviatureAsPlainText() const; static Instrument fromTemplate(const InstrumentTemplate* t); Trait trait() const; void setTrait(const Trait& trait); bool isPrimary() const; void setIsPrimary(bool isPrimary); void updateInstrumentId(); bool singleNoteDynamics() const { return m_singleNoteDynamics; } void setSingleNoteDynamics(bool val) { m_singleNoteDynamics = val; } void setSingleNoteDynamicsFromTemplate(); bool getSingleNoteDynamicsFromTemplate() const; void switchExpressive(MasterScore* score, Synthesizer* synth, bool expressive, bool force = false); bool isVocalInstrument() const; bool isNormallyMultiStaveInstrument() const; GlissandoStyle glissandoStyle() const; void setGlissandoStyle(GlissandoStyle style); private: InstrumentLabel m_instrumentLabel; String m_trackName; String m_id; String m_soundId; int m_minPitchA = 0; int m_maxPitchA = 0; int m_minPitchP = 0; int m_maxPitchP = 0; Interval m_transpose; String m_musicXmlId; bool m_useDrumset = false; Drumset* m_drumset = nullptr; StringData m_stringData; std::vector m_midiActions; std::vector m_articulation; std::vector m_channel; // at least one entry std::vector m_clefType; bool m_singleNoteDynamics = false; Trait m_trait; bool m_isPrimary = false; mutable String m_familyCache; mutable String m_groupCache; GlissandoStyle m_glissandoStyle = GlissandoStyle::CHROMATIC; }; //--------------------------------------------------------- // InstrumentList //--------------------------------------------------------- class InstrumentList : public std::map { OBJECT_ALLOCATOR(engraving, InstrumentList) public: InstrumentList() {} const Instrument* instrument(int tick) const; Instrument* instrument(int tick); void setInstrument(Instrument*, int tick); bool contains(const String& instrumentId) const; private: static Instrument defaultInstrument; }; }