changeset 81:f277a171749d

* Pull out temporary directory management into its own class * Make sure playback plugins get a default sample path in their original play parameters configuration * Save play parameters to .sv file (we aren't reloading yet though)
author Chris Cannam
date Tue, 25 Apr 2006 22:14:43 +0000
parents 8739096929dd
children bf42d8d63885
files base/PlayParameterRepository.cpp base/PlayParameters.cpp base/PlayParameters.h base/TempDirectory.cpp base/TempDirectory.h base/ViewManager.cpp base/ViewManager.h plugin/PluginXml.cpp plugin/PluginXml.h plugin/plugins/SamplePlayer.cpp
diffstat 10 files changed, 336 insertions(+), 108 deletions(-) [+]
line wrap: on
line diff
--- a/base/PlayParameterRepository.cpp	Mon Apr 24 17:06:32 2006 +0000
+++ b/base/PlayParameterRepository.cpp	Tue Apr 25 22:14:43 2006 +0000
@@ -17,6 +17,8 @@
 #include "PlayParameters.h"
 
 //!!! shouldn't be including this here -- restructure needed
+
+//!!! should the AudioGenerator actually implement all this stuff itself?  do we even want this class?
 #include "audioio/AudioGenerator.h"
 
 #include <iostream>
--- a/base/PlayParameters.cpp	Mon Apr 24 17:06:32 2006 +0000
+++ b/base/PlayParameters.cpp	Tue Apr 25 22:14:43 2006 +0000
@@ -17,6 +17,27 @@
 
 #include <iostream>
 
+QString
+PlayParameters::toXmlString(QString indent,
+                            QString extraAttributes) const
+{
+    QString s;
+    s += indent;
+    s += QString("<playParameters mute=\"%1\" pan=\"%2\" gain=\"%3\" pluginId=\"%4\" %6")
+        .arg(m_playMuted ? "true" : "false")
+        .arg(m_playPan)
+        .arg(m_playGain)
+        .arg(m_playPluginId)
+        .arg(extraAttributes);
+    if (m_playPluginConfiguration != "") {
+        s += ">\n  " + indent + m_playPluginConfiguration
+            + indent + "</playParameters>\n";
+    } else {
+        s += "/>\n";
+    }
+    return s;
+}
+
 void
 PlayParameters::setPlayMuted(bool muted)
 {
--- a/base/PlayParameters.h	Mon Apr 24 17:06:32 2006 +0000
+++ b/base/PlayParameters.h	Tue Apr 25 22:14:43 2006 +0000
@@ -18,7 +18,9 @@
 
 #include <QObject>
 
-class PlayParameters : public QObject
+#include "XmlExportable.h"
+
+class PlayParameters : public QObject, public XmlExportable
 {
     Q_OBJECT
 
@@ -32,6 +34,9 @@
     virtual QString getPlayPluginId() const { return m_playPluginId; } 
     virtual QString getPlayPluginConfiguration() const { return m_playPluginConfiguration; }
 
+    virtual QString toXmlString(QString indent = "",
+                                QString extraAttributes = "") const;
+
 public slots:
     virtual void setPlayMuted(bool muted);
     virtual void setPlayAudible(bool nonMuted);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/base/TempDirectory.cpp	Tue Apr 25 22:14:43 2006 +0000
@@ -0,0 +1,196 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    Sonic Visualiser
+    An audio file viewer and annotation editor.
+    Centre for Digital Music, Queen Mary, University of London.
+    This file copyright 2006 Chris Cannam.
+    
+    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 "TempDirectory.h"
+
+#include <QDir>
+#include <QFile>
+#include <QMutexLocker>
+
+#include <iostream>
+#include <cassert>
+
+TempDirectory *
+TempDirectory::m_instance = new TempDirectory;
+
+TempDirectory *
+TempDirectory::instance()
+{
+    return m_instance;
+}
+
+TempDirectory::DirectoryCreationFailed::DirectoryCreationFailed(QString directory) throw() :
+    m_directory(directory)
+{
+    std::cerr << "ERROR: Directory creation failed for directory: "
+              << directory.toLocal8Bit().data() << std::endl;
+}
+
+const char *
+TempDirectory::DirectoryCreationFailed::what() const throw()
+{
+    return QString("Directory creation failed for \"%1\"")
+        .arg(m_directory).toLocal8Bit().data();
+}
+
+TempDirectory::TempDirectory() :
+    m_tmpdir("")
+{
+}
+
+TempDirectory::~TempDirectory()
+{
+    std::cerr << "TempDirectory::~TempDirectory" << std::endl;
+
+    cleanup();
+}
+
+void
+TempDirectory::cleanup()
+{
+    cleanupDirectory("");
+}
+
+QString
+TempDirectory::getPath()
+{
+    QMutexLocker locker(&m_mutex);
+    
+    if (m_tmpdir != "") return m_tmpdir;
+
+    QDir systemTempDir = QDir::temp();
+
+    // Generate a temporary directory.  Qt4.1 doesn't seem to be able
+    // to do this for us, and mkdtemp is not standard.  This method is
+    // based on the way glibc does mkdtemp.
+
+    static QString chars =
+        "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
+
+    QString suffix;
+    int padlen = 6, attempts = 100;
+    unsigned int r = time(0) ^ getpid();
+
+    for (int i = 0; i < padlen; ++i) {
+        suffix += "X";
+    }
+    
+    for (int j = 0; j < attempts; ++j) {
+
+        unsigned int v = r;
+        
+        for (int i = 0; i < padlen; ++i) {
+            suffix[i] = chars[v % 62];
+            v /= 62;
+        }
+
+        QString candidate = QString("sv_%1").arg(suffix);
+
+        if (QDir::temp().mkpath(candidate)) {
+            m_tmpdir = systemTempDir.filePath(candidate);
+            break;
+        }
+
+        r = r + 7777;
+    }
+
+    if (m_tmpdir == "") {
+        throw DirectoryCreationFailed(QString("temporary subdirectory in %1")
+                                      .arg(systemTempDir.canonicalPath()));
+    }
+
+    return m_tmpdir;
+}
+
+QString
+TempDirectory::getSubDirectoryPath(QString subdir)
+{
+    QString tmpdirpath = getPath();
+    
+    QMutexLocker locker(&m_mutex);
+
+    QDir tmpdir(tmpdirpath);
+    QFileInfo fi(tmpdir.filePath(subdir));
+
+    if (!fi.exists()) {
+        if (!tmpdir.mkdir(subdir)) {
+            throw DirectoryCreationFailed(fi.filePath());
+        } else {
+            return fi.filePath();
+        }
+    } else if (fi.isDir()) {
+        return fi.filePath();
+    } else {
+        throw DirectoryCreationFailed(fi.filePath());
+    }
+}
+
+void
+TempDirectory::cleanupDirectory(QString tmpdir)
+{
+    bool isRoot = false;
+
+    if (tmpdir == "") {
+
+        m_mutex.lock();
+
+        isRoot = true;
+        tmpdir = m_tmpdir;
+
+        if (tmpdir == "") {
+            m_mutex.unlock();
+            return;
+        }
+    }
+
+    QDir dir(tmpdir);
+    dir.setFilter(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot);
+
+    for (unsigned int i = 0; i < dir.count(); ++i) {
+
+        QFileInfo fi(dir.filePath(dir[i]));
+
+        if (fi.isDir()) {
+            cleanupDirectory(fi.absoluteFilePath());
+        } else {
+            if (!QFile(fi.absoluteFilePath()).remove()) {
+                std::cerr << "WARNING: TempDirectory::cleanup: "
+                          << "Failed to unlink file \""
+                          << fi.absoluteFilePath().toStdString() << "\""
+                          << std::endl;
+            }
+        }
+    }
+
+    QString dirname = dir.dirName();
+    if (dirname != "") {
+        if (!dir.cdUp()) {
+            std::cerr << "WARNING: TempDirectory::cleanup: "
+                      << "Failed to cd to parent directory of "
+                      << tmpdir.toStdString() << std::endl;
+            return;
+        }
+        if (!dir.rmdir(dirname)) {
+            std::cerr << "WARNING: TempDirectory::cleanup: "
+                      << "Failed to remove directory "
+                      << dirname.toStdString() << std::endl;
+        } 
+    }
+
+    if (isRoot) {
+        m_tmpdir = "";
+        m_mutex.unlock();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/base/TempDirectory.h	Tue Apr 25 22:14:43 2006 +0000
@@ -0,0 +1,81 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    Sonic Visualiser
+    An audio file viewer and annotation editor.
+    Centre for Digital Music, Queen Mary, University of London.
+    This file copyright 2006 Chris Cannam.
+    
+    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.
+*/
+
+#ifndef _TEMP_DIRECTORY_H_
+#define _TEMP_DIRECTORY_H_
+
+#include <QString>
+#include <QMutex>
+
+#include <exception>
+
+/**
+ * A class that manages the creation and removal of a temporary
+ * directory tree to store data during the program run.  There is one
+ * root temporary directory for the program, created on demand and
+ * deleted when the program exits.
+ *
+ * This class is thread safe.
+ */
+
+class TempDirectory
+{
+public:
+    static TempDirectory *instance();
+    
+    virtual ~TempDirectory();
+
+    class DirectoryCreationFailed : virtual public std::exception
+    {
+    public:
+        DirectoryCreationFailed(QString directory) throw();
+        virtual DirectoryCreationFailed::~DirectoryCreationFailed() throw() { }
+        virtual const char *what() const throw();
+
+    protected:
+        QString m_directory;
+    };
+
+    /**
+     * Create the root temporary directory if necessary, and return
+     * its path.  Throw DirectoryCreationFailed if the directory
+     * cannot be created.
+     */
+    QString getPath();
+
+    /** 
+     * Create an immediate subdirectory of the root temporary
+     * directory of the given name, if it doesn't already exist, and
+     * return its path.  Throw DirectoryCreationFailed if the
+     * directory cannot be created.
+     */
+    QString getSubDirectoryPath(QString subdir);
+
+    /**
+     * Delete the temporary directory (before exiting).
+     */
+    void cleanup();
+
+protected:
+    TempDirectory();
+    void cleanupDirectory(QString tmpDir);
+    QString m_tmpdir;
+    QMutex m_mutex;
+
+    static TempDirectory *m_instance;
+};
+
+
+#endif
--- a/base/ViewManager.cpp	Mon Apr 24 17:06:32 2006 +0000
+++ b/base/ViewManager.cpp	Tue Apr 25 22:14:43 2006 +0000
@@ -20,11 +20,6 @@
 
 #include <iostream>
 
-#include <QDir>
-#include <QFile>
-
-#include <cassert>
-
 // #define DEBUG_VIEW_MANAGER 1
 
 ViewManager::ViewManager() :
@@ -52,7 +47,6 @@
 
 ViewManager::~ViewManager()
 {
-    if (m_tmpdir != "") deleteTemporaryDirectory(m_tmpdir);
 }
 
 unsigned long
@@ -361,94 +355,6 @@
     }
 }
 
-QString
-ViewManager::getTemporaryDirectory()
-{
-    if (m_tmpdir != "") return m_tmpdir;
-
-    // Generate a temporary directory.  Qt4.1 doesn't seem to be able
-    // to do this for us, and mkdtemp is not standard.  This method is
-    // based on the way glibc does mkdtemp.
-
-    static QString chars =
-        "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
-
-    QString suffix;
-    int padlen = 6, attempts = 100;
-    unsigned int r = time(0) ^ getpid();
-
-    for (int i = 0; i < padlen; ++i) {
-        suffix += "X";
-    }
-    
-    for (int j = 0; j < attempts; ++j) {
-
-        unsigned int v = r;
-        
-        for (int i = 0; i < padlen; ++i) {
-            suffix[i] = chars[v % 62];
-            v /= 62;
-        }
-
-        QString candidate = QString("sv_%1").arg(suffix);
-
-        if (QDir::temp().mkpath(candidate)) {
-            m_tmpdir = QDir::temp().filePath(candidate);
-            break;
-        }
-
-        r = r + 7777;
-    }
-
-    if (m_tmpdir == "") {
-        std::cerr << "ERROR: ViewManager::getTemporaryDirectory: "
-                  << "Unable to create a temporary directory!" << std::endl;
-        assert(0);
-    }
-
-    return m_tmpdir;
-}
-
-void
-ViewManager::deleteTemporaryDirectory(QString tmpdir)
-{
-    if (tmpdir == "") return;
-    
-    QDir dir(tmpdir);
-    dir.setFilter(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot);
-
-    for (unsigned int i = 0; i < dir.count(); ++i) {
-
-        QFileInfo fi(dir.filePath(dir[i]));
-
-        if (fi.isDir()) {
-            deleteTemporaryDirectory(fi.absoluteFilePath());
-        } else {
-            if (!QFile(fi.absoluteFilePath()).remove()) {
-                std::cerr << "WARNING: ViewManager::deleteTemporaryDirectory: "
-                          << "Failed to unlink file \""
-                          << fi.absoluteFilePath().toStdString() << "\""
-                          << std::endl;
-            }
-        }
-    }
-
-    QString dirname = dir.dirName();
-    if (dirname != "") {
-        if (!dir.cdUp()) {
-            std::cerr << "WARNING: ViewManager::deleteTemporaryDirectory: "
-                      << "Failed to cd to parent directory of "
-                      << tmpdir.toStdString() << std::endl;
-            return;
-        }
-        if (!dir.rmdir(dirname)) {
-            std::cerr << "WARNING: ViewManager::deleteTemporaryDirectory: "
-                      << "Failed to remove directory "
-                      << dirname.toStdString() << std::endl;
-        } 
-    }
-}
-
 #ifdef INCLUDE_MOCFILES
 #include "ViewManager.moc.cpp"
 #endif
--- a/base/ViewManager.h	Mon Apr 24 17:06:32 2006 +0000
+++ b/base/ViewManager.h	Tue Apr 25 22:14:43 2006 +0000
@@ -105,8 +105,6 @@
     void setOverlayMode(OverlayMode mode);
     OverlayMode getOverlayMode() const { return m_overlayMode; }
 
-    QString getTemporaryDirectory();
-
 signals:
     /** Emitted when a widget pans.  The originator identifies the widget. */
     void centreFrameChanged(void *originator, unsigned long frame, bool locked);
@@ -184,9 +182,6 @@
     };
 
     OverlayMode m_overlayMode;
-
-    void deleteTemporaryDirectory(QString);
-    QString m_tmpdir;
 };
 
 #endif
--- a/plugin/PluginXml.cpp	Mon Apr 24 17:06:32 2006 +0000
+++ b/plugin/PluginXml.cpp	Tue Apr 25 22:14:43 2006 +0000
@@ -36,6 +36,24 @@
 PluginXml::~PluginXml() { }
 
 QString
+PluginXml::encodeConfigurationChars(QString text)
+{
+    QString rv(text);
+    rv.replace(";", "[[SEMICOLON]]");
+    rv.replace("=", "[[EQUALS]]");
+    return rv;
+}
+
+QString
+PluginXml::decodeConfigurationChars(QString text)
+{
+    QString rv(text);
+    rv.replace("[[SEMICOLON]]", ";");
+    rv.replace("[[EQUALS]]", "=");
+    return rv;
+}
+    
+QString
 PluginXml::toXmlString(QString indent, QString extraAttributes) const
 {
     QString s;
@@ -74,10 +92,8 @@
              i != configurePairs.end(); ++i) {
             QString key = i->first.c_str();
             QString value = i->second.c_str();
-            key.replace(";", "[[SEMICOLON]]");
-            key.replace("=", "[[EQUALS]]");
-            value.replace(";", "[[SEMICOLON]]");
-            value.replace("=", "[[EQUALS]]");
+            key = encodeConfigurationChars(key);
+            value = encodeConfigurationChars(value);
             if (config != "") config += ";";
             config += QString("%1=%2").arg(key).arg(value);
         }
@@ -128,10 +144,8 @@
                     continue;
                 }
                 QString key(kv[0]), value(kv[1]);
