//============================================================================= // MuseScore // Music Composition & Notation // // Copyright (C) 2008-2011 Werner Schweer // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 // as published by the Free Software Foundation and appearing in // the file LICENCE.GPL //============================================================================= #include "instrument.h" #include "xml.h" #include "drumset.h" #include "articulation.h" #include "utils.h" #include "stringdata.h" #include "instrtemplate.h" #include "mscore.h" #include "part.h" #include "score.h" namespace Ms { Instrument InstrumentList::defaultInstrument; //--------------------------------------------------------- // write //--------------------------------------------------------- void NamedEventList::write(XmlWriter& xml, const QString& n) const { xml.stag(QString("%1 name=\"%2\"").arg(n).arg(name)); if (!descr.isEmpty()) xml.tag("descr", descr); for (const MidiCoreEvent& e : events) e.write(xml); xml.etag(); } //--------------------------------------------------------- // read //--------------------------------------------------------- void NamedEventList::read(XmlReader& e) { name = e.attribute("name"); while (e.readNextStartElement()) { const QStringRef& tag(e.name()); if (tag == "program") { MidiCoreEvent ev(ME_CONTROLLER, 0, CTRL_PROGRAM, e.intAttribute("value", 0)); events.push_back(ev); e.skipCurrentElement(); } else if (tag == "controller") { MidiCoreEvent ev; ev.setType(ME_CONTROLLER); ev.setDataA(e.intAttribute("ctrl", 0)); ev.setDataB(e.intAttribute("value", 0)); events.push_back(ev); e.skipCurrentElement(); } else if (tag == "descr") descr = e.readElementText(); else e.unknown(); } } //--------------------------------------------------------- // operator //--------------------------------------------------------- bool MidiArticulation::operator==(const MidiArticulation& i) const { return (i.name == name) && (i.velocity == velocity) && (i.gateTime == gateTime); } //--------------------------------------------------------- // Instrument //--------------------------------------------------------- Instrument::Instrument() { Channel* a = new Channel; a->name = "normal"; _channel.append(a); _minPitchA = 0; _maxPitchA = 127; _minPitchP = 0; _maxPitchP = 127; _useDrumset = false; _drumset = 0; } Instrument::Instrument(const Instrument& i) { _longNames = i._longNames; _shortNames = i._shortNames; _trackName = i._trackName; _minPitchA = i._minPitchA; _maxPitchA = i._maxPitchA; _minPitchP = i._minPitchP; _maxPitchP = i._maxPitchP; _transpose = i._transpose; _instrumentId = i._instrumentId; _stringData = i._stringData; _drumset = 0; setDrumset(i._drumset); _useDrumset = i._useDrumset; _stringData = i._stringData; _midiActions = i._midiActions; _articulation = i._articulation; for (Channel* c : i._channel) _channel.append(new Channel(*c)); _clefType = i._clefType; } void Instrument::operator=(const Instrument& i) { qDeleteAll(_channel); _channel.clear(); delete _drumset; _longNames = i._longNames; _shortNames = i._shortNames; _trackName = i._trackName; _minPitchA = i._minPitchA; _maxPitchA = i._maxPitchA; _minPitchP = i._minPitchP; _maxPitchP = i._maxPitchP; _transpose = i._transpose; _instrumentId = i._instrumentId; _stringData = i._stringData; _drumset = 0; setDrumset(i._drumset); _useDrumset = i._useDrumset; _stringData = i._stringData; _midiActions = i._midiActions; _articulation = i._articulation; for (Channel* c : i._channel) _channel.append(new Channel(*c)); _clefType = i._clefType; } //--------------------------------------------------------- // ~Instrument //--------------------------------------------------------- Instrument::~Instrument() { qDeleteAll(_channel); delete _drumset; } //--------------------------------------------------------- // StaffName::write //--------------------------------------------------------- void StaffName::write(XmlWriter& xml, const char* tag) const { if (!name().isEmpty()) { if (pos() == 0) xml.writeXml(QString("%1").arg(tag), name()); else xml.writeXml(QString("%1 pos=\"%2\"").arg(tag).arg(pos()), name()); } } //--------------------------------------------------------- // read //--------------------------------------------------------- void StaffName::read(XmlReader& e) { _pos = e.intAttribute("pos", 0); _name = e.readXml(); if (_name.startsWith("")) { // compatibility to old html implementation: _name = QTextDocumentFragment::fromHtml(_name).toPlainText(); } } //--------------------------------------------------------- // Instrument::write //--------------------------------------------------------- void Instrument::write(XmlWriter& xml, Part* part) const { xml.stag("Instrument"); _longNames.write(xml, "longName"); _shortNames.write(xml, "shortName"); // if (!_trackName.empty()) xml.tag("trackName", _trackName); if (_minPitchP > 0) xml.tag("minPitchP", _minPitchP); if (_maxPitchP < 127) xml.tag("maxPitchP", _maxPitchP); if (_minPitchA > 0) xml.tag("minPitchA", _minPitchA); if (_maxPitchA < 127) xml.tag("maxPitchA", _maxPitchA); if (_transpose.diatonic) xml.tag("transposeDiatonic", _transpose.diatonic); if (_transpose.chromatic) xml.tag("transposeChromatic", _transpose.chromatic); if (!_instrumentId.isEmpty()) xml.tag("instrumentId", _instrumentId); if (_useDrumset) { xml.tag("useDrumset", _useDrumset); _drumset->save(xml); } for (int i = 0; i < _clefType.size(); ++i) { ClefTypeList ct = _clefType[i]; if (ct._concertClef == ct._transposingClef) { if (ct._concertClef != ClefType::G) { QString tag = ClefInfo::tag(ct._concertClef); if (i) xml.tag(QString("clef staff=\"%1\"").arg(i+1), tag); else xml.tag("clef", tag); } } else { QString tag1 = ClefInfo::tag(ct._concertClef); QString tag2 = ClefInfo::tag(ct._transposingClef); if (i) { xml.tag(QString("concertClef staff=\"%1\"").arg(i+1), tag1); xml.tag(QString("transposingClef staff=\"%1\"").arg(i+1), tag2); } else { xml.tag("concertClef", tag1); xml.tag("transposingClef", tag2); } } } if (!(_stringData == StringData())) _stringData.write(xml); foreach(const NamedEventList& a, _midiActions) a.write(xml, "MidiAction"); foreach(const MidiArticulation& a, _articulation) a.write(xml); for (const Channel* a : _channel) a->write(xml, part); xml.etag(); } //--------------------------------------------------------- // Instrument::read //--------------------------------------------------------- void Instrument::read(XmlReader& e, Part* part) { bool customDrumset = false; _channel.clear(); // remove default channel while (e.readNextStartElement()) { if (!readProperties(e, part, &customDrumset)) e.unknown(); } if (_useDrumset) { if (_channel[0]->bank == 0) _channel[0]->bank = 128; _channel[0]->updateInitList(); } } //--------------------------------------------------------- // Instrument::readProperties //--------------------------------------------------------- bool Instrument::readProperties(XmlReader& e, Part* part, bool* customDrumset) { const QStringRef& tag(e.name()); if (tag == "longName") { StaffName name; name.read(e); _longNames.append(name); } else if (tag == "shortName") { StaffName name; name.read(e); _shortNames.append(name); } else if (tag == "trackName") _trackName = e.readElementText(); else if (tag == "minPitch") { // obsolete _minPitchP = _minPitchA = e.readInt(); } else if (tag == "maxPitch") { // obsolete _maxPitchP = _maxPitchA = e.readInt(); } else if (tag == "minPitchA") _minPitchA = e.readInt(); else if (tag == "minPitchP") _minPitchP = e.readInt(); else if (tag == "maxPitchA") _maxPitchA = e.readInt(); else if (tag == "maxPitchP") _maxPitchP = e.readInt(); else if (tag == "transposition") { // obsolete _transpose.chromatic = e.readInt(); _transpose.diatonic = chromatic2diatonic(_transpose.chromatic); } else if (tag == "transposeChromatic") _transpose.chromatic = e.readInt(); else if (tag == "transposeDiatonic") _transpose.diatonic = e.readInt(); else if (tag == "instrumentId") _instrumentId = e.readElementText(); else if (tag == "useDrumset") { _useDrumset = e.readInt(); if (_useDrumset) { delete _drumset; _drumset = new Drumset(*smDrumset); } } else if (tag == "Drum") { // if we see on of this tags, a custom drumset will // be created if (!_drumset) _drumset = new Drumset(*smDrumset); if (!customDrumset) { const_cast(_drumset)->clear(); *customDrumset = true; } const_cast(_drumset)->load(e); } // support tag "Tablature" for a while for compatibility with existent 2.0 scores else if (tag == "Tablature" || tag == "StringData") _stringData.read(e); else if (tag == "MidiAction") { NamedEventList a; a.read(e); _midiActions.append(a); } else if (tag == "Articulation") { MidiArticulation a; a.read(e); _articulation.append(a); } else if (tag == "Channel" || tag == "channel") { Channel* a = new Channel; a->read(e, part); _channel.append(a); } else if (tag == "clef") { // sets both transposing and concert clef int idx = e.intAttribute("staff", 1) - 1; QString val(e.readElementText()); ClefType ct = Clef::clefType(val); setClefType(idx, ClefTypeList(ct, ct)); } else if (tag == "concertClef") { int idx = e.intAttribute("staff", 1) - 1; QString val(e.readElementText()); setClefType(idx, ClefTypeList(Clef::clefType(val), clefType(idx)._transposingClef)); } else if (tag == "transposingClef") { int idx = e.intAttribute("staff", 1) - 1; QString val(e.readElementText()); setClefType(idx, ClefTypeList(clefType(idx)._concertClef, Clef::clefType(val))); } else return false; return true; } //--------------------------------------------------------- // action //--------------------------------------------------------- NamedEventList* Instrument::midiAction(const QString& s, int channelIdx) const { // first look in channel list foreach(const NamedEventList& a, _channel[channelIdx]->midiActions) { if (s == a.name) return const_cast(&a); } foreach(const NamedEventList& a, _midiActions) { if (s == a.name) return const_cast(&a); } return 0; } //--------------------------------------------------------- // Channel //--------------------------------------------------------- Channel::Channel() { for(int i = 0; i < int(A::INIT_COUNT); ++i) init.push_back(MidiCoreEvent()); synti = "Fluid"; // default synthesizer channel = -1; program = -1; bank = 0; volume = 100; pan = 64; // actually 63.5 for center chorus = 0; reverb = 0; mute = false; solo = false; soloMute = false; } //--------------------------------------------------------- // write //--------------------------------------------------------- void Channel::write(XmlWriter& xml, Part* part) const { if (name.isEmpty() || name == "normal") xml.stag("Channel"); else xml.stag(QString("Channel name=\"%1\"").arg(name)); if (!descr.isEmpty()) xml.tag("descr", descr); updateInitList(); foreach(const MidiCoreEvent& e, init) { if (e.type() == ME_INVALID) continue; if (e.type() == ME_CONTROLLER) { if (e.dataA() == CTRL_HBANK && e.dataB() == 0) continue; if (e.dataA() == CTRL_LBANK && e.dataB() == 0) continue; if (e.dataA() == CTRL_VOLUME && e.dataB() == 100) continue; if (e.dataA() == CTRL_PANPOT && e.dataB() == 64) continue; if (e.dataA() == CTRL_REVERB_SEND && e.dataB() == 0) continue; if (e.dataA() == CTRL_CHORUS_SEND && e.dataB() == 0) continue; } e.write(xml); } if (!MScore::testMode) // xml.tag("synti", ::synti->name(synti)); xml.tag("synti", synti); if (mute) xml.tag("mute", mute); if (solo) xml.tag("solo", solo); if (part && part->masterScore()->exportMidiMapping() && part->score() == part->masterScore()) { xml.tag("midiPort", part->masterScore()->midiMapping(channel)->port); xml.tag("midiChannel", part->masterScore()->midiMapping(channel)->channel); } for (const NamedEventList& a : midiActions) a.write(xml, "MidiAction"); for (const MidiArticulation& a : articulation) a.write(xml); xml.etag(); } //--------------------------------------------------------- // read //--------------------------------------------------------- void Channel::read(XmlReader& e, Part* part) { // synti = 0; name = e.attribute("name"); if (name == "") name = "normal"; while (e.readNextStartElement()) { const QStringRef& tag(e.name()); if (tag == "program") { program = e.intAttribute("value", -1); if (program == -1) program = e.readInt(); else e.readNext(); } else if (tag == "controller") { int value = e.intAttribute("value", 0); int ctrl = e.intAttribute("ctrl", 0); switch (ctrl) { case CTRL_HBANK: bank = (value << 7) + (bank & 0x7f); break; case CTRL_LBANK: bank = (bank & ~0x7f) + (value & 0x7f); break; case CTRL_VOLUME: volume = value; break; case CTRL_PANPOT: pan = value; break; case CTRL_CHORUS_SEND: chorus = value; break; case CTRL_REVERB_SEND: reverb = value; break; default: { Event e(ME_CONTROLLER); e.setOntime(-1); e.setChannel(0); e.setDataA(ctrl); e.setDataB(value); init.push_back(e); } break; } e.readNext(); } else if (tag == "Articulation") { MidiArticulation a; a.read(e); articulation.append(a); } else if (tag == "MidiAction") { NamedEventList a; a.read(e); midiActions.append(a); } else if (tag == "synti") synti = e.readElementText(); else if (tag == "descr") descr = e.readElementText(); else if (tag == "mute") mute = e.readInt(); else if (tag == "solo") solo = e.readInt(); else if (tag == "midiPort") { int midiPort = e.readInt(); if (part) { MidiMapping mm; mm.port = midiPort; mm.channel = -1; mm.part = part; mm.articulation = this; part->masterScore()->midiMapping()->append(mm); channel = part->masterScore()->midiMapping()->size() - 1; } } else if (tag == "midiChannel") { int midiChannel = e.readInt(); if (part) part->masterScore()->midiMapping(channel)->channel = midiChannel; } else e.unknown(); } updateInitList(); } //--------------------------------------------------------- // updateInitList //--------------------------------------------------------- void Channel::updateInitList() const { MidiCoreEvent e; if (program != -1) { e.setType(ME_CONTROLLER); e.setDataA(CTRL_PROGRAM); e.setDataB(program); init[int(A::PROGRAM)] = e; } e.setData(ME_CONTROLLER, CTRL_HBANK, (bank >> 7) & 0x7f); init[int(A::HBANK)] = e; e.setData(ME_CONTROLLER, CTRL_LBANK, bank & 0x7f); init[int(A::LBANK)] = e; e.setData(ME_CONTROLLER, CTRL_VOLUME, volume); init[int(A::VOLUME)] = e; e.setData(ME_CONTROLLER, CTRL_PANPOT, pan); init[int(A::PAN)] = e; e.setData(ME_CONTROLLER, CTRL_CHORUS_SEND, chorus); init[int(A::CHORUS)] = e; e.setData(ME_CONTROLLER, CTRL_REVERB_SEND, reverb); init[int(A::REVERB)] = e; } //--------------------------------------------------------- // channelIdx //--------------------------------------------------------- int Instrument::channelIdx(const QString& s) const { int idx = 0; for (const Channel* a : _channel) { if (a->name.isEmpty() && s == "normal") return idx; if (s == a->name) return idx; ++idx; } return -1; } //--------------------------------------------------------- // write //--------------------------------------------------------- void MidiArticulation::write(XmlWriter& xml) const { if (name.isEmpty()) xml.stag("Articulation"); else xml.stag(QString("Articulation name=\"%1\"").arg(name)); if (!descr.isEmpty()) xml.tag("descr", descr); xml.tag("velocity", velocity); xml.tag("gateTime", gateTime); xml.etag(); } //--------------------------------------------------------- // read //--------------------------------------------------------- void MidiArticulation::read(XmlReader& e) { name = e.attribute("name"); while (e.readNextStartElement()) { const QStringRef& tag(e.name()); if (tag == "velocity") { QString text(e.readElementText()); if (text.endsWith("%")) text = text.left(text.size()-1); velocity = text.toInt(); } else if (tag == "gateTime") { QString text(e.readElementText()); if (text.endsWith("%")) text = text.left(text.size()-1); gateTime = text.toInt(); } else if (tag == "descr") descr = e.readElementText(); else e.unknown(); } } //--------------------------------------------------------- // updateVelocity //--------------------------------------------------------- void Instrument::updateVelocity(int* velocity, int /*channelIdx*/, const QString& name) { for (const MidiArticulation& a : _articulation) { if (a.name == name) { *velocity = *velocity * a.velocity / 100; break; } } } //--------------------------------------------------------- // updateGateTime //--------------------------------------------------------- void Instrument::updateGateTime(int* gateTime, int /*channelIdx*/, const QString& name) { for (const MidiArticulation& a : _articulation) { if (a.name == name) { *gateTime = a.gateTime; break; } } } //--------------------------------------------------------- // operator== //--------------------------------------------------------- bool Instrument::operator==(const Instrument& i) const { int n = _longNames.size(); if (i._longNames.size() != n) return false; for (int k = 0; k < n; ++k) { if (!(i._longNames[k] == _longNames[k])) return false; } n = _shortNames.size(); if (i._shortNames.size() != n) return false; for (int k = 0; k < n; ++k) { if (!(i._shortNames[k] == _shortNames[k].name())) return false; } return i._minPitchA == _minPitchA && i._maxPitchA == _maxPitchA && i._minPitchP == _minPitchP && i._maxPitchP == _maxPitchP && i._useDrumset == _useDrumset && i._midiActions == _midiActions && i._channel == _channel && i._articulation == _articulation && i._transpose.diatonic == _transpose.diatonic && i._transpose.chromatic == _transpose.chromatic && i._trackName == _trackName && *i.stringData() == *stringData(); ; } //--------------------------------------------------------- // operator== //--------------------------------------------------------- bool StaffName::operator==(const StaffName& i) const { return (i._pos == _pos) && (i._name == _name); } //--------------------------------------------------------- // setUseDrumset //--------------------------------------------------------- void Instrument::setUseDrumset(bool val) { _useDrumset = val; if (val && !_drumset) _drumset = new Drumset(*smDrumset); } //--------------------------------------------------------- // setDrumset //--------------------------------------------------------- void Instrument::setDrumset(const Drumset* ds) { delete _drumset; if (ds) { _useDrumset = true; _drumset = new Drumset(*ds); } else { _useDrumset = false; _drumset = 0; } } //--------------------------------------------------------- // setLongName // f is in richtext format //--------------------------------------------------------- void Instrument::setLongName(const QString& f) { _longNames.clear(); if (f.length() > 0) _longNames.append(StaffName(f, 0)); } //--------------------------------------------------------- // setShortName // f is in richtext format //--------------------------------------------------------- void Instrument::setShortName(const QString& f) { _shortNames.clear(); if (f.length() > 0) _shortNames.append(StaffName(f, 0)); } //--------------------------------------------------------- // addLongName //--------------------------------------------------------- void Instrument::addLongName(const StaffName& f) { _longNames.append(f); } //--------------------------------------------------------- // addShortName //--------------------------------------------------------- void Instrument::addShortName(const StaffName& f) { _shortNames.append(f); } //--------------------------------------------------------- // clefType //--------------------------------------------------------- ClefTypeList Instrument::clefType(int staffIdx) const { if (staffIdx >= _clefType.size()) { if (_clefType.empty()) return ClefTypeList(staffIdx == 1 ? ClefType::F : ClefType::G); return _clefType[0]; } return _clefType[staffIdx]; } //--------------------------------------------------------- // setClefType //--------------------------------------------------------- void Instrument::setClefType(int staffIdx, const ClefTypeList& c) { while (_clefType.size() <= staffIdx) _clefType.append(ClefTypeList()); _clefType[staffIdx] = c; } //--------------------------------------------------------- // minPitchP //--------------------------------------------------------- int Instrument::minPitchP() const { return _minPitchP; } //--------------------------------------------------------- // maxPitchP //--------------------------------------------------------- int Instrument::maxPitchP() const { return _maxPitchP; } //--------------------------------------------------------- // minPitchA //--------------------------------------------------------- int Instrument::minPitchA() const { return _minPitchA; } //--------------------------------------------------------- // maxPitchA //--------------------------------------------------------- int Instrument::maxPitchA() const { return _maxPitchA; } //--------------------------------------------------------- // instrumentId //--------------------------------------------------------- QString Instrument::instrumentId() const { return _instrumentId; } //--------------------------------------------------------- // instrument //--------------------------------------------------------- const Instrument* InstrumentList::instrument(int tick) const { if (empty()) return &defaultInstrument; auto i = upper_bound(tick); if (i == begin()) return &defaultInstrument; --i; return i->second; } //--------------------------------------------------------- // instrument //--------------------------------------------------------- Instrument* InstrumentList::instrument(int tick) { if (empty()) return &defaultInstrument; auto i = upper_bound(tick); if (i == begin()) return &defaultInstrument; --i; return i->second; } //--------------------------------------------------------- // setInstrument //--------------------------------------------------------- void InstrumentList::setInstrument(Instrument* instr, int tick) { if (!insert({tick, instr}).second) (*this)[tick] = instr; } //--------------------------------------------------------- // longName //--------------------------------------------------------- const QList& Instrument::longNames() const { return _longNames; } //--------------------------------------------------------- // shortName //--------------------------------------------------------- const QList& Instrument::shortNames() const { return _shortNames; } //--------------------------------------------------------- // longName //--------------------------------------------------------- QList& Instrument::longNames() { return _longNames; } //--------------------------------------------------------- // shortName //--------------------------------------------------------- QList& Instrument::shortNames() { return _shortNames; } //--------------------------------------------------------- // trackName //--------------------------------------------------------- QString Instrument::trackName() const { return _trackName; } void Instrument::setTrackName(const QString& s) { _trackName = s; } //--------------------------------------------------------- // fromTemplate //--------------------------------------------------------- Instrument Instrument::fromTemplate(const InstrumentTemplate* t) { Instrument instr; instr.setAmateurPitchRange(t->minPitchA, t->maxPitchA); instr.setProfessionalPitchRange(t->minPitchP, t->maxPitchP); for (StaffName sn : t->longNames) instr.addLongName(StaffName(sn.name(), sn.pos())); for (StaffName sn : t->shortNames) instr.addShortName(StaffName(sn.name(), sn.pos())); instr.setTrackName(t->trackName); instr.setTranspose(t->transpose); instr.setInstrumentId(t->musicXMLid); if (t->useDrumset) instr.setDrumset(t->drumset ? t->drumset : smDrumset); for (int i = 0; i < t->nstaves(); ++i) instr.setClefType(i, t->clefTypes[i]); instr.setMidiActions(t->midiActions); instr.setArticulation(t->articulation); instr._channel.clear(); for (const Channel& c : t->channel) instr._channel.append(new Channel(c)); instr.setStringData(t->stringData); return instr; } //--------------------------------------------------------- // StaffNameList::write //--------------------------------------------------------- void StaffNameList::write(XmlWriter& xml, const char* name) const { for (const StaffName& sn : *this) sn.write(xml, name); } }