1 //=========================================================
2 //  MusE
3 //  Linux Music Editor
4 //  $Id: songfile.cpp,v 1.25.2.12 2009/11/04 15:06:07 spamatica Exp $
5 //
6 //  (C) Copyright 1999/2000 Werner Schweer (ws@seh.de)
7 //
8 //  This program is free software; you can redistribute it and/or
9 //  modify it under the terms of the GNU General Public License
10 //  as published by the Free Software Foundation; version 2 of
11 //  the License, or (at your option) any later version.
12 //
13 //  This program is distributed in the hope that it will be useful,
14 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
15 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 //  GNU General Public License for more details.
17 //
18 //  You should have received a copy of the GNU General Public License
19 //  along with this program; if not, write to the Free Software
20 //  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
21 //
22 //=========================================================
23 
24 #include <map>
25 
26 #include <QUuid>
27 #include <QProgressDialog>
28 #include <QMessageBox>
29 #include <QCheckBox>
30 #include <QString>
31 
32 #include "app.h"
33 #include "song.h"
34 #include "arranger.h"
35 #include "arrangerview.h"
36 #include "cobject.h"
37 #include "drumedit.h"
38 #include "pianoroll.h"
39 #include "scoreedit.h"
40 #include "globals.h"
41 #include "xml.h"
42 #include "drummap.h"
43 #include "drum_ordering.h"
44 #include "event.h"
45 #include "marker/marker.h"
46 #include "midiport.h"
47 #include "audio.h"
48 #include "mitplugin.h"
49 #include "wave.h"
50 #include "midictrl.h"
51 #include "audiodev.h"
52 #include "conf.h"
53 #include "keyevent.h"
54 #include "gconfig.h"
55 #include "config.h"
56 
57 namespace MusEGlobal {
58 MusECore::CloneList cloneList;
59 }
60 
61 namespace MusECore {
62 
63 
64 //---------------------------------------------------------
65 //   NKey::write
66 //---------------------------------------------------------
67 
write(int level,Xml & xml) const68 void NKey::write(int level, Xml& xml) const
69       {
70       xml.intTag(level, "key", val);
71       }
72 
73 //---------------------------------------------------------
74 //   NKey::read
75 //---------------------------------------------------------
76 
read(Xml & xml)77 void NKey::read(Xml& xml)
78       {
79       for (;;) {
80             Xml::Token token = xml.parse();
81             switch (token) {
82                   case Xml::Error:
83                   case Xml::End:
84                         return;
85                   case Xml::Text:
86                         val = xml.s1().toInt();
87                         break;
88                   case Xml::TagEnd:
89                         if (xml.s1() == "key")
90                               return;
91                   default:
92                         break;
93                   }
94             }
95       }
96 
97 
98 //---------------------------------------------------------
99 //   Scale::write
100 //---------------------------------------------------------
101 
write(int level,Xml & xml) const102 void Scale::write(int level, Xml& xml) const
103       {
104       xml.intTag(level, "scale", val);
105       }
106 
107 //---------------------------------------------------------
108 //   Scale::read
109 //---------------------------------------------------------
110 
read(Xml & xml)111 void Scale::read(Xml& xml)
112       {
113       for (;;) {
114             Xml::Token token = xml.parse();
115             switch (token) {
116                   case Xml::Error:
117                   case Xml::End:
118                         return;
119                   case Xml::Text:
120                         val = xml.s1().toInt();
121                         break;
122                   case Xml::TagEnd:
123                         if (xml.s1() == "scale")
124                               return;
125                   default:
126                         break;
127                   }
128             }
129       }
130 
131 //---------------------------------------------------------
132 //   Part::readFromXml
133 //---------------------------------------------------------
134 
readFromXml(Xml & xml,Track * track,bool doClone,bool toTrack)135 Part* Part::readFromXml(Xml& xml, Track* track, bool doClone, bool toTrack)
136       {
137       int id = -1;
138       Part* npart = nullptr;
139       QUuid uuid;
140       bool uuidvalid = false;
141       bool clone = true;
142       bool wave = false;
143       bool isclone = false;
144 
145       for (;;) {
146             Xml::Token token = xml.parse();
147             const QString& tag = xml.s1();
148             switch (token) {
149                   case Xml::Error:
150                   case Xml::End:
151                         return npart;
152                   case Xml::TagStart:
153                         if(!npart) // If the part has not been created yet...
154                         {
155                           if(id != -1) // If an id was found...
156                           {
157                             for(iClone i = MusEGlobal::cloneList.begin(); i != MusEGlobal::cloneList.end(); ++i)
158                             {
159                               if(i->is_deleted) // Is the clone item marked as deleted? Ignore it.
160                                 continue;
161                               if(i->id == id) // Is a matching part found in the clone list?
162                               {
163                                 // Create a clone. It must still be added later in a operationgroup
164                                 npart = track->newPart((Part*)i->cp, true);
165                                 break;
166                               }
167                             }
168                           }
169                           else if(uuidvalid) // If a uuid was found...
170                           {
171                             for(iClone i = MusEGlobal::cloneList.begin(); i != MusEGlobal::cloneList.end(); ++i)
172                             {
173                               if(uuid == i->_uuid) // Is a matching part found in the clone list?
174                               {
175                                 Track* cpt = i->cp->track();
176                                 if(toTrack) // If we want to paste to the given track...
177                                 {
178                                   // If the given track type is not the same as the part's
179                                   //  original track type, we can't continue. Just return.
180                                   if(!track || cpt->type() != track->type())
181                                   {
182                                     xml.skip("part");
183                                     return nullptr;
184                                   }
185                                 }
186                                 else // ...else we want to paste to the part's original track.
187                                 {
188                                   // Make sure the track exists (has not been deleted).
189                                   if((cpt->isMidiTrack() && MusEGlobal::song->midis()->find(cpt) != MusEGlobal::song->midis()->end()) ||
190                                       (cpt->type() == Track::WAVE && MusEGlobal::song->waves()->find(cpt) != MusEGlobal::song->waves()->end()))
191                                     track = cpt;
192                                   else // Track was not found. Try pasting to the given track, as above...
193                                   {
194                                     if(!track || cpt->type() != track->type())
195                                     {
196                                       // No luck. Just return.
197                                       xml.skip("part");
198                                       return nullptr;
199                                     }
200                                   }
201                                 }
202 
203                                 if(i->is_deleted) // Is the clone item marked as deleted? Don't create a clone, create a copy.
204                                   break;
205 
206                                 // If it's a regular paste (not paste clone), and the original part is
207                                 //  not a clone, defer so that a new copy is created in TagStart above.
208                                 if(!doClone && !isclone)
209                                   break;
210 
211                                 // Create a clone. It must still be added later in a operationgroup
212                                 npart = track->newPart((Part*)i->cp, true);
213                                 break;
214                               }
215                             }
216                           }
217 
218                           if(!npart) // If the part still has not been created yet...
219                           {
220 
221                             if(!track) // A clone was not created from any matching
222                             {          // part. Create a non-clone part now.
223                               xml.skip("part");
224                               return nullptr;
225                             }
226                             // If we're pasting to selected track and the 'wave'
227                             //  variable is valid, check for mismatch...
228                             if(toTrack && uuidvalid)
229                             {
230                               // If both the part and track are not midi or wave...
231                               if((wave && track->isMidiTrack()) ||
232                                 (!wave && track->type() == Track::WAVE))
233                               {
234                                 xml.skip("part");
235                                 return nullptr;
236                               }
237                             }
238 
239                             if (track->isMidiTrack())
240                               npart = new MidiPart((MidiTrack*)track);
241                             else if (track->type() == Track::WAVE)
242                               npart = new MusECore::WavePart((MusECore::WaveTrack*)track);
243                             else
244                             {
245                               xml.skip("part");
246                               return nullptr;
247                             }
248 
249                             // Signify a new non-clone part was created.
250                             // Even if the original part was itself a clone, clear this because the
251                             //  attribute section did not create a clone from any matching part.
252                             clone = false;
253 
254                             // If an id or uuid was found, add the part to the clone list
255                             //  so that subsequent parts can look it up and clone from it...
256                             if(id != -1)
257                             {
258                               ClonePart ncp(npart, id);
259                               MusEGlobal::cloneList.push_back(ncp);
260                             }
261                             else
262                             if(uuidvalid)
263                             {
264                               ClonePart ncp(npart);
265                               // New ClonePart creates its own uuid, but we need to replace it.
266                               ncp._uuid = uuid; // OK for non-windows?
267                               MusEGlobal::cloneList.push_back(ncp);
268                             }
269                           }
270                         }
271 
272                         if (tag == "name")
273                               npart->setName(xml.parse1());
274                         else if (tag == "viewState") {
275                               npart->viewState().read(xml);
276                               }
277                         else if (tag == "poslen") {
278                               ((PosLen*)npart)->read(xml, "poslen");
279                               }
280                         else if (tag == "pos") {
281                               Pos pos;
282                               pos.read(xml, "pos");  // obsolete
283                               npart->setTick(pos.tick());
284                               }
285                         else if (tag == "len") {
286                               Pos len;
287                               len.read(xml, "len");  // obsolete
288                               npart->setLenTick(len.tick());
289                               }
290                         else if (tag == "selected")
291                               npart->setSelected(xml.parseInt());
292                         else if (tag == "color")
293                                npart->setColorIndex(xml.parseInt());
294                         else if (tag == "mute")
295                               npart->setMute(xml.parseInt());
296                         else if (tag == "event")
297                         {
298                               // If a new non-clone part was created, accept the events...
299                               if(!clone)
300                               {
301                                 EventType type = Wave;
302                                 if(track->isMidiTrack())
303                                   type = Note;
304                                 Event e(type);
305                                 e.read(xml);
306                                 // stored pos for event has absolute value. However internally
307                                 // pos is relative to start of part, we substract part pos.
308                                 // In case the event and part pos types differ, the event dominates.
309                                 e.setPosValue(e.posValue() - npart->posValue(e.pos().type()));
310 #ifdef ALLOW_LEFT_HIDDEN_EVENTS
311                                 npart->addEvent(e);
312 #else
313                                 const int posval = e.posValue();
314                                 if(posval < 0)
315                                 {
316                                   printf("readClone: warning: event at posval:%d not in part:%s, discarded\n",
317                                     posval, npart->name().toLatin1().constData());
318                                 }
319 #endif
320                               }
321                               else // ...Otherwise a clone was created, so we don't need the events.
322                                 xml.skip(tag);
323                         }
324                         else
325                               xml.unknown("readXmlPart");
326                         break;
327                   case Xml::Attribut:
328                         if (tag == "type")
329                         {
330                           if(xml.s2() == "wave")
331                             wave = true;
332                         }
333                         else if (tag == "cloneId")
334                         {
335                           id = xml.s2().toInt();
336                         }
337                         else if (tag == "uuid")
338                         {
339                           uuid = QUuid(xml.s2());
340                           if(!uuid.isNull())
341                           {
342                             uuidvalid = true;
343                           }
344                         }
345                         else if(tag == "isclone")
346                           isclone = xml.s2().toInt();
347                         break;
348                   case Xml::TagEnd:
349                         if (tag == "part")
350                           return npart;
351                   default:
352                         break;
353                   }
354             }
355   return npart;
356 }
357 
358 //---------------------------------------------------------
359 //   Part::write
360 //   If isCopy is true, write the xml differently so that
361 //    we can have 'Paste Clone' feature.
362 //---------------------------------------------------------
363 
write(int level,Xml & xml,bool isCopy,bool forceWavePaths) const364 void Part::write(int level, Xml& xml, bool isCopy, bool forceWavePaths) const
365       {
366       int id              = -1;
367       QUuid uuid;
368       bool dumpEvents     = true;
369       bool wave = _track->type() == Track::WAVE;
370 
371       // NOTE ::write() should never be called on a deleted part, so no checking here for cloneList items marked as deleted.
372       //      Checking is awkward anyway and doesn't fit well here.
373 
374       if(isCopy)
375       {
376         for(iClone i = MusEGlobal::cloneList.begin(); i != MusEGlobal::cloneList.end(); ++i)
377         {
378           if(i->cp->isCloneOf(this))
379           {
380             uuid = i->_uuid;
381             dumpEvents = false;
382             break;
383           }
384         }
385         if(uuid.isNull())
386         {
387           ClonePart cp(this);
388           uuid = cp._uuid;
389           MusEGlobal::cloneList.push_back(cp);
390         }
391       }
392       else
393       {
394         if (this->hasClones())
395         {
396           for (iClone i = MusEGlobal::cloneList.begin(); i != MusEGlobal::cloneList.end(); ++i)
397           {
398             if (i->cp->isCloneOf(this))
399             {
400               id = i->id;
401               dumpEvents = false;
402               break;
403             }
404           }
405           if (id == -1)
406           {
407             id = MusEGlobal::cloneList.size();
408             ClonePart cp(this, id);
409             MusEGlobal::cloneList.push_back(cp);
410           }
411         }
412       }
413 
414       // Special markers if this is a copy operation and the
415       //  part is a clone.
416       if(isCopy)
417       {
418         if(wave)
419           xml.nput(level, "<part type=\"wave\" uuid=\"%s\"", uuid.toByteArray().constData());
420         else
421           xml.nput(level, "<part uuid=\"%s\"", uuid.toByteArray().constData());
422 
423         if(hasClones())
424           xml.nput(" isclone=\"1\"");
425         xml.put(">");
426         level++;
427       }
428       else
429       if (id != -1)
430       {
431         xml.tag(level++, "part cloneId=\"%d\"", id);
432       }
433       else
434         xml.tag(level++, "part");
435 
436       xml.strTag(level, "name", _name);
437 
438       // This won't bother writing if the state is invalid.
439       viewState().write(level, xml);
440 
441       PosLen::write(level, xml, "poslen");
442       xml.intTag(level, "selected", _selected);
443       xml.intTag(level, "color", _colorIndex);
444       if (_mute)
445             xml.intTag(level, "mute", _mute);
446       if (dumpEvents) {
447             for (ciEvent e = events().begin(); e != events().end(); ++e)
448                   e->second.write(level, xml, *this, forceWavePaths);
449             }
450       xml.etag(level, "part");
451       }
452 
453 
454 //---------------------------------------------------------
455 //   writeFont
456 //---------------------------------------------------------
457 
458 //void Song::writeFont(int level, Xml& xml, const char* name,
459 //   const QFont& font) const
460 //      {
461 //      xml.nput(level, "<%s family=\"%s\" size=\"%d\"",
462 //         name, Xml::xmlString(font.family()).toLatin1().constData(), font.pointSize());
463 //      if (font.weight() != QFont::Normal)
464 //            xml.nput(" weight=\"%d\"", font.weight());
465 //      if (font.italic())
466 //            xml.nput(" italic=\"1\"");
467 //      xml.nput(" />\n");
468 //      }
469 
470 //---------------------------------------------------------
471 //   readFont
472 //---------------------------------------------------------
473 
474 //QFont Song::readFont(Xml& xml, const char* name)
475 //      {
476 //      QFont f;
477 //      for (;;) {
478 //            Xml::Token token = xml.parse();
479 //            switch (token) {
480 //                  case Xml::Error:
481 //                  case Xml::End:
482 //                        return f;
483 //                  case Xml::TagStart:
484 //                        xml.unknown("readFont");
485 //                        break;
486 //                  case Xml::Attribut:
487 //                        if (xml.s1() == "family")
488 //                              f.setFamily(xml.s2());
489 //                        else if (xml.s1() == "size")
490 //                              f.setPointSize(xml.s2().toInt());
491 //                        else if (xml.s1() == "weight")
492 //                              f.setWeight(xml.s2().toInt());
493 //                        else if (xml.s1() == "italic")
494 //                              f.setItalic(xml.s2().toInt());
495 //                        break;
496 //                  case Xml::TagEnd:
497 //                        if (xml.s1() == name)
498 //                              return f;
499 //                  default:
500 //                        break;
501 //                  }
502 //            }
503 //      return f;
504 //      }
505 
506 //---------------------------------------------------------
507 //   readMarker
508 //---------------------------------------------------------
509 
readMarker(Xml & xml)510 void Song::readMarker(Xml& xml)
511       {
512       Marker m;
513       m.read(xml);
514       _markerList->add(m);
515       }
516 
517 //---------------------------------------------------------
518 //   checkSongSampleRate
519 //   Called by gui thread.
520 //---------------------------------------------------------
521 
checkSongSampleRate()522 void Song::checkSongSampleRate()
523 {
524   std::map<int, int> waveRates;
525 
526   for(ciWaveTrack iwt = waves()->begin(); iwt != waves()->end(); ++iwt)
527   {
528     WaveTrack* wt = *iwt;
529     for(ciPart ipt = wt->parts()->begin(); ipt != wt->parts()->end(); ++ipt)
530     {
531       Part* pt = ipt->second;
532       for(ciEvent ie = pt->events().begin(); ie != pt->events().end(); ++ie)
533       {
534         const Event e(ie->second);
535         if(e.sndFile().isOpen())
536         {
537           const int sr = e.sndFile().samplerate();
538           std::map<int, int>::iterator iwr = waveRates.find(sr);
539           if(iwr == waveRates.end())
540           {
541             waveRates.insert(std::pair<int, int>(sr, 1));
542           }
543           else
544           {
545             ++iwr->second;
546           }
547         }
548       }
549     }
550   }
551 
552   for(std::map<int, int>::const_iterator iwr = waveRates.cbegin(); iwr != waveRates.cend(); ++iwr)
553   {
554 
555   }
556 }
557 
558 //---------------------------------------------------------
559 //   read
560 //---------------------------------------------------------
561 
read(Xml & xml,bool)562 void Song::read(Xml& xml, bool /*isTemplate*/)
563       {
564       MusEGlobal::cloneList.clear();
565       for (;;) {
566          if (MusEGlobal::muse->progress) {
567             MusEGlobal::muse->progress->setValue(MusEGlobal::muse->progress->value()+1);
568          }
569 
570             Xml::Token token;
571             token = xml.parse();
572             const QString& tag = xml.s1();
573             switch (token) {
574                   case Xml::Error:
575                   case Xml::End:
576                         goto song_read_end;
577                   case Xml::TagStart:
578                         if (tag == "master")
579                         {
580                               // Avoid emitting songChanged.
581                               // Tick parameter is not used.
582                               MusEGlobal::tempomap.setMasterFlag(0, xml.parseInt());
583                         }
584                         else if (tag == "info")
585                               songInfoStr = xml.parse1();
586                         else if (tag == "showinfo")
587                               showSongInfo = xml.parseInt();
588                         else if (tag == "loop")
589                               setLoop(xml.parseInt());
590                         else if (tag == "punchin")
591                               setPunchin(xml.parseInt());
592                         else if (tag == "punchout")
593                               setPunchout(xml.parseInt());
594                         else if (tag == "record")
595                         // This doesn't work as there are no tracks yet at this point and this is checked
596                         // in setRecord. So better make it clear and explicit.
597                         // (Using the default autoRecEnable==true would seem wrong too at this point.)
598                         // setRecord(xml.parseInt());
599                               setRecord(false);
600                         else if (tag == "solo")
601                               soloFlag = xml.parseInt();
602                         else if (tag == "type")          // Obsolete.
603                               xml.parseInt();
604                         else if (tag == "recmode")
605                               _recMode  = xml.parseInt();
606                         else if (tag == "cycle")
607                               _cycleMode  = xml.parseInt();
608                         else if (tag == "click")
609                               setClick(xml.parseInt());
610                         else if (tag == "quantize")
611                               _quantize  = xml.parseInt();
612                         else if (tag == "len")
613                               _len  = xml.parseInt();
614                         else if (tag == "follow")
615                               _follow  = FollowMode(xml.parseInt());
616                         else if (tag == "midiDivision") {
617                               // TODO: Compare with current global setting and convert the
618                               //  song if required - similar to how the song vs. global
619                               //  sample rate ratio is handled. Ignore for now.
620                               xml.parseInt();
621                             }
622                         else if (tag == "sampleRate") {
623                               // Ignore. Sample rate setting is handled by the
624                               //  song discovery mechanism (in MusE::loadProjectFile1()).
625                               xml.parseInt();
626                             }
627                         else if (tag == "tempolist") {
628                               MusEGlobal::tempomap.read(xml);
629                               }
630                         else if (tag == "siglist")
631                               MusEGlobal::sigmap.read(xml);
632                         else if (tag == "keylist") {
633                               MusEGlobal::keymap.read(xml);
634                               }
635                         else if (tag == "miditrack") {
636                               MidiTrack* track = new MidiTrack();
637                               track->read(xml);
638                               insertTrack0(track, -1);
639                               }
640                         else if (tag == "drumtrack") { // Old drumtrack is obsolete.
641                               MidiTrack* track = new MidiTrack();
642                               track->setType(Track::DRUM);
643                               track->read(xml);
644                               track->convertToType(Track::DRUM); // Convert the notes and controllers.
645                               insertTrack0(track, -1);
646                               }
647                         else if (tag == "newdrumtrack") {
648                               MidiTrack* track = new MidiTrack();
649                               track->setType(Track::DRUM);
650                               track->read(xml);
651                               insertTrack0(track, -1);
652                               }
653                         else if (tag == "wavetrack") {
654                               MusECore::WaveTrack* track = new MusECore::WaveTrack();
655                               track->read(xml);
656                               insertTrack0(track,-1);
657                               // Now that the track has been added to the lists in insertTrack2(),
658                               //  OSC can find the track and its plugins, and start their native guis if required...
659                               track->showPendingPluginNativeGuis();
660                               }
661                         else if (tag == "AudioInput") {
662                               AudioInput* track = new AudioInput();
663                               track->read(xml);
664                               insertTrack0(track,-1);
665                               track->showPendingPluginNativeGuis();
666                               }
667                         else if (tag == "AudioOutput") {
668                               AudioOutput* track = new AudioOutput();
669                               track->read(xml);
670                               insertTrack0(track,-1);
671                               track->showPendingPluginNativeGuis();
672                               }
673                         else if (tag == "AudioGroup") {
674                               AudioGroup* track = new AudioGroup();
675                               track->read(xml);
676                               insertTrack0(track,-1);
677                               track->showPendingPluginNativeGuis();
678                               }
679                         else if (tag == "AudioAux") {
680                               AudioAux* track = new AudioAux();
681                               track->read(xml);
682                               insertTrack0(track,-1);
683                               track->showPendingPluginNativeGuis();
684                               }
685                         else if (tag == "SynthI") {
686                               SynthI* track = new SynthI();
687                               track->read(xml);
688                               // Done in SynthI::read()
689                               // insertTrack(track,-1);
690                               //track->showPendingPluginNativeGuis();
691                               }
692                         else if (tag == "Route") {
693                               readRoute(xml);
694                               }
695                         else if (tag == "marker")
696                               readMarker(xml);
697                         else if (tag == "globalPitchShift")
698                               _globalPitchShift = xml.parseInt();
699                         // REMOVE Tim. automation. Removed.
700                         // Deprecated. MusEGlobal::automation is now fixed TRUE
701                         //  for now until we decide what to do with it.
702                         else if (tag == "automation")
703                         //      MusEGlobal::automation = xml.parseInt();
704                               xml.parseInt();
705                         else if (tag == "cpos") {
706                               int pos = xml.parseInt();
707                               Pos p(pos, true);
708                               setPos(Song::CPOS, p, false, false, false);
709                               }
710                         else if (tag == "lpos") {
711                               int pos = xml.parseInt();
712                               Pos p(pos, true);
713                               setPos(Song::LPOS, p, false, false, false);
714                               }
715                         else if (tag == "rpos") {
716                               int pos = xml.parseInt();
717                               Pos p(pos, true);
718                               setPos(Song::RPOS, p, false, false, false);
719                               }
720                         else if (tag == "drummap")
721                               readDrumMap(xml, false);
722                         else if (tag == "drum_ordering")
723                               MusEGlobal::global_drum_ordering.read(xml);
724                         else
725                               xml.unknown("Song");
726                         break;
727                   case Xml::Attribut:
728                         break;
729                   case Xml::TagEnd:
730                         if (tag == "song") {
731                               goto song_read_end;
732                               }
733                   default:
734                         break;
735                   }
736             }
737 
738 song_read_end:
739       dirty = false;
740 
741       // Since cloneList is also used for copy/paste operations,
742       //  clear the copy clone list again.
743       MusEGlobal::cloneList.clear();
744       }
745 
746 //---------------------------------------------------------
747 //   write
748 //---------------------------------------------------------
749 
write(int level,Xml & xml) const750 void Song::write(int level, Xml& xml) const
751       {
752       xml.tag(level++, "song");
753       xml.strTag(level, "info", songInfoStr);
754       xml.intTag(level, "showinfo", showSongInfo);
755 // REMOVE Tim. automation. Removed.
756 // Deprecated. MusEGlobal::automation is now fixed TRUE
757 //   for now until we decide what to do with it.
758 //       xml.intTag(level, "automation", MusEGlobal::automation);
759       xml.intTag(level, "cpos", MusEGlobal::song->cpos());
760       xml.intTag(level, "rpos", MusEGlobal::song->rpos());
761       xml.intTag(level, "lpos", MusEGlobal::song->lpos());
762       xml.intTag(level, "master", MusEGlobal::tempomap.masterFlag());
763       xml.intTag(level, "loop", loopFlag);
764       xml.intTag(level, "punchin", punchinFlag);
765       xml.intTag(level, "punchout", punchoutFlag);
766       xml.intTag(level, "record", recordFlag);
767       xml.intTag(level, "solo", soloFlag);
768       xml.intTag(level, "recmode", _recMode);
769       xml.intTag(level, "cycle", _cycleMode);
770       xml.intTag(level, "click", _click);
771       xml.intTag(level, "quantize", _quantize);
772       xml.intTag(level, "len", _len);
773       xml.intTag(level, "follow", _follow);
774       // Save the current global midi division as well as current sample rate
775       //  so the song can be scaled properly if the values differ on reload.
776       xml.intTag(level, "midiDivision", MusEGlobal::config.division);
777       xml.intTag(level, "sampleRate", MusEGlobal::sampleRate);
778       if (_globalPitchShift)
779             xml.intTag(level, "globalPitchShift", _globalPitchShift);
780 
781       // Make a backup of the current clone list, to retain any 'copy' items,
782       //  so that pasting works properly after.
783       CloneList copyCloneList = MusEGlobal::cloneList;
784       MusEGlobal::cloneList.clear();
785 
786       // write tracks
787       for (ciTrack i = _tracks.begin(); i != _tracks.end(); ++i)
788             (*i)->write(level, xml);
789 
790       // write routing
791       for (ciTrack i = _tracks.begin(); i != _tracks.end(); ++i)
792             (*i)->writeRouting(level, xml);
793 
794       // Write midi device routing.
795       for (iMidiDevice i = MusEGlobal::midiDevices.begin(); i != MusEGlobal::midiDevices.end(); ++i)
796             (*i)->writeRouting(level, xml);
797 
798       // Write midi port routing.
799       for (int i = 0; i < MusECore::MIDI_PORTS; ++i)
800             MusEGlobal::midiPorts[i].writeRouting(level, xml);
801 
802       MusEGlobal::tempomap.write(level, xml);
803       MusEGlobal::sigmap.write(level, xml);
804       MusEGlobal::keymap.write(level, xml);
805       _markerList->write(level, xml);
806 
807       writeDrumMap(level, xml, false);
808       MusEGlobal::global_drum_ordering.write(level, xml);
809       xml.tag(level, "/song");
810 
811       // Restore backup of the clone list, to retain any 'copy' items,
812       //  so that pasting works properly after.
813       MusEGlobal::cloneList.clear();
814       MusEGlobal::cloneList = copyCloneList;
815       }
816 
817 //--------------------------------
818 //   resolveInstrumentReferences
819 //--------------------------------
820 
resolveInstrumentReferences()821 static void resolveInstrumentReferences()
822 {
823   for(int i = 0; i < MIDI_PORTS; ++i)
824   {
825     MidiPort* mp = &MusEGlobal::midiPorts[i];
826     const QString& name = mp->tmpInstrRef();
827     const int idx = mp->tmpTrackRef();
828     if(idx >= 0)
829     {
830       Track* track = MusEGlobal::song->tracks()->index(idx);
831       if(track && track->isSynthTrack())
832       {
833         SynthI* si = static_cast<SynthI*>(track);
834         mp->changeInstrument(si);
835       }
836     }
837     else if(!name.isEmpty())
838     {
839       mp->changeInstrument(registerMidiInstrument(name));
840     }
841 
842     // Done with temporary file references. Clear them.
843     mp->clearTmpFileRefs();
844   }
845 }
846 
847 //--------------------------------
848 //   resolveStripReferences
849 //--------------------------------
850 
resolveStripReferences(MusEGlobal::MixerConfig * mconfig)851 static void resolveStripReferences(MusEGlobal::MixerConfig* mconfig)
852 {
853   MusECore::TrackList* tl = MusEGlobal::song->tracks();
854   MusECore::Track* track;
855   MusEGlobal::StripConfigList_t& scl = mconfig->stripConfigList;
856   if(!scl.empty())
857   {
858     for(MusEGlobal::iStripConfigList isc = scl.begin(); isc != scl.end(); )
859     {
860       MusEGlobal::StripConfig& sc = *isc;
861 
862       // Is it a dud? Delete it.
863       if(sc.isNull() && sc._tmpFileIdx < 0)
864       {
865         isc = scl.erase(isc);
866         continue;
867       }
868 
869       // Does it have a temporary track index?
870       if(sc._tmpFileIdx >= 0)
871       {
872         // Find an existing track at that index.
873         track = tl->index(sc._tmpFileIdx);
874         // No corresponding track found? Delete the strip config.
875         if(!track)
876         {
877           isc = scl.erase(isc);
878           continue;
879         }
880         // Link the strip config to the track, via serial number.
881         sc._serial = track->serial();
882         // Done with temporary index. Reset it.
883         sc._tmpFileIdx = -1;
884       }
885 
886       // As for other configs with a serial and no _tmpFileIdx,
887       //  leave them alone. They may already be valid. If not,
888       //  they may need to persist for the undo system to restore from.
889       // Ultimately when saving, the strip config write() function removes duds anyway.
890 
891       ++isc;
892     }
893   }
894 }
895 
896 //--------------------------------
897 //   resolveSongfileReferences
898 //--------------------------------
899 
resolveSongfileReferences()900 void Song::resolveSongfileReferences()
901 {
902   //-----------------------------------------------
903   // Resolve instrument references:
904   //-----------------------------------------------
905   resolveInstrumentReferences();
906 
907   //-----------------------------------------------
908   // Resolve mixer strip configuration references:
909   //-----------------------------------------------
910   resolveStripReferences(&MusEGlobal::config.mixer1);
911   resolveStripReferences(&MusEGlobal::config.mixer2);
912 }
913 
914 // REMOVE Tim. samplerate. Added. TODO
915 #if 0
916 //---------------------------------------------------------
917 //   convertProjectSampleRate
918 //---------------------------------------------------------
919 
920 void Song::convertProjectSampleRate(int newRate)
921 {
922   double sratio = double(newRate) / double(MusEGlobal::sampleRate);
923 
924   for(iTrack i = _tracks.begin(); i != _tracks.end(); ++i)
925   {
926     Track* track = *i;
927     //if(track->isMidiTrack)
928     //  continue;
929 
930     PartList* parts = track->parts();
931 
932     for(iPart ip = parts->begin(); i != parts->end(); ++i)
933     {
934       Part* part = *ip;
935 
936       EventList& el = part->nonconst_events();
937 
938       for(iEvent ie = el.begin(); ie != el.end(); ++ie)
939       {
940         Event& e = *ie;
941       }
942 
943       if(part->type() == Part::FRAMES)
944       {
945         part->setFrame(double(part->frame()) * sratio);
946         part->setLenFrame(double(part->lenFrame()) * sratio);
947       }
948     }
949 
950     if(track->type == Track::WAVE)
951     {
952 
953     }
954 
955 
956   }
957 }
958 #endif
959 
960 } // namespace MusECore
961 
962 namespace MusEGui {
963 
964 //---------------------------------------------------------
965 //   readPart
966 //---------------------------------------------------------
967 
readPart(MusECore::Xml & xml)968 MusECore::Part* MusE::readPart(MusECore::Xml& xml)
969       {
970       MusECore::Part* part = nullptr;
971       for (;;) {
972             MusECore::Xml::Token token = xml.parse();
973             const QString& tag = xml.s1();
974             switch (token) {
975                   case MusECore::Xml::Error:
976                   case MusECore::Xml::End:
977                         return part;
978                   case MusECore::Xml::Text:
979                         {
980                         int trackIdx, partIdx;
981                         sscanf(tag.toLatin1().constData(), "%d:%d", &trackIdx, &partIdx);
982                         MusECore::Track* track = nullptr;
983                         //check if track index is in bounds before getting it (danvd)
984                         if(trackIdx < (int)MusEGlobal::song->tracks()->size())
985                         {
986                             track = MusEGlobal::song->tracks()->index(trackIdx);
987                         }
988                         if (track)
989                               part = track->parts()->find(partIdx);
990                         }
991                         break;
992                   case MusECore::Xml::TagStart:
993                         xml.unknown("readPart");
994                         break;
995                   case MusECore::Xml::TagEnd:
996                         if (tag == "part")
997                               return part;
998                   default:
999                         break;
1000                   }
1001             }
1002       }
1003 
1004 //---------------------------------------------------------
1005 //   readToplevels
1006 //---------------------------------------------------------
1007 
readToplevels(MusECore::Xml & xml)1008 void MusE::readToplevels(MusECore::Xml& xml)
1009 {
1010     MusECore::PartList* pl = new MusECore::PartList;
1011     for (;;) {
1012         MusECore::Xml::Token token = xml.parse();
1013         const QString& tag = xml.s1();
1014         switch (token) {
1015         case MusECore::Xml::Error:
1016         case MusECore::Xml::End:
1017             delete pl;
1018             return;
1019         case MusECore::Xml::TagStart:
1020             if (tag == "part") {
1021                 MusECore::Part* part = readPart(xml);
1022                 if (part)
1023                     pl->add(part);
1024             }
1025             else if (tag == "pianoroll") {
1026                 // p3.3.34
1027                 // Do not open if there are no parts.
1028                 // Had bogus '-1' part index for list edit in med file,
1029                 //  causing list edit to segfault on song load.
1030                 // Somehow that -1 was put there on write, because the
1031                 //  current part didn't exist anymore, so no index number
1032                 //  could be found for it on write. Watching... may be fixed.
1033                 // But for now be safe for all the top levels...
1034                 if(!pl->empty())
1035                 {
1036                     startPianoroll(pl);
1037                     toplevels.back()->readStatus(xml);
1038                     pl = new MusECore::PartList;
1039                 }
1040             }
1041             else if (tag == "scoreedit") {
1042                 MusEGui::ScoreEdit* score = new MusEGui::ScoreEdit(this, 0, _arranger->cursorValue());
1043                 toplevels.push_back(score);
1044                 connect(score, SIGNAL(isDeleting(MusEGui::TopWin*)), SLOT(toplevelDeleting(MusEGui::TopWin*)));
1045                 connect(score, SIGNAL(name_changed()), arrangerView, SLOT(scoreNamingChanged()));
1046                 score->show();
1047                 score->readStatus(xml);
1048             }
1049             else if (tag == "drumedit") {
1050                 if(!pl->empty())
1051                 {
1052                     startDrumEditor(pl);
1053                     toplevels.back()->readStatus(xml);
1054                     pl = new MusECore::PartList;
1055                 }
1056             }
1057             else if (tag == "master") {
1058                 startMasterEditor();
1059                 toplevels.back()->readStatus(xml);
1060             }
1061             else if (tag == "arrangerview") {
1062                 TopWin* tw = toplevels.findType(TopWin::ARRANGER);
1063                 tw->readStatus(xml);
1064                 tw->showMaximized();
1065             }
1066             else if (tag == "waveedit") {
1067                 if(!pl->empty())
1068                 {
1069                     startWaveEditor(pl);
1070                     toplevels.back()->readStatus(xml);
1071                     pl = new MusECore::PartList;
1072                 }
1073             }
1074             else
1075                 xml.unknown("MusE");
1076             break;
1077         case MusECore::Xml::Attribut:
1078             break;
1079         case MusECore::Xml::TagEnd:
1080             if (tag == "toplevels") {
1081                 delete pl;
1082                 return;
1083             }
1084         default:
1085             break;
1086         }
1087     }
1088 }
1089 
1090 //---------------------------------------------------------
1091 //   read
1092 //    read song
1093 //---------------------------------------------------------
1094 
read(MusECore::Xml & xml,bool doReadMidiPorts,bool isTemplate)1095 void MusE::read(MusECore::Xml& xml, bool doReadMidiPorts, bool isTemplate)
1096       {
1097       bool skipmode = true;
1098 
1099       writeTopwinState=true;
1100 
1101       for (;;) {
1102             if (progress)
1103                 progress->setValue(progress->value()+1);
1104             MusECore::Xml::Token token = xml.parse();
1105             const QString& tag = xml.s1();
1106             switch (token) {
1107                   case MusECore::Xml::Error:
1108                   case MusECore::Xml::End:
1109                         return;
1110                   case MusECore::Xml::TagStart:
1111                         if (skipmode && tag == "muse")
1112                               skipmode = false;
1113                         else if (skipmode)
1114                               break;
1115                         else if (tag == "configuration")
1116                               readConfiguration(xml, doReadMidiPorts, false /* do NOT read global settings, see below */);
1117                         /* Explanation for why "do NOT read global settings":
1118                          * if you would use true here, then muse would overwrite certain global config stuff
1119                          * by the settings stored in the song. but you don't want this. imagine that you
1120                          * send a friend a .med file. your friend opens it and baaam, his configuration is
1121                          * garbled. why? well, because these readConfigurations here would have overwritten
1122                          * parts (but not all) of his global config (like MDI/SDI, toolbar states etc.)
1123                          * with the data stored in the song. (it IS stored there. dunny why, i find it pretty
1124                          * senseless.)
1125                          *
1126                          * If you've a problem which seems to be solved by replacing "false" with "true", i've
1127                          * a better solution for you: go into conf.cpp, in void readConfiguration(Xml& xml, bool readOnlySequencer, bool doReadGlobalConfig)
1128                          * (around line 525), look for a comment like this:
1129                          * "Global and/or per-song config stuff ends here" (alternatively just search for
1130                          * "----"). Your problem is probably that some non-global setting should be loaded but
1131                          * is not. Fix it by either placing the else if (foo)... clause responsible for that
1132                          * setting to be loaded into the first part, that is, before "else if (!doReadGlobalConfig)"
1133                          * or (if the settings actually IS global and not per-song), ensure that the routine
1134                          * which writes the global (and not the song-)configuration really writes that setting.
1135                          * (it might happen that it formerly worked because it was written to the song instead
1136                          *  of the global config by mistake, and now isn't loaded anymore. write it to the
1137                          *  correct location.)
1138                          *
1139                          *                                                                                -- flo93
1140                          */
1141                         else if (tag == "song")
1142                         {
1143                               MusEGlobal::song->read(xml, isTemplate);
1144 
1145                               // Now that the song file has been fully loaded, resolve any references in the file.
1146                               MusEGlobal::song->resolveSongfileReferences();
1147 
1148                               // Now that all track and instrument references have been resolved,
1149                               //  it is safe to add all the midi controller cache values.
1150                               MusEGlobal::song->changeMidiCtrlCacheEvents(true, true, true, true, true);
1151 
1152                               MusEGlobal::audio->msgUpdateSoloStates();
1153                               // Inform the rest of the app that the song (may) have changed, using these flags.
1154                               // After this function is called, the caller can do a general Song::update() MINUS these flags,
1155                               //  like in MusE::loadProjectFile1() - the only place calling so far, as of this writing.
1156                               // Some existing windows need this, like arranger, some don't which are dynamically created after this.
1157                               MusEGlobal::song->update(SC_TRACK_INSERTED);
1158                         }
1159                         else if (tag == "toplevels")
1160                               readToplevels(xml);
1161                         else if (tag == "no_toplevels")
1162                         {
1163                               if (!isTemplate)
1164                                 writeTopwinState=false;
1165 
1166                               xml.skip("no_toplevels");
1167                         }
1168 
1169                         else
1170                               xml.unknown("muse");
1171                         break;
1172                   case MusECore::Xml::Attribut:
1173                         if (tag == "version") {
1174                               int major = xml.s2().section('.', 0, 0).toInt();
1175                               int minor = xml.s2().section('.', 1, 1).toInt();
1176                               xml.setVersion(major, minor);
1177                               }
1178                         break;
1179                   case MusECore::Xml::TagEnd:
1180                         if(!xml.isVersionEqualToLatest())
1181                         {
1182                           fprintf(stderr, "\n***WARNING***\nLoaded file version is %d.%d\nCurrent version is %d.%d\n"
1183                                   "Conversions may be applied if file is saved!\n\n",
1184                                   xml.majorVersion(), xml.minorVersion(),
1185                                   xml.latestMajorVersion(), xml.latestMinorVersion());
1186                           // Cannot construct QWidgets until QApplication created!
1187                           // Check MusEGlobal::muse which is created shortly after the application...
1188                           if(MusEGlobal::muse && MusEGlobal::config.warnOnFileVersions)
1189                           {
1190                             QString txt = tr("File version is %1.%2\nCurrent version is %3.%4\n"
1191                                              "Conversions may be applied if file is saved!")
1192                                             .arg(xml.majorVersion()).arg(xml.minorVersion())
1193                                             .arg(xml.latestMajorVersion()).arg(xml.latestMinorVersion());
1194                             QMessageBox* mb = new QMessageBox(QMessageBox::Warning,
1195                                                               tr("Opening file"),
1196                                                               txt,
1197                                                               QMessageBox::Ok, MusEGlobal::muse);
1198                             QCheckBox* cb = new QCheckBox(tr("Do not warn again"));
1199                             cb->setChecked(!MusEGlobal::config.warnOnFileVersions);
1200                             mb->setCheckBox(cb);
1201                             mb->exec();
1202                             if(!mb->checkBox()->isChecked() != MusEGlobal::config.warnOnFileVersions)
1203                             {
1204                               MusEGlobal::config.warnOnFileVersions = !mb->checkBox()->isChecked();
1205                               // Save settings. Use simple version - do NOT set style or stylesheet, this has nothing to do with that.
1206                               //MusEGlobal::muse->changeConfig(true);  // Save settings? No, wait till close.
1207                             }
1208                             delete mb;
1209                           }
1210                         }
1211                         if (!skipmode && tag == "muse")
1212                               return;
1213                   default:
1214                         break;
1215                   }
1216             }
1217       }
1218 
1219 
1220 //---------------------------------------------------------
1221 //   write
1222 //    write song
1223 //---------------------------------------------------------
1224 
write(MusECore::Xml & xml,bool writeTopwins) const1225 void MusE::write(MusECore::Xml& xml, bool writeTopwins) const
1226 {
1227     xml.header();
1228 
1229     int level = 0;
1230     xml.nput(level++, "<muse version=\"%d.%d\">\n", xml.latestMajorVersion(), xml.latestMinorVersion());
1231 
1232     writeConfiguration(level, xml);
1233 
1234     writeStatusMidiInputTransformPlugins(level, xml);
1235 
1236     MusEGlobal::song->write(level, xml);
1237 
1238     if (writeTopwins && !toplevels.empty()) {
1239         xml.tag(level++, "toplevels");
1240         for (const auto& i : toplevels) {
1241             if (i->isVisible())
1242                 i->writeStatus(level, xml);
1243         }
1244         xml.tag(level--, "/toplevels");
1245     }
1246     else if (!writeTopwins)
1247     {
1248         xml.tag(level, "no_toplevels");
1249         xml.etag(level, "no_toplevels");
1250     }
1251 
1252     xml.tag(level, "/muse");
1253 }
1254 
1255 } // namespace MusEGui
1256 
1257