-                key.replace("[[SEMICOLON]]", ";");
-                key.replace("[[EQUALS]]", ";");
-                value.replace("[[SEMICOLON]]", ";");
-                value.replace("[[SEMICOLON]]", ";");
+                key = decodeConfigurationChars(key);
+                value = decodeConfigurationChars(value);
                 rtpi->configure(key.toStdString(), value.toStdString());
             }
         }
--- a/plugin/PluginXml.h	Mon Apr 24 17:06:32 2006 +0000
+++ b/plugin/PluginXml.h	Tue Apr 25 22:14:43 2006 +0000
@@ -47,6 +47,9 @@
      */
     virtual void setParametersFromXml(QString xml);
 
+    static QString encodeConfigurationChars(QString text);
+    static QString decodeConfigurationChars(QString text);
+
 protected:
     QString stripInvalidParameterNameCharacters(QString) const;
 
--- a/plugin/plugins/SamplePlayer.cpp	Mon Apr 24 17:06:32 2006 +0000
+++ b/plugin/plugins/SamplePlayer.cpp	Tue Apr 25 22:14:43 2006 +0000
@@ -217,6 +217,9 @@
 
 	QMutexLocker locker(&player->m_mutex);
 
+
+        //!!! What do we do if receiving an antique path pointing at things that no longer exist?
+
         player->m_samplePath = value;
 
         if (player->m_sampleSearchComplete) {
@@ -340,6 +343,8 @@
 {
     if (m_sampleSearchComplete) return;
 
+    m_samples.clear();
+
     std::cerr << "Current working directory is \"" << getcwd(0, 0) << "\"" << std::endl;
 
     std::cerr << "SamplePlayer::searchSamples: Path is \""