1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
2 
3 /*
4     Rosegarden
5     A MIDI and audio sequencer and musical notation editor.
6     Copyright 2000-2021 the Rosegarden development team.
7 
8     Other copyrights also apply to some parts of this work.  Please
9     see the AUTHORS file and individual file headers for details.
10 
11     This program is free software; you can redistribute it and/or
12     modify it under the terms of the GNU General Public License as
13     published by the Free Software Foundation; either version 2 of the
14     License, or (at your option) any later version.  See the file
15     COPYING included with this distribution for more information.
16 */
17 
18 #ifndef RG_ROSEGARDENDOCUMENT_H
19 #define RG_ROSEGARDENDOCUMENT_H
20 
21 #include "base/Composition.h"
22 #include "base/Configuration.h"
23 #include "base/Device.h"
24 #include "base/MidiProgram.h"
25 #include "base/RealTime.h"
26 #include "base/Segment.h"
27 #include "base/Studio.h"
28 #include "gui/editors/segment/compositionview/AudioPeaksThread.h"
29 #include "sound/AudioFileManager.h"
30 #include "base/Event.h"
31 
32 #include <QObject>
33 #include <QString>
34 #include <QStringList>
35 #include <QProgressDialog>
36 #include <QPointer>
37 #include <QSharedPointer>
38 
39 #include <map>
40 #include <vector>
41 
42 class QLockFile;
43 class QWidget;
44 class QTextStream;
45 class NoteOnRecSet;
46 
47 namespace Rosegarden
48 {
49 
50 class SequenceManager;
51 class RosegardenMainViewWidget;
52 class MappedEventList;
53 class Event;
54 class EditViewBase;
55 class AudioPluginManager;
56 
57 
58 static const int MERGE_AT_END           = (1 << 0);
59 static const int MERGE_IN_NEW_TRACKS    = (1 << 1);
60 static const int MERGE_KEEP_OLD_TIMINGS = (1 << 2);
61 static const int MERGE_KEEP_NEW_TIMINGS = (1 << 3);
62 
63 
64 /// The document object for a document-view model.
65 /**
66   * The RosegardenDocument class provides a document object that can be
67   * used in conjunction with the classes RosegardenMainWindow and
68   * RosegardenMainViewWidget to create a document-view model
69   * based on QApplication and QMainWindow. Thereby, the
70   * document object is created by the RosegardenMainWindow instance and
71   * contains the document structure with related methods for
72   * manipulation of the document data by RosegardenMainViewWidget
73   * objects. Also, RosegardenDocument contains the methods for
74   * serialization of the document data from and to files.
75   *
76   * An instance of RosegardenDocument is owned by RosegardenMainWindow.
77   * The easiest way to get here without passing pointers around:
78   *
79   *   RosegardenDocument *doc = RosegardenMainWindow::self()->getDocument();
80   *
81   * RosegardenDocument owns the Composition in the document.
82   */
83 class ROSEGARDENPRIVATE_EXPORT RosegardenDocument : public QObject
84 {
85     Q_OBJECT
86 
87 public:
88 
89     /**
90      * Constructor for the fileclass of the application
91      *
92      * Our old command history under KDE didn't get wiped out when creating a
93      * temporary document to use alongside the application's current one, but
94      * that is no longer the case since Thorn.  We need to be able to avoid
95      * clearing the command history here, or certain commands wipe out the
96      * entire undo history needlessly.
97      */
98     RosegardenDocument(QObject *parent,
99                        QSharedPointer<AudioPluginManager> audioPluginManager,
100                        bool skipAutoload = false,
101                        bool clearCommandHistory = true,
102                        bool enableSound = true);
103 
104 private:
105     RosegardenDocument(const RosegardenDocument &doc);
106     RosegardenDocument& operator=(const RosegardenDocument &doc);
107 
108 public:
109     static int FILE_FORMAT_VERSION_MAJOR;
110     static int FILE_FORMAT_VERSION_MINOR;
111     static int FILE_FORMAT_VERSION_POINT;
112 
113     /**
114      * Destructor for the fileclass of the application
115      */
116     ~RosegardenDocument() override;
117 
118     /**
119      * adds a view to the document which represents the document
120      * contents. Usually this is your main view.
121      */
122     void attachView(RosegardenMainViewWidget *view);
123 
124     /**
125      * removes a view from the list of currently connected views
126      */
127     void detachView(RosegardenMainViewWidget *view);
128 
129     /**
130      * adds an Edit View (notation, matrix, event list)
131      */
132     void attachEditView(EditViewBase*);
133 
134     /**
135      * removes a view from the list of currently connected edit views
136      */
137     void detachEditView(EditViewBase*);
138 
139     /**
140      * delete all Edit Views
141      */
142     void deleteEditViews();
143 
144     /// Set the modified flag but do not notify observers.
145     /**
146      * This also clears m_autoSaved.
147      *
148      * Use this rather than slotDocumentModified() when you do not
149      * want the entire UI to refresh.  This can be used for very high
150      * frequency changes that might cause high CPU usage if the UI
151      * were refreshed every time.
152      *
153      * See slotDocumentModified() and emitDocumentModified().
154      */
155     void setModified();
156 
157     /**
158      * returns if the document is modified or not. Use this to
159      * determine if your document needs saving by the user on closing.
160      */
isModified()161     bool isModified() const { return m_modified; }
162 
163     /**
164      * clears the 'modified' status of the document (sets it back to false).
165      *
166      */
167     void clearModifiedStatus();
168 
169     /// Emit the documentModified() signal.
170     /**
171      * Use this in situations where you need a UI refresh, but the document
172      * hasn't been modified to the point of requiring a save (e.g. the Track
173      * selection has changed).
174      *
175      * See setModified() and slotDocumentModified().
176      */
emitDocumentModified()177     void emitDocumentModified()  { emit documentModified(true); }
178 
179     /**
180      * get the autosave interval in seconds
181      */
182     unsigned int getAutoSavePeriod() const;
183 
184     /**
185      * Load the document by filename and format and emit the
186      * updateViews() signal.  The "permanent" argument should be true
187      * if this document is intended to be loaded to the GUI for real
188      * editing work: in this case, any necessary device-synchronisation
189      * with the sequencer will be carried out.  If permanent is false,
190      * the sequencer's device list will be left alone.  If squelch is
191      * true, no progress dialog will be shown.
192      */
193     bool openDocument(const QString &filename,
194                       bool permanent = true,
195                       bool squelchProgressDialog = false,
196                       bool enableLock = true);
197 
198     /**
199      * merge another document into this one
200      */
201     void mergeDocument(RosegardenDocument *doc, int options);
202 
203     /**
204      * saves the document under filename and format.
205      *
206      * errMsg will be set to a user-readable error message if save fails
207      */
208     bool saveDocument(const QString &filename, QString& errMsg,
209                       bool autosave = false);
210 
211     /// Save under a new name.
212     bool saveAs(const QString &newName, QString &errMsg);
213 
214     /**
215      * exports all or part of the studio to a file.  If devices is
216      * empty, exports all devices.
217      */
218     bool exportStudio(const QString &filename,
219 		      QString &errMsg,
220                       std::vector<DeviceId> devices =
221                       std::vector<DeviceId>());
222 
223     /**
224      *   sets the path to the file connected with the document
225      */
226     void setAbsFilePath(const QString &filename);
227 
228     /**
229      * returns the pathname of the current document file
230      */
231     const QString &getAbsFilePath() const;
232 
233     /**
234      * removes the autosave file (e.g. after saving)
235      */
236     void deleteAutoSaveFile();
237 
238     /**
239      * sets the filename of the document
240      */
241     void setTitle(const QString &title);
242 
243     /**
244      * returns the title of the document
245      */
246     const QString &getTitle() const;
247 
248     /**
249      * Returns true if the file is a regular Rosegarden ".rg" file,
250      * false if it's an imported file or a new file (not yet saved)
251      */
252     bool isRegularDotRGFile() const;
253 
254     void setQuickMarker();
255     void jumpToQuickMarker();
getQuickMarkerTime()256     timeT getQuickMarkerTime() { return m_quickMarkerTime; }
257 
258     /**
259      * returns the composition (the principal constituent of the document)
260      */
getComposition()261     Composition&       getComposition()       { return m_composition; }
262 
263     /**
264      * returns the composition (the principal constituent of the document)
265      */
getComposition()266     const Composition& getComposition() const { return m_composition; }
267 
268     /*
269      * return the Studio
270      */
getStudio()271     Studio& getStudio() { return m_studio;}
272 
getStudio()273     const Studio& getStudio() const { return m_studio;}
274 
275     /*
276      * return the AudioPeaksThread
277      */
getAudioPeaksThread()278     AudioPeaksThread& getAudioPeaksThread()
279         { return m_audioPeaksThread; }
280 
getAudioPeaksThread()281     const AudioPeaksThread& getAudioPeaksThread() const
282         { return m_audioPeaksThread; }
283 
284     /*
285      * return the AudioFileManager
286      */
getAudioFileManager()287     AudioFileManager& getAudioFileManager()
288         { return m_audioFileManager; }
289 
getAudioFileManager()290     const AudioFileManager& getAudioFileManager() const
291         { return m_audioFileManager; }
292 
293     /*
294      * return the Configuration object
295      */
getConfiguration()296     DocumentConfiguration& getConfiguration() { return m_config; }
297 
getConfiguration()298     const DocumentConfiguration& getConfiguration() const
299         { return m_config; }
300 
301     /**
302      * Returns whether playing sound is enabled at all
303      */
304     bool isSoundEnabled() const;
305 
306     /// Insert some recorded MIDI events into our recording Segment.
307     /**
308      * These MIDI events come from AlsaDriver::getMappedEventList() in
309      * the sequencer thread.
310      */
311     void insertRecordedMidi(const MappedEventList &mc);
312 
313     /**
314      * Update the recording value() -- called regularly from
315      * RosegardenMainWindow::slotUpdatePlaybackPosition() while recording
316      */
317     void updateRecordingMIDISegment();
318 
319     /**
320      * Update the recording value() for audio
321      */
322     void updateRecordingAudioSegments();
323 
324     /**
325      * Tidy up the recording SegmentItems and other post record jobs
326      */
327     void stopRecordingMidi();
328     void stopRecordingAudio();
329 
330     /**
331      * And any post-play jobs
332      */
333     void stopPlaying();
334 
335     /**
336      * Register audio samples at the sequencer
337      */
338     void prepareAudio();
339 
340     /**
341      * Cause the loopChanged signal to be emitted and any
342      * associated internal work in the document to happen
343      */
344     void setLoop(timeT, timeT);
345 
346     /**
347      * Cause the document to use the given time as the origin
348      * when inserting any subsequent recorded data
349      */
setRecordStartTime(timeT t)350     void setRecordStartTime(timeT t) { m_recordStartTime = t; }
351 
352     /*
353      * Get a MappedDevice from the sequencer and add the
354      * results to our Studio
355      */
356 /*!DEVPUSH
357     void getMappedDevice(DeviceId id);
358 */
359 
360     void addRecordMIDISegment(TrackId);
361     void addRecordAudioSegment(InstrumentId, AudioFileId);
362 
363     /**
364      * Audio play and record latencies direct from the sequencer
365      */
366 
367     RealTime getAudioPlayLatency();
368     RealTime getAudioRecordLatency();
369     void updateAudioRecordLatency();
370 
371     /** Complete the add of an audio file when a new file has finished
372      * being recorded at the sequencer.  This method will ensure that
373      * the audio file is added to the AudioFileManager, that
374      * a preview is generated and that the sequencer also knows to add
375      * the new file to its own hash table.  Flow of control is a bit
376      * awkward around new audio files as timing is crucial - the gui can't
377      * access the file until lead-out information has been written by the
378      * sequencer.
379      *
380      * Note that the sequencer doesn't know the audio file id (yet),
381      * only the instrument it was recorded to.  (It does know the
382      * filename, but the instrument id is enough for us.)
383      */
384 
385     void finalizeAudioFile(InstrumentId instrument);
386 
387     /** Tell the document that an audio file has been orphaned.  An
388     * orphaned audio file is a file that was created by recording in
389     * Rosegarden during the current session, but that has been
390     * unloaded from the audio file manager.  It's therefore likely
391     * that no other application will be using it, and that that user
392     * doesn't want to keep it.  We can offer to delete these files
393     * permanently when the document is saved.
394     */
395     void addOrphanedRecordedAudioFile(QString fileName);
396     void addOrphanedDerivedAudioFile(QString fileName);
397 
398     /*
399      * Consider whether to orphan the given audio file which is about
400      * to be removed from the audio file manager.
401      */
402     void notifyAudioFileRemoval(AudioFileId id);
403 
404     /*
405     void setAudioRecordLatency(const RealTime &latency)
406         { m_audioRecordLatency = latency; }
407     void setAudioPlayLatency(const RealTime &latency)
408         { m_audioPlayLatency = latency; }
409         */
410 
411     /**
412      * Return the AudioPluginManager
413      */
getPluginManager()414     QSharedPointer<AudioPluginManager> getPluginManager()
415         { return m_pluginManager; }
416 
417     /**
418      * Return the instrument that plays segment
419      */
420     Instrument *
421     getInstrument(Segment *segment);
422 
423     /**
424      * Clear all plugins from sequencer and from gui
425      */
426     void clearAllPlugins();
427 
428     /// Send channel setups (BS/PC/CCs) for each Track.
429     void sendChannelSetups(bool reset);
430 
431     /**
432      * Initialise the Studio with a new document's settings
433      */
434     void initialiseStudio();
435 
436     /*
437      * Get the sequence manager from the app
438      */
439     SequenceManager* getSequenceManager();
440 
441     /**
442      * Set the sequence manager (called by SequenceManager itself)
443      */
444     void setSequenceManager(SequenceManager *sm);
445 
446     /**
447      * return the list of the views currently connected to the document
448      */
getViewList()449     QList<RosegardenMainViewWidget*>& getViewList() { return m_viewList; }
450 
isBeingDestroyed()451     bool isBeingDestroyed() { return m_beingDestroyed; }
452 
453     static const unsigned int MinNbOfTracks; // 64
454 
455     /// Verify that the audio path exists and can be written to.
456     void checkAudioPath(Track *track);
457 
458     bool deleteOrphanedAudioFiles(bool documentWillNotBeSaved);
459 
460     void stealLockFile(RosegardenDocument *other);
461 
462 public slots:
463     /**
464      * calls repaint() on all views connected to the document object
465      * and is called by the view by which the document has been
466      * changed.  As this view normally repaints itself, it is excluded
467      * from the paintEvent.
468      */
469     void slotUpdateAllViews(RosegardenMainViewWidget *sender);
470 
471     /// Set the modified flag and notify observers via documentModified().
472     /**
473      * This also clears m_autoSaved and emits documentModified().
474      *
475      * Call this when modifications have been made to the document and
476      * an immediate update to the UI is needed.  Do not call this too
477      * frequently as it causes a refresh of the entire UI which is very
478      * expensive.  For high-frequency changes, use setModified() and
479      * let the UI update on a timer at a reasonable pace.
480      *
481      * See setModified() and emitDocumentModified().
482      */
483     void slotDocumentModified();
484     void slotDocumentRestored();
485 
486     /**
487      * saves the document to a suitably-named backup file
488      */
489     void slotAutoSave();
490 
491     void slotSetPointerPosition(timeT);
492 
slotSetLoop(timeT s,timeT e)493     void slotSetLoop(timeT s, timeT e) {setLoop(s,e);}
494 
495     void slotDocColoursChanged();
496 
497 signals:
498     /// Emitted when the document is modified.
499     /**
500      * See slotDocumentModified().
501      *
502      * ??? These signals should be moved out of RosegardenDocument into
503      *     a separate global signal class.  Then the document can come and
504      *     go, and the various observers of these signals can stay up and
505      *     connected.  RosegardenDocument should not derive from QObject.
506      */
507     void documentModified(bool);
508 
509     /**
510      * Emitted during playback, to suggest that views should track along,
511      * as well as when pointer is moved via a click on the loop ruler.
512      */
513     void pointerPositionChanged(timeT);
514 
515     /**
516      * Emitted during recording, to indicate that some new notes (it's
517      * only emitted for notes) have appeared in the recording segment
518      * and anything tracking should track.  updatedFrom gives the
519      * start of the new region, which is presumed to extend up to the
520      * end of the segment.
521      */
522     void recordMIDISegmentUpdated(Segment *recordSegment,
523                                   timeT updatedFrom);
524 
525     /**
526      * Emitted when a new MIDI recording segment is set
527      */
528     void newMIDIRecordingSegment(Segment*);
529 
530     /**
531      * Emitted when a new audio recording segment is set
532      */
533     void newAudioRecordingSegment(Segment*);
534 
535     void makeTrackVisible(int trackPosition);
536 
537     void stoppedAudioRecording();
538     void stoppedMIDIRecording();
539     void audioFileFinalized(Segment*);
540 
541     void playPositionChanged(timeT);
542     void loopChanged(timeT, timeT);
543     /**
544      * We probably want to keep this notification as a special case.
545      * The reason being that to detect a change to the color list will
546      * require comparing a list of 420 strings.  That's a bit too much.
547      * I guess we could implement some sort of trickery like a hash
548      * or a change count that clients can cache and compare with the
549      * current value to detect a change.  Clever.  But is it too
550      * clever?  Which is easier to understand?  A special notification
551      * or a change count?
552      */
553     void docColoursChanged();
554     void devicesResyncd();
555 
556 private:
557     /**
558      * initializes the document generally
559      */
560     void newDocument();
561 
562     /**
563      * Autoload
564      */
565     void performAutoload();
566 
567     /**
568      * Parse the Rosegarden file in \a file
569      *
570      * \a errMsg will contains the error messages
571      * if parsing failed.
572      *
573      * @return false if parsing failed
574      * @see RoseXmlHandler
575      */
576     bool xmlParse(QString fileContents, QString &errMsg,
577                   bool permanent,
578                   bool &cancelled);
579 
580     /**
581      * Set the "auto saved" status of the document
582      * Doc. modification sets it to false, autosaving
583      * sets it to true
584      */
setAutoSaved(bool s)585     void setAutoSaved(bool s) { m_autoSaved = s; }
586 
587     /**
588      * Returns whether the document should be auto-saved
589      */
isAutoSaved()590     bool isAutoSaved() const { return m_autoSaved; }
591 
592     /**
593      * Returns the name of the autosave file
594      */
595     QString getAutoSaveFileName();
596 
597     /**
598      * Save document to the given file.  This function does the actual
599      * save of the file to the given filename; saveDocument() wraps
600      * this, saving to a temporary file and then renaming to the
601      * required file, so as not to lose the original if a failure
602      * occurs during overwriting.
603      */
604     bool saveDocumentActual(const QString &filename, QString& errMsg,
605                             bool autosave = false);
606 
607     /**
608      * Save one segment to the given text stream
609      */
610     void saveSegment(QTextStream&, Segment*,
611                      long totalNbOfEvents, long &count,
612                      QString extraAttributes = QString());
613 
614     /// Identifies a specific event within a specific segment.
615     /**
616      * A struct formed by a Segment pointer and an iterator into the same
617      * Segment, used in NoteOn calculations when recording MIDI.
618      */
619     struct NoteOnRec {
620         Segment *m_segment;
621         Segment::iterator m_segmentIterator;
622     };
623 
624     /**
625      * A vector of NoteOnRec elements, necessary in multitrack MIDI
626      * recording for NoteOn calculations
627      */
628     typedef std::vector<NoteOnRec> NoteOnRecSet;
629 
630     /**
631      * Store a single NoteOnRec element in the m_noteOnEvents map
632      */
633     void storeNoteOnEvent( Segment *s, Segment::iterator it,
634                            int device, int channel );
635 
636     /// Adjust the end time for a list of overlapping note events.
637     /**
638      * Adjusts the end time for all the note-on events for a
639      * device/channel/pitch.
640      *
641      * Replace recorded Note events in one or several segments, returning the
642      * resulting NoteOnRecSet
643      */
644     NoteOnRecSet* adjustEndTimes(NoteOnRecSet &rec_vec, timeT endTime);
645 
646     /**
647      * Insert a recorded event in one or several segments
648      */
649     void insertRecordedEvent(Event *ev, int device, int channel, bool isNoteOn);
650 
651     /**
652      * Transpose an entire segment relative to its destination track.  This is
653      * used for transposing a source MIDI recording segment on a per-track
654      * basis, so that the results all come out with the same sound as the
655      * original recording.
656      */
657     void transposeRecordedSegment(Segment *s);
658 
659     // File locking functions to prevent multiple users from editing
660     // the same file.
661 
662     /// Returns true if the lock was successful.
663     bool lock();
664     void release();
665 
666     static QLockFile *createLock(const QString &absFilePath);
667     static QString lockFilename(const QString &absFilePath);
668 
669     //--------------- Data members ---------------------------------
670 
671     /**
672      * the list of the views currently connected to the document
673      */
674     QList<RosegardenMainViewWidget*> m_viewList;
675 
676     /**
677      * the list of the edit views currently editing a part of this document
678      */
679     QList<EditViewBase*> m_editViewList;
680 
681     /**
682      * the modified flag of the current document
683      */
684     bool m_modified;
685 
686     /**
687      * the autosaved status of the current document
688      */
689     bool m_autoSaved;
690 
691     /**
692      * the title of the current document
693      */
694     QString m_title;
695 
696     /**
697      * absolute file path of the current document
698      */
699     QString m_absFilePath;
700 
701     /**
702      * absolute file path of the current document
703      */
704     QLockFile *m_lockFile;
705 
706     /**
707      * the composition this document is wrapping
708      */
709     Composition m_composition;
710 
711     /**
712      * stores AudioFile mappings
713      */
714     AudioFileManager m_audioFileManager;
715 
716     /**
717      * calculates AudioFile previews
718      */
719     AudioPeaksThread m_audioPeaksThread;
720 
721     typedef std::map<InstrumentId, Segment *> RecordingSegmentMap;
722 
723     /**
724      * Segments onto which we can record MIDI events
725      */
726     //Segment *m_recordMIDISegment;
727     RecordingSegmentMap m_recordMIDISegments;
728 
729     /**
730      * Segments for recording audio (per instrument)
731      */
732     RecordingSegmentMap m_recordAudioSegments;
733 
734     /**
735      * a map[Pitch] of NoteOnRecSet elements, for NoteOn calculations
736      */
737     typedef std::map<int /*pitch*/, NoteOnRecSet> PitchMap;
738 
739     /**
740      * a map[Channel] of PitchMap
741      */
742     typedef std::map<int /*channel*/, PitchMap> ChanMap;
743 
744     /**
745      * a map[Port] of ChanMap
746      */
747     typedef std::map<int /*device*/, ChanMap> NoteOnMap;
748 
749     /**
750      * During recording, we collect note-ons that haven't yet had a note-off
751      * in here
752      */
753     NoteOnMap m_noteOnEvents;
754 
755     /**
756      * the Studio
757      */
758     Studio m_studio;
759 
760     /**
761      * A configuration object
762      */
763     DocumentConfiguration m_config;
764 
765     SequenceManager *m_seqManager;
766 
767     /**
768      * AudioPluginManager - sequencer and local plugin management
769      */
770     QSharedPointer<AudioPluginManager> m_pluginManager;
771 
772     RealTime m_audioRecordLatency;
773 
774     timeT m_recordStartTime;
775 
776     timeT m_quickMarkerTime;
777 
778     std::vector<QString> m_orphanedRecordedAudioFiles;
779     std::vector<QString> m_orphanedDerivedAudioFiles;
780 
781     /**
782      *  Autosave period for this document in seconds
783      */
784     int m_autoSavePeriod;
785 
786     // Set to true when the dtor starts
787     bool m_beingDestroyed;
788 
789     /**
790      * Tells this document whether it should clear the command history upon
791      * construction.  Usually true.
792      */
793     bool m_clearCommandHistory;
794 
795     /// Enable/disable playing sounds
796     bool m_soundEnabled;
797 
798     /// Allow file lock to be released.
799     bool m_release;
800 
801     QPointer<QProgressDialog> m_progressDialog;
802 };
803 
804 
805 }
806 
807 #endif
808