    //=============================================================================
    //  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("<html>")) {
                // 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*>(_drumset)->clear();
                      *customDrumset = true;
                      }
                const_cast<Drumset*>(_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<NamedEventList*>(&a);
                }

          foreach(const NamedEventList& a, _midiActions) {
                if (s == a.name)
                      return const_cast<NamedEventList*>(&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<StaffName>& Instrument::longNames() const
          {
          return _longNames;
          }

    //---------------------------------------------------------
    //   shortName
    //---------------------------------------------------------

    const QList<StaffName>& Instrument::shortNames() const
          {
          return _shortNames;
          }

    //---------------------------------------------------------
    //   longName
    //---------------------------------------------------------

    QList<StaffName>& Instrument::longNames()
          {
          return _longNames;
          }

    //---------------------------------------------------------
    //   shortName
    //---------------------------------------------------------

    QList<StaffName>& 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);
          }
    }

↑ V794 The copy operator should be protected from the case of 'this == &i'.

↑ V522 Dereferencing of the null pointer 'customDrumset' might take place.