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_AUDIOFILEMANAGER_H
19 #define RG_AUDIOFILEMANAGER_H
20 
21 #include <string>
22 #include <vector>
23 #include <set>
24 
25 #include <QPixmap>
26 #include <QObject>
27 #include <QUrl>
28 #include <QPointer>
29 #include <QProgressDialog>
30 
31 #include "AudioFile.h"
32 #include "PeakFileManager.h"
33 
34 #include "base/XmlExportable.h"
35 #include "base/Exception.h"
36 
37 class QProcess;
38 
39 namespace Rosegarden
40 {
41 
42 typedef std::vector<AudioFile *>::const_iterator AudioFileManagerIterator;
43 
44 /**
45  * AudioFileManager loads and maps audio files to their
46  * internal references (ids).  A point of contact for
47  * AudioFile information - loading a Composition should
48  * use this class to pick up the AudioFile references,
49  * editing the AudioFiles in a Composition will be
50  * made through this manager.
51  *
52  * This is in the sound library because it's so closely
53  * connected to other sound classes like the AudioFile
54  * ones.  However, the audio file manager itself within
55  * Rosegarden is stored in the GUI process.  This class
56  * is not (and should not be) used elsewhere within the
57  * sound or sequencer libraries.
58  */
59 class AudioFileManager : public QObject, public XmlExportable
60 {
61     Q_OBJECT
62 public:
63     AudioFileManager();
64     ~AudioFileManager() override;
65 
66     class BadAudioPathException : public Exception
67     {
68     public:
BadAudioPathException(QString path)69         BadAudioPathException(QString path) :
70             Exception(QObject::tr("Bad audio file path ") + path), m_path(path) { }
BadAudioPathException(QString path,QString file,int line)71         BadAudioPathException(QString path, QString file, int line) :
72             Exception(QObject::tr("Bad audio file path ") + path, file, line), m_path(path) { }
BadAudioPathException(const SoundFile::BadSoundFileException & e)73         BadAudioPathException(const SoundFile::BadSoundFileException &e) :
74             Exception(QObject::tr("Bad audio file path (malformed file?) ") + e.getPath()), m_path(e.getPath()) { }
75 
throw()76         ~BadAudioPathException() throw() override { }
77 
getPath()78         QString getPath() const { return m_path; }
79 
80     private:
81         QString m_path;
82     };
83 
84 private:
85     AudioFileManager(const AudioFileManager &aFM);
86     AudioFileManager &operator=(const AudioFileManager &);
87 
88 public:
89 
90     /// Create an AudioFile object from an absolute path
91     /**
92      * We use this interface to add an actual file.  This only works
93      * with files that are already in a format RG understands natively.
94      * If you are not sure about that, use importFile() or importURL()
95      * instead.
96      *
97      * throws BadAudioPathException
98      */
99     AudioFileId addFile(const QString &filePath);
100 
101     /// Create an audio file by importing from a URL
102     /**
103      * throws BadAudioPathException, BadSoundFileException
104      */
105     AudioFileId importURL(const QUrl &filePath,
106                           int targetSampleRate);
107 
108     /// Used by RoseXmlHandler to add an audio file.
109     /**
110      * throws BadAudioPathException
111      */
112     bool insertFile(const std::string &name, const QString &fileName,
113                     AudioFileId id);
114 
115     /// Does a specific file id exist?
116     bool fileExists(AudioFileId id);
117 
118     /// Does a specific file path exist?  Return ID or -1.
119     int fileExists(const QString &path);
120 
121     AudioFile* getAudioFile(AudioFileId id);
122 
123     /// Get an iterator into the list of AudioFile objects.
begin()124     AudioFileManagerIterator begin() const
125         { return m_audioFiles.begin(); }
126 
end()127     AudioFileManagerIterator end() const
128         { return m_audioFiles.end(); }
129 
130     /// Remove one audio file.
131     bool removeFile(AudioFileId id);
132     /// Remove all audio files.
133     void clear();
134 
135     /// Get the audio record path
getAudioPath()136     QString getAudioPath() const  { return m_audioPath; }
137     /// Set the audio record path
138     void setAudioPath(const QString &path);
139 
140     /// Throw if the current audio path does not exist or is not writable
141     /**
142      * throws BadAudioPathException
143      */
144     void testAudioPath();
145 
146     /**
147      * Get a new audio filename at the audio record path, inserting the
148      * projectFilename and instrumentAlias into the filename for easier
149      * recognition away from the file's original context.
150      *
151      * throws BadAudioPathException
152      */
153     AudioFile *createRecordingAudioFile(
154             QString projectName, QString instrumentAlias);
155 
156     /// Return whether a file was created by recording within this "session"
157     bool wasAudioFileRecentlyRecorded(AudioFileId id);
158 
159     /// Return whether a file was created by derivation within this "session"
160     bool wasAudioFileRecentlyDerived(AudioFileId id);
161 
162     /**
163      * Indicate that a new "session" has started from the point of
164      * view of recorded and derived audio files (e.g. that the
165      * document has been saved)
166      */
167     void resetRecentlyCreatedFiles();
168 
169     /// Create an empty file "derived from" the source (used by e.g. stretcher)
170     AudioFile *createDerivedAudioFile(AudioFileId source,
171                                       const char *prefix);
172 
173     /// Export audio files and assorted bits and bobs
174     /**
175      * The files are stored in a format that's user independent so
176      * that people can pack up and swap their songs (including audio
177      * files) and shift them about easily.
178      */
179     std::string toXmlString() const override;
180 
181     /// Generate previews for all audio files.
182     /**
183      * Generates preview peak files or peak chunks according to file type.
184      *
185      * throw BadSoundFileException, BadPeakFileException
186      */
187     void generatePreviews();
188 
189     /// Generate preview for a single audio file.
190     /**
191      * Generate a preview for a specific audio file - say if
192      * one has just been added to the AudioFileManager.
193      * Also used for generating previews if the file has been
194      * modified.
195      *
196      * throws BadSoundFileException, BadPeakFileException
197      */
198     bool generatePreview(AudioFileId id);
199 
200     /**
201      * Get a preview for an AudioFile adjusted to Segment start and
202      * end parameters (assuming they fall within boundaries).
203      *
204      * We can get back a set of values (floats) or a Pixmap if we
205      * supply the details.
206      *
207      * throws BadPeakFileException, BadAudioPathException
208      */
209     std::vector<float> getPreview(AudioFileId id,
210                                   const RealTime &startTime,
211                                   const RealTime &endTime,
212                                   int width,
213                                   bool withMinima);
214 
215     /// Draw a fixed size (fixed by QPixmap) preview of an audio file
216     /**
217      * throws BadPeakFileException, BadAudioPathException
218      */
219     void drawPreview(AudioFileId id,
220                      const RealTime &startTime,
221                      const RealTime &endTime,
222                      QPixmap *pixmap);
223 
224     /**
225      * Usually used to show how an audio Segment makes up part of
226      * an audio file.
227      *
228      * throws BadPeakFileException, BadAudioPathException
229      */
230     void drawHighlightedPreview(AudioFileId it,
231                                 const RealTime &startTime,
232                                 const RealTime &endTime,
233                                 const RealTime &highlightStart,
234                                 const RealTime &highlightEnd,
235                                 QPixmap *pixmap);
236 
237     /// Convert the user's home directory to a "~".
238     QString homeToTilde(const QString &path) const;
239     /// Expand "~" to the user's home directory.
240     QString tildeToHome(const QString &path) const;
241 
242     /// Get a split point vector from a peak file
243     /**
244      * throws BadPeakFileException, BadAudioPathException
245      */
246     std::vector<SplitPointPair>
247         getSplitPoints(AudioFileId id,
248                        const RealTime &startTime,
249                        const RealTime &endTime,
250                        int threshold,
251                        const RealTime &minTime = RealTime(0, 100000000));
252 
getExpectedSampleRate()253     int getExpectedSampleRate() const  { return m_expectedSampleRate; }
setExpectedSampleRate(int rate)254     void setExpectedSampleRate(int rate)  { m_expectedSampleRate = rate; }
255 
256     std::set<int> getActualSampleRates() const;
257 
258     /// Provide a progress dialog to be used to show progress.
setProgressDialog(QPointer<QProgressDialog> progressDialog)259     void setProgressDialog(QPointer<QProgressDialog> progressDialog)
260             { m_progressDialog = progressDialog; }
261 
262     /// Show entries for debug purposes
263     void print();
264 
265     /**
266      * Insert an audio file into the AudioFileManager and get the
267      * first allocated id for it.  Used from the RG file as we already
268      * have both name and filename/path.
269      *
270      * throws BadAudioPathException
271      */
272     //AudioFileId insertFile(const std::string &name,
273     //                       const QString &fileName);
274 
275     //const PeakFileManager &getPeakFileManager() const  { return m_peakManager; }
276     //PeakFileManager &getPeakFileManager()  { return m_peakManager; }
277 
278     /// Get the last file in the vector - the last created.
279     //AudioFile *getLastAudioFile();
280 
281 private:
282     /// The audio files we are managing.
283     std::vector<AudioFile *> m_audioFiles;
284 
285     /**
286      * Create an audio file by importing (i.e. converting and/or
287      * resampling) an existing file using the conversion library.  If
288      * you are not sure whether to use addFile() or importFile(), go for
289      * importFile().
290      *
291      * throws BadAudioPathException, BadSoundFileException
292      */
293     AudioFileId importFile(const QString &filePath,
294                            int targetSampleRate);
295 
296     /**
297      * Convert an audio file from arbitrary external format to an
298      * internal format suitable for use by addFile, using packages in
299      * Rosegarden.  This replaces the Perl script previously used. It
300      * returns 0 for OK.  This is used by importFile and importURL
301      * which normally provide the more suitable interface for import.
302      */
303     int convertAudioFile(const QString &inFile, const QString &outFile);
304 
305     /// Get a short file name from a long one (with '/'s)
306     QString getShortFilename(const QString &fileName) const;
307 
308     /// Get a directory from a full file path
309     QString getDirectory(const QString &path) const;
310 
311     /// See if we can find a given file in our search path
312     /**
313      * Returns the first occurrence of a match or the empty
314      * std::string if no match.
315      */
316     QString getFileInPath(const QString &file);
317 
318     /// Reset ID counter based on actual Audio Files in Composition
319     void updateAudioFileID(AudioFileId id);
320 
321     /// Fetch a new unique Audio File ID.
322     AudioFileId getUniqueAudioFileID();
323     /// Last Audio File ID that was handed out by getUniqueAudioFileID().
324     unsigned int m_lastAudioFileID;
325 
326     QString m_audioPath;
327 
328     PeakFileManager m_peakManager;
329 
330     // All audio files are stored in m_audioFiles.  These additional
331     // sets of pointers just refer to those that have been created by
332     // recording or derivations within the current session, and thus
333     // that the user may wish to remove at the end of the session if
334     // the document is not saved.
335     std::set<AudioFile *> m_recordedAudioFiles;
336     std::set<AudioFile *> m_derivedAudioFiles;
337 
338     int m_expectedSampleRate;
339 
340     /// Progress Dialog passed in by clients.
341     QPointer<QProgressDialog> m_progressDialog;
342 };
343 
344 
345 }
346 
347 #endif // RG_AUDIOFILEMANAGER_H
348