summaryrefslogtreecommitdiffstats
path: root/src/sound/AudioFileManager.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/sound/AudioFileManager.cpp')
-rw-r--r--src/sound/AudioFileManager.cpp1257
1 files changed, 1257 insertions, 0 deletions
diff --git a/src/sound/AudioFileManager.cpp b/src/sound/AudioFileManager.cpp
new file mode 100644
index 0000000..93be26c
--- /dev/null
+++ b/src/sound/AudioFileManager.cpp
@@ -0,0 +1,1257 @@
+// -*- c-indentation-style:"stroustrup" c-basic-offset: 4 -*-
+/*
+ Rosegarden
+ A sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral right of the authors to claim authorship of this work
+ has been asserted.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include <iostream>
+#include <kapplication.h>
+#include <fstream>
+#include <string>
+#include <dirent.h> // for new recording file
+#include <cstdio> // sprintf
+#include <cstdlib>
+#include <pthread.h>
+
+#if (__GNUC__ < 3)
+#include <strstream>
+#define stringstream strstream
+#else
+#include <sstream>
+#endif
+
+#include <kapp.h>
+#include <klocale.h>
+#include <kprocess.h>
+#include <kio/netaccess.h>
+#include <kmessagebox.h>
+
+#include <qpixmap.h>
+#include <qpainter.h>
+#include <qdatetime.h>
+#include <qfile.h>
+
+#include "AudioFile.h"
+#include "AudioFileManager.h"
+#include "WAVAudioFile.h"
+#include "BWFAudioFile.h"
+#include "MP3AudioFile.h"
+#include "misc/Strings.h"
+
+namespace Rosegarden
+{
+
+static pthread_mutex_t _audioFileManagerLock;
+
+class MutexLock
+{
+public:
+ MutexLock(pthread_mutex_t *mutex) : m_mutex(mutex)
+ {
+ pthread_mutex_lock(m_mutex);
+ }
+ ~MutexLock()
+ {
+ pthread_mutex_unlock(m_mutex);
+ }
+private:
+ pthread_mutex_t *m_mutex;
+};
+
+AudioFileManager::AudioFileManager() :
+ m_importProcess(0),
+ m_expectedSampleRate(0)
+{
+ pthread_mutexattr_t attr;
+ pthread_mutexattr_init(&attr);
+#ifdef HAVE_PTHREAD_MUTEX_RECURSIVE
+
+ pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
+#else
+#ifdef PTHREAD_MUTEX_RECURSIVE
+
+ pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
+#else
+
+ pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE_NP);
+#endif
+#endif
+
+ pthread_mutex_init(&_audioFileManagerLock, &attr);
+
+ // Set this through the set method so that the tilde gets
+ // shaken out.
+ //
+ setAudioPath("~/rosegarden");
+
+ // Retransmit progress
+ //
+ connect(&m_peakManager, SIGNAL(setProgress(int)),
+ this, SIGNAL(setProgress(int)));
+}
+
+AudioFileManager::~AudioFileManager()
+{
+ clear();
+}
+
+// Add a file from an absolute path
+//
+AudioFileId
+AudioFileManager::addFile(const std::string &filePath)
+{
+ MutexLock lock (&_audioFileManagerLock)
+ ;
+
+ QString ext;
+
+ if (filePath.length() > 3) {
+ ext = QString(filePath.substr(filePath.length() - 3, 3).c_str()).lower();
+ }
+
+ // Check for file existing already in manager by path
+ //
+ int check = fileExists(filePath);
+ if (check != -1) {
+ return AudioFileId(check);
+ }
+
+ // prepare for audio file
+ AudioFile *aF = 0;
+ AudioFileId id = getFirstUnusedID();
+
+ if (ext == "wav") {
+ // identify file type
+ AudioFileType subType = RIFFAudioFile::identifySubType(filePath);
+
+ if (subType == BWF) {
+#ifdef DEBUG_AUDIOFILEMANAGER
+ std::cout << "FOUND BWF" << std::endl;
+#endif
+
+ try {
+ aF = new BWFAudioFile(id, getShortFilename(filePath), filePath);
+ } catch (SoundFile::BadSoundFileException e) {
+ delete aF;
+ throw BadAudioPathException(e);
+ }
+ } else if (subType == WAV) {
+ try {
+ aF = new WAVAudioFile(id, getShortFilename(filePath), filePath);
+ } catch (SoundFile::BadSoundFileException e) {
+ delete aF;
+ throw BadAudioPathException(e);
+ }
+ }
+
+ // Ensure we have a valid file handle
+ //
+ if (aF == 0) {
+ std::cerr << "AudioFileManager: Unknown WAV audio file subtype in " << filePath << std::endl;
+ throw BadAudioPathException(filePath, __FILE__, __LINE__);
+ }
+
+ // Add file type on extension
+ try {
+ if (aF->open() == false) {
+ delete aF;
+ std::cerr << "AudioFileManager: Malformed audio file in " << filePath << std::endl;
+ throw BadAudioPathException(filePath, __FILE__, __LINE__);
+ }
+ } catch (SoundFile::BadSoundFileException e) {
+ delete aF;
+ throw BadAudioPathException(e);
+ }
+ }
+#ifdef HAVE_LIBMAD
+ else if (ext == "mp3") {
+ try {
+ aF = new MP3AudioFile(id, getShortFilename(filePath), filePath);
+
+ if (aF->open() == false) {
+ delete aF;
+ std::cerr << "AudioFileManager: Malformed mp3 audio file in " << filePath << std::endl;
+ throw BadAudioPathException(filePath, __FILE__, __LINE__);
+ }
+ } catch (SoundFile::BadSoundFileException e) {
+ delete aF;
+ throw BadAudioPathException(e);
+ }
+ }
+#endif // HAVE_LIBMAD
+ else {
+ std::cerr << "AudioFileManager: Unsupported audio file extension in " << filePath << std::endl;
+ throw BadAudioPathException(filePath, __FILE__, __LINE__);
+ }
+
+ if (aF) {
+ m_audioFiles.push_back(aF);
+ return id;
+ }
+
+ return 0;
+}
+
+// Convert long filename to shorter version
+std::string
+AudioFileManager::getShortFilename(const std::string &fileName)
+{
+ std::string rS = fileName;
+ unsigned int pos = rS.find_last_of("/");
+
+ if (pos > 0 && ( pos + 1 ) < rS.length())
+ rS = rS.substr(pos + 1, rS.length());
+
+ return rS;
+}
+
+// Turn a long path into a directory ending with a slash
+//
+std::string
+AudioFileManager::getDirectory(const std::string &path)
+{
+ std::string rS = path;
+ unsigned int pos = rS.find_last_of("/");
+
+ if (pos > 0 && ( pos + 1 ) < rS.length())
+ rS = rS.substr(0, pos + 1);
+
+ return rS;
+}
+
+
+// Create a new AudioFile with unique ID and label - insert from
+// our RG4 file
+//
+AudioFileId
+AudioFileManager::insertFile(const std::string &name,
+ const std::string &fileName)
+{
+ MutexLock lock (&_audioFileManagerLock)
+ ;
+
+ // first try to expand any beginning tilde
+ //
+ std::string foundFileName = substituteTildeForHome(fileName);
+
+ // If we've expanded and we can't find the file
+ // then try to find it in audio file directory.
+ //
+ QFileInfo info(foundFileName.c_str());
+ if (!info.exists())
+ foundFileName = getFileInPath(foundFileName);
+
+#ifdef DEBUG_AUDIOFILEMANAGER_INSERT_FILE
+
+ std::cout << "AudioFileManager::insertFile - "
+ << "expanded fileName = \""
+ << foundFileName << "\"" << std::endl;
+#endif
+
+ // bail if we haven't found any reasonable filename
+ if (foundFileName == "")
+ return false;
+
+ AudioFileId id = getFirstUnusedID();
+
+ WAVAudioFile *aF = 0;
+
+ try {
+
+ aF = new WAVAudioFile(id, name, foundFileName);
+
+ // if we don't recognise the file then don't insert it
+ //
+ if (aF->open() == false) {
+ delete aF;
+ std::cerr << "AudioFileManager::insertFile - don't recognise file type in " << foundFileName << std::endl;
+ throw BadAudioPathException(foundFileName, __FILE__, __LINE__);
+ }
+ m_audioFiles.push_back(aF);
+
+ } catch (SoundFile::BadSoundFileException e) {
+ delete aF;
+ throw BadAudioPathException(e);
+ }
+
+ return id;
+}
+
+
+bool
+AudioFileManager::removeFile(AudioFileId id)
+{
+ MutexLock lock (&_audioFileManagerLock)
+ ;
+
+ std::vector<AudioFile*>::iterator it;
+
+ for (it = m_audioFiles.begin();
+ it != m_audioFiles.end();
+ ++it) {
+ if ((*it)->getId() == id) {
+ m_peakManager.removeAudioFile(*it);
+ m_recordedAudioFiles.erase(*it);
+ m_derivedAudioFiles.erase(*it);
+ delete(*it);
+ m_audioFiles.erase(it);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+AudioFileId
+AudioFileManager::getFirstUnusedID()
+{
+ AudioFileId rI = 0;
+
+ if (m_audioFiles.size() == 0)
+ return rI;
+
+ std::vector<AudioFile*>::iterator it;
+
+ for (it = m_audioFiles.begin();
+ it != m_audioFiles.end();
+ ++it) {
+ if (rI < (*it)->getId())
+ rI = (*it)->getId();
+ }
+
+ rI++;
+
+ return rI;
+}
+
+bool
+AudioFileManager::insertFile(const std::string &name,
+ const std::string &fileName,
+ AudioFileId id)
+{
+ MutexLock lock (&_audioFileManagerLock)
+ ;
+
+ // first try to expany any beginning tilde
+ std::string foundFileName = substituteTildeForHome(fileName);
+
+ // If we've expanded and we can't find the file
+ // then try to find it in audio file directory.
+ //
+ QFileInfo info(foundFileName.c_str());
+ if (!info.exists())
+ foundFileName = getFileInPath(foundFileName);
+
+#ifdef DEBUG_AUDIOFILEMANAGER_INSERT_FILE
+
+ std::cout << "AudioFileManager::insertFile - "
+ << "expanded fileName = \""
+ << foundFileName << "\"" << std::endl;
+#endif
+
+ // If no joy here then we can't find this file
+ if (foundFileName == "")
+ return false;
+
+ // make sure we don't have a file of this ID hanging around already
+ removeFile(id);
+
+ // and insert
+ WAVAudioFile *aF = 0;
+
+ try {
+
+ aF = new WAVAudioFile(id, name, foundFileName);
+
+ // Test the file
+ if (aF->open() == false) {
+ delete aF;
+ return false;
+ }
+
+ m_audioFiles.push_back(aF);
+
+ } catch (SoundFile::BadSoundFileException e) {
+ delete aF;
+ throw BadAudioPathException(e);
+ }
+
+ return true;
+}
+
+// Add a given path to our sample search path
+//
+void
+AudioFileManager::setAudioPath(const std::string &path)
+{
+ MutexLock lock (&_audioFileManagerLock)
+ ;
+
+ std::string hPath = path;
+
+ // add a trailing / if we don't have one
+ //
+ if (hPath[hPath.size() - 1] != '/')
+ hPath += std::string("/");
+
+ // get the home directory
+ if (hPath[0] == '~') {
+ hPath.erase(0, 1);
+ hPath = std::string(getenv("HOME")) + hPath;
+ }
+
+ m_audioPath = hPath;
+
+}
+
+void
+AudioFileManager::testAudioPath() throw (BadAudioPathException)
+{
+ QFileInfo info(m_audioPath.c_str());
+ if (!(info.exists() && info.isDir() && !info.isRelative() &&
+ info.isWritable() && info.isReadable()))
+ throw BadAudioPathException(m_audioPath.data());
+}
+
+
+// See if we can find a given file in our search path
+// return the first occurence of a match or the empty
+// std::string if no match.
+//
+std::string
+AudioFileManager::getFileInPath(const std::string &file)
+{
+ MutexLock lock (&_audioFileManagerLock)
+ ;
+
+ QFileInfo info(file.c_str());
+
+ if (info.exists())
+ return file;
+
+ // Build the search filename from the audio path and
+ // the file basename.
+ //
+ QString searchFile = QString(m_audioPath.c_str()) + info.fileName();
+ QFileInfo searchInfo(searchFile);
+
+ if (searchInfo.exists())
+ return searchFile.latin1();
+
+ std::cout << "AudioFileManager::getFileInPath - "
+ << "searchInfo = " << searchFile << std::endl;
+
+ return "";
+}
+
+
+// Check for file path existence
+//
+int
+AudioFileManager::fileExists(const std::string &path)
+{
+ MutexLock lock (&_audioFileManagerLock)
+ ;
+
+ std::vector<AudioFile*>::iterator it;
+
+ for (it = m_audioFiles.begin();
+ it != m_audioFiles.end();
+ ++it) {
+ if ((*it)->getFilename() == path)
+ return (*it)->getId();
+ }
+
+ return -1;
+
+}
+
+// Does a specific file id exist on the manager?
+//
+bool
+AudioFileManager::fileExists(AudioFileId id)
+{
+ MutexLock lock (&_audioFileManagerLock)
+ ;
+
+ std::vector<AudioFile*>::iterator it;
+
+ for (it = m_audioFiles.begin();
+ it != m_audioFiles.end();
+ ++it) {
+ if ((*it)->getId() == id)
+ return true;
+ }
+
+ return false;
+
+}
+
+void
+AudioFileManager::clear()
+{
+ MutexLock lock (&_audioFileManagerLock)
+ ;
+
+ std::vector<AudioFile*>::iterator it;
+
+ for (it = m_audioFiles.begin();
+ it != m_audioFiles.end();
+ ++it) {
+ m_recordedAudioFiles.erase(*it);
+ m_derivedAudioFiles.erase(*it);
+ delete(*it);
+ }
+
+ m_audioFiles.erase(m_audioFiles.begin(), m_audioFiles.end());
+
+ // Clear the PeakFileManager too
+ //
+ m_peakManager.clear();
+}
+
+AudioFile *
+AudioFileManager::createRecordingAudioFile()
+{
+ MutexLock lock (&_audioFileManagerLock)
+ ;
+
+ AudioFileId newId = getFirstUnusedID();
+ QString fileName = "";
+
+ while (fileName == "") {
+
+ fileName = QString("rg-%1-%2.wav")
+ .arg(QDateTime::currentDateTime().toString("yyyyMMdd-hhmmss"))
+ .arg(newId + 1);
+
+ if (QFile(m_audioPath.c_str() + fileName).exists()) {
+ fileName = "";
+ ++newId;
+ }
+ }
+
+ // insert file into vector
+ WAVAudioFile *aF = 0;
+
+ try {
+ aF = new WAVAudioFile(newId, fileName.data(), m_audioPath + fileName.data());
+ m_audioFiles.push_back(aF);
+ m_recordedAudioFiles.insert(aF);
+ } catch (SoundFile::BadSoundFileException e) {
+ delete aF;
+ throw BadAudioPathException(e);
+ }
+
+ return aF;
+}
+
+std::vector<std::string>
+AudioFileManager::createRecordingAudioFiles(unsigned int n)
+{
+ std::vector<std::string> v;
+ for (unsigned int i = 0; i < n; ++i) {
+ AudioFile *af = createRecordingAudioFile();
+ if (af)
+ v.push_back(m_audioPath + af->getFilename().data());
+ // !af should not happen, and we have no good recovery if it does
+ }
+ return v;
+}
+
+bool
+AudioFileManager::wasAudioFileRecentlyRecorded(AudioFileId id)
+{
+ AudioFile *file = getAudioFile(id);
+ if (file)
+ return (m_recordedAudioFiles.find(file) !=
+ m_recordedAudioFiles.end());
+ return false;
+}
+
+bool
+AudioFileManager::wasAudioFileRecentlyDerived(AudioFileId id)
+{
+ AudioFile *file = getAudioFile(id);
+ if (file)
+ return (m_derivedAudioFiles.find(file) !=
+ m_derivedAudioFiles.end());
+ return false;
+}
+
+void
+AudioFileManager::resetRecentlyCreatedFiles()
+{
+ m_recordedAudioFiles.clear();
+ m_derivedAudioFiles.clear();
+}
+
+AudioFile *
+AudioFileManager::createDerivedAudioFile(AudioFileId source,
+ const char *prefix)
+{
+ MutexLock lock (&_audioFileManagerLock);
+
+ AudioFile *sourceFile = getAudioFile(source);
+ if (!sourceFile) return 0;
+
+ AudioFileId newId = getFirstUnusedID();
+ QString fileName = "";
+
+ std::string sourceBase = sourceFile->getShortFilename();
+ if (sourceBase.length() > 4 && sourceBase.substr(0, 3) == "rg-") {
+ sourceBase = sourceBase.substr(3);
+ }
+ if (sourceBase.length() > 15) sourceBase = sourceBase.substr(0, 15);
+
+ while (fileName == "") {
+
+ fileName = QString("%1-%2-%3-%4.wav")
+ .arg(prefix)
+ .arg(sourceBase)
+ .arg(QDateTime::currentDateTime().toString("yyyyMMdd-hhmmss"))
+ .arg(newId + 1);
+
+ if (QFile(m_audioPath.c_str() + fileName).exists()) {
+ fileName = "";
+ ++newId;
+ }
+ }
+
+ // insert file into vector
+ WAVAudioFile *aF = 0;
+
+ try {
+ aF = new WAVAudioFile(newId,
+ fileName.data(),
+ m_audioPath + fileName.data());
+ m_audioFiles.push_back(aF);
+ m_derivedAudioFiles.insert(aF);
+ } catch (SoundFile::BadSoundFileException e) {
+ delete aF;
+ throw BadAudioPathException(e);
+ }
+
+ return aF;
+}
+
+AudioFileId
+AudioFileManager::importURL(const KURL &url, int sampleRate)
+{
+ if (url.isLocalFile()) return importFile(url.path(), sampleRate);
+
+ std::cerr << "AudioFileManager::importURL("<< url.prettyURL() << ", " << sampleRate << ")" << std::endl;
+
+ emit setOperationName(i18n("Downloading file %1").arg(url.prettyURL()));
+
+ QString localPath = "";
+ if (!KIO::NetAccess::download(url, localPath)) {
+ KMessageBox::error(0, i18n("Cannot download file %1").arg(url.prettyURL()));
+ throw SoundFile::BadSoundFileException(url.prettyURL());
+ }
+
+ AudioFileId id = 0;
+
+ try {
+ id = importFile(localPath.data(), sampleRate);
+ } catch (BadAudioPathException ape) {
+ KIO::NetAccess::removeTempFile(localPath);
+ throw ape;
+ } catch (SoundFile::BadSoundFileException bse) {
+ KIO::NetAccess::removeTempFile(localPath);
+ throw bse;
+ }
+
+ return id;
+}
+
+bool
+AudioFileManager::fileNeedsConversion(const std::string &fileName,
+ int sampleRate)
+{
+ KProcess *proc = new KProcess();
+ *proc << "rosegarden-audiofile-importer";
+ if (sampleRate > 0) {
+ *proc << "-r";
+ *proc << QString("%1").arg(sampleRate);
+ }
+ *proc << "-w";
+ *proc << fileName.c_str();
+
+ proc->start(KProcess::Block, KProcess::NoCommunication);
+
+ int es = proc->exitStatus();
+ delete proc;
+
+ if (es == 0 || es == 1) { // 1 == "other error" -- wouldn't be able to convert
+ return false;
+ }
+ return true;
+}
+
+AudioFileId
+AudioFileManager::importFile(const std::string &fileName, int sampleRate)
+{
+ MutexLock lock (&_audioFileManagerLock);
+
+ std::cerr << "AudioFileManager::importFile("<< fileName << ", " << sampleRate << ")" << std::endl;
+
+ KProcess *proc = new KProcess();
+ *proc << "rosegarden-audiofile-importer";
+ if (sampleRate > 0) {
+ *proc << "-r";
+ *proc << QString("%1").arg(sampleRate);
+ }
+ *proc << "-w";
+ *proc << fileName.c_str();
+
+ proc->start(KProcess::Block, KProcess::NoCommunication);
+
+ int es = proc->exitStatus();
+ delete proc;
+
+ if (es == 0) {
+ AudioFileId id = addFile(fileName);
+ m_expectedSampleRate = sampleRate;
+ return id;
+ }
+
+ if (es == 2) {
+ emit setOperationName(i18n("Converting audio file..."));
+ } else if (es == 3) {
+ emit setOperationName(i18n("Resampling audio file..."));
+ } else if (es == 4) {
+ emit setOperationName(i18n("Converting and resampling audio file..."));
+ } else {
+ emit setOperationName(i18n("Importing audio file..."));
+ }
+
+ AudioFileId newId = getFirstUnusedID();
+ QString targetName = "";
+
+ QString sourceBase = QFileInfo(fileName.c_str()).baseName();
+ if (sourceBase.length() > 3 && sourceBase.startsWith("rg-")) {
+ sourceBase = sourceBase.right(sourceBase.length() - 3);
+ }
+ if (sourceBase.length() > 15) sourceBase = sourceBase.left(15);
+
+ while (targetName == "") {
+
+ targetName = QString("conv-%2-%3-%4.wav")
+ .arg(sourceBase)
+ .arg(QDateTime::currentDateTime().toString("yyyyMMdd-hhmmss"))
+ .arg(newId + 1);
+
+ if (QFile(m_audioPath.c_str() + targetName).exists()) {
+ targetName = "";
+ ++newId;
+ }
+ }
+
+ m_importProcess = new KProcess;
+
+ *m_importProcess << "rosegarden-audiofile-importer";
+ if (sampleRate > 0) {
+ *m_importProcess << "-r";
+ *m_importProcess << QString("%1").arg(sampleRate);
+ }
+ *m_importProcess << "-c";
+ *m_importProcess << fileName.c_str();
+ *m_importProcess << (m_audioPath.c_str() + targetName);
+
+ m_importProcess->start(KProcess::NotifyOnExit, KProcess::NoCommunication);
+
+ while (m_importProcess->isRunning()) {
+ kapp->processEvents(100);
+ }
+
+ if (!m_importProcess->normalExit()) {
+ // interrupted
+ throw SoundFile::BadSoundFileException(fileName, "Import cancelled");
+ }
+
+ es = m_importProcess->exitStatus();
+ delete m_importProcess;
+ m_importProcess = 0;
+
+ if (es) {
+ std::cerr << "audio file importer failed" << std::endl;
+ throw SoundFile::BadSoundFileException(fileName, i18n("Failed to convert or resample audio file on import"));
+ } else {
+ std::cerr << "audio file importer succeeded" << std::endl;
+ }
+
+ // insert file into vector
+ WAVAudioFile *aF = 0;
+
+ aF = new WAVAudioFile(newId,
+ targetName.data(),
+ m_audioPath + targetName.data());
+ m_audioFiles.push_back(aF);
+ m_derivedAudioFiles.insert(aF);
+ // Don't catch SoundFile::BadSoundFileException
+
+ m_expectedSampleRate = sampleRate;
+
+ return aF->getId();
+}
+
+void
+AudioFileManager::slotStopImport()
+{
+ if (m_importProcess) {
+ m_importProcess->kill(SIGTERM);
+ sleep(1);
+ m_importProcess->kill(SIGKILL);
+ }
+}
+
+AudioFile*
+AudioFileManager::getLastAudioFile()
+{
+ MutexLock lock (&_audioFileManagerLock)
+ ;
+
+ std::vector<AudioFile*>::iterator it = m_audioFiles.begin();
+ AudioFile* audioFile = 0;
+
+ while (it != m_audioFiles.end()) {
+ audioFile = (*it);
+ it++;
+ }
+
+ return audioFile;
+}
+
+std::string
+AudioFileManager::substituteHomeForTilde(const std::string &path)
+{
+ std::string rS = path;
+ std::string homePath = std::string(getenv("HOME"));
+
+ // if path length is less than homePath then just return unchanged
+ if (rS.length() < homePath.length())
+ return rS;
+
+ // if the first section matches the path then substitute
+ if (rS.substr(0, homePath.length()) == homePath) {
+ rS.erase(0, homePath.length());
+ rS = "~" + rS;
+ }
+
+ return rS;
+}
+
+std::string
+AudioFileManager::substituteTildeForHome(const std::string &path)
+{
+ std::string rS = path;
+ std::string homePath = std::string(getenv("HOME"));
+
+ if (rS.substr(0, 2) == std::string("~/")) {
+ rS.erase(0, 1); // erase tilde and prepend HOME env
+ rS = homePath + rS;
+ }
+
+ return rS;
+}
+
+
+
+// Export audio files and assorted bits and bobs - make sure
+// that we store the files in a format that's user independent
+// so that people can pack up and swap their songs (including
+// audio files) and shift them about easily.
+//
+std::string
+AudioFileManager::toXmlString()
+{
+ MutexLock lock (&_audioFileManagerLock)
+ ;
+
+ std::stringstream audioFiles;
+ std::string audioPath = substituteHomeForTilde(m_audioPath);
+
+ audioFiles << "<audiofiles";
+ if (m_expectedSampleRate != 0) {
+ audioFiles << " expectedRate=\"" << m_expectedSampleRate << "\"";
+ }
+ audioFiles << ">" << std::endl;
+ audioFiles << " <audioPath value=\""
+ << audioPath << "\"/>" << std::endl;
+
+ std::string fileName;
+ std::vector<AudioFile*>::iterator it;
+
+ for (it = m_audioFiles.begin(); it != m_audioFiles.end(); ++it) {
+ fileName = (*it)->getFilename();
+
+ // attempt two substitutions - If the prefix to the filename
+ // is the same as the audio path then we can dock the prefix
+ // as it'll be added again next time. If the path doesn't
+ // have the audio path in it but has our home directory in it
+ // then swap this out for a tilde '~'
+ //
+#ifdef DEBUG_AUDIOFILEMANAGER
+
+ std::cout << "DIR = " << getDirectory(fileName) << " : "
+ " PATH = " << m_audioPath << std::endl;
+#endif
+
+ if (getDirectory(fileName) == m_audioPath)
+ fileName = getShortFilename(fileName);
+ else
+ fileName = substituteHomeForTilde(fileName);
+
+ audioFiles << " <audio id=\""
+ << (*it)->getId()
+ << "\" file=\""
+ << fileName
+ << "\" label=\""
+ << encode((*it)->getName())
+ << "\"/>" << std::endl;
+ }
+
+ audioFiles << "</audiofiles>" << std::endl;
+
+#if (__GNUC__ < 3)
+
+ audioFiles << std::ends;
+#else
+
+ audioFiles << std::endl;
+#endif
+
+ return audioFiles.str();
+}
+
+// Generate preview peak files or peak chunks according
+// to file type.
+//
+void
+AudioFileManager::generatePreviews()
+{
+ MutexLock lock (&_audioFileManagerLock)
+ ;
+
+#ifdef DEBUG_AUDIOFILEMANAGER
+
+ std::cout << "AudioFileManager::generatePreviews - "
+ << "for " << m_audioFiles.size() << " files"
+ << std::endl;
+#endif
+
+
+ // Generate peaks if we need to
+ //
+ std::vector<AudioFile*>::iterator it;
+ for (it = m_audioFiles.begin(); it != m_audioFiles.end(); ++it) {
+ if (!m_peakManager.hasValidPeaks(*it))
+ m_peakManager.generatePeaks(*it, 1);
+ }
+}
+
+// Attempt to stop a preview
+//
+void
+AudioFileManager::slotStopPreview()
+{
+ MutexLock lock (&_audioFileManagerLock);
+ m_peakManager.stopPreview();
+}
+
+
+// Generate a preview for a specific audio file - say if
+// one has just been added to the AudioFileManager.
+// Also used for generating previews if the file has been
+// modified.
+//
+bool
+AudioFileManager::generatePreview(AudioFileId id)
+{
+ MutexLock lock (&_audioFileManagerLock)
+ ;
+
+ AudioFile *audioFile = getAudioFile(id);
+
+ if (audioFile == 0)
+ return false;
+
+ if (!m_peakManager.hasValidPeaks(audioFile))
+ m_peakManager.generatePeaks(audioFile, 1);
+
+ return true;
+}
+
+AudioFile*
+AudioFileManager::getAudioFile(AudioFileId id)
+{
+ MutexLock lock (&_audioFileManagerLock)
+ ;
+
+ std::vector<AudioFile*>::iterator it;
+
+ for (it = m_audioFiles.begin();
+ it != m_audioFiles.end();
+ it++) {
+ if ((*it)->getId() == id)
+ return (*it);
+ }
+ return 0;
+}
+
+std::vector<float>
+AudioFileManager::getPreview(AudioFileId id,
+ const RealTime &startTime,
+ const RealTime &endTime,
+ int width,
+ bool withMinima)
+{
+ MutexLock lock (&_audioFileManagerLock)
+ ;
+
+ AudioFile *audioFile = getAudioFile(id);
+
+ if (audioFile == 0) {
+ return std::vector<float>();
+ }
+
+ if (!m_peakManager.hasValidPeaks(audioFile)) {
+ std::cerr << "AudioFileManager::getPreview: No peaks for audio file " << audioFile->getFilename() << std::endl;
+ throw PeakFileManager::BadPeakFileException
+ (audioFile->getFilename(), __FILE__, __LINE__);
+ }
+
+ return m_peakManager.getPreview(audioFile,
+ startTime,
+ endTime,
+ width,
+ withMinima);
+}
+
+void
+AudioFileManager::drawPreview(AudioFileId id,
+ const RealTime &startTime,
+ const RealTime &endTime,
+ QPixmap *pixmap)
+{
+ MutexLock lock (&_audioFileManagerLock)
+ ;
+
+ AudioFile *audioFile = getAudioFile(id);
+ if (!audioFile)
+ return ;
+
+ if (!m_peakManager.hasValidPeaks(audioFile)) {
+ std::cerr << "AudioFileManager::getPreview: No peaks for audio file " << audioFile->getFilename() << std::endl;
+ throw PeakFileManager::BadPeakFileException
+ (audioFile->getFilename(), __FILE__, __LINE__);
+ }
+
+ std::vector<float> values = m_peakManager.getPreview
+ (audioFile,
+ startTime,
+ endTime,
+ pixmap->width(),
+ false);
+
+ QPainter painter(pixmap);
+ pixmap->fill(kapp->palette().color(QPalette::Active,
+ QColorGroup::Base));
+ painter.setPen(kapp->palette().color(QPalette::Active,
+ QColorGroup::Dark));
+
+ if (values.size() == 0) {
+#ifdef DEBUG_AUDIOFILEMANAGER
+ std::cerr << "AudioFileManager::drawPreview - "
+ << "no preview values returned!" << std::endl;
+#endif
+
+ return ;
+ }
+
+ float yStep = pixmap->height() / 2;
+ int channels = audioFile->getChannels();
+ float ch1Value, ch2Value;
+
+ if (channels == 0) {
+#ifdef DEBUG_AUDIOFILEMANAGER
+ std::cerr << "AudioFileManager::drawPreview - "
+ << "no channels in audio file!" << std::endl;
+#endif
+
+ return ;
+ }
+
+
+ // Render pixmap
+ //
+ for (int i = 0; i < pixmap->width(); i++) {
+ // Always get two values for our pixmap no matter how many
+ // channels in AudioFile as that's all we can display.
+ //
+ if (channels == 1) {
+ ch1Value = values[i];
+ ch2Value = values[i];
+ } else {
+ ch1Value = values[i * channels];
+ ch2Value = values[i * channels + 1];
+ }
+
+ painter.drawLine(i, static_cast<int>(yStep - ch1Value * yStep),
+ i, static_cast<int>(yStep + ch2Value * yStep));
+ }
+}
+
+void
+AudioFileManager::drawHighlightedPreview(AudioFileId id,
+ const RealTime &startTime,
+ const RealTime &endTime,
+ const RealTime &highlightStart,
+ const RealTime &highlightEnd,
+ QPixmap *pixmap)
+{
+ MutexLock lock (&_audioFileManagerLock)
+ ;
+
+ AudioFile *audioFile = getAudioFile(id);
+ if (!audioFile)
+ return ;
+
+ if (!m_peakManager.hasValidPeaks(audioFile)) {
+ std::cerr << "AudioFileManager::getPreview: No peaks for audio file " << audioFile->getFilename() << std::endl;
+ throw PeakFileManager::BadPeakFileException
+ (audioFile->getFilename(), __FILE__, __LINE__);
+ }
+
+ std::vector<float> values = m_peakManager.getPreview
+ (audioFile,
+ startTime,
+ endTime,
+ pixmap->width(),
+ false);
+
+ int startWidth = (int)(double(pixmap->width()) * (highlightStart /
+ (endTime - startTime)));
+ int endWidth = (int)(double(pixmap->width()) * (highlightEnd /
+ (endTime - startTime)));
+
+ QPainter painter(pixmap);
+ pixmap->fill(kapp->palette().color(QPalette::Active,
+ QColorGroup::Base));
+
+ float yStep = pixmap->height() / 2;
+ int channels = audioFile->getChannels();
+ float ch1Value, ch2Value;
+
+ // Render pixmap
+ //
+ for (int i = 0; i < pixmap->width(); ++i) {
+ if ((i * channels + (channels - 1)) >= int(values.size()))
+ break;
+
+ // Always get two values for our pixmap no matter how many
+ // channels in AudioFile as that's all we can display.
+ //
+ if (channels == 1) {
+ ch1Value = values[i];
+ ch2Value = values[i];
+ } else {
+ ch1Value = values[i * channels];
+ ch2Value = values[i * channels + 1];
+ }
+
+ if (i < startWidth || i > endWidth)
+ painter.setPen(kapp->palette().color(QPalette::Active,
+ QColorGroup::Mid));
+ else
+ painter.setPen(kapp->palette().color(QPalette::Active,
+ QColorGroup::Dark));
+
+ painter.drawLine(i, static_cast<int>(yStep - ch1Value * yStep),
+ i, static_cast<int>(yStep + ch2Value * yStep));
+ }
+}
+
+
+void
+AudioFileManager::print()
+{
+ MutexLock lock (&_audioFileManagerLock)
+ ;
+
+#ifdef DEBUG_AUDIOFILEMANAGER
+
+ std::cout << "AudioFileManager - " << m_audioFiles.size() << " entr";
+
+ if (m_audioFiles.size() == 1)
+ std::cout << "y";
+ else
+ std::cout << "ies";
+
+ std::cout << std::endl << std::endl;
+
+ std::vector<AudioFile*>::iterator it;
+ for (it = m_audioFiles.begin(); it != m_audioFiles.end(); ++it) {
+ std::cout << (*it)->getId() << " : " << (*it)->getName()
+ << " : \"" << (*it)->getFilename() << "\"" << std::endl;
+ }
+#endif
+}
+
+std::vector<SplitPointPair>
+AudioFileManager::getSplitPoints(AudioFileId id,
+ const RealTime &startTime,
+ const RealTime &endTime,
+ int threshold,
+ const RealTime &minTime)
+{
+ MutexLock lock (&_audioFileManagerLock)
+ ;
+
+ AudioFile *audioFile = getAudioFile(id);
+
+ if (audioFile == 0)
+ return std::vector<SplitPointPair>();
+
+ return m_peakManager.getSplitPoints(audioFile,
+ startTime,
+ endTime,
+ threshold,
+ minTime);
+}
+
+std::set<int>
+AudioFileManager::getActualSampleRates() const
+{
+ std::set<int> rates;
+
+ for (std::vector<AudioFile *>::const_iterator i = m_audioFiles.begin();
+ i != m_audioFiles.end(); ++i) {
+
+ unsigned int sr = (*i)->getSampleRate();
+ if (sr != 0) rates.insert(int(sr));
+ }
+
+ return rates;
+}
+
+}
+
+
+#include "AudioFileManager.moc"