changeset 456:4bccf2014f80

Merge from branch "templating"
author Chris Cannam
date Sun, 26 Jun 2011 19:56:01 +0100
parents fa0c3e79ea8e (current diff) e3644cd9cf99 (diff)
children 8f0c1b0f17fa
files main/MainWindow.cpp main/PreferencesDialog.cpp main/main.cpp
diffstat 11 files changed, 400 insertions(+), 37 deletions(-) [+]
line wrap: on
line diff
--- a/main/MainWindow.cpp	Fri Jun 24 12:17:44 2011 +0100
+++ b/main/MainWindow.cpp	Sun Jun 26 19:56:01 2011 +0100
@@ -30,6 +30,7 @@
 #include "framework/TransformUserConfigurator.h"
 #include "view/ViewManager.h"
 #include "base/Preferences.h"
+#include "base/ResourceFinder.h"
 #include "layer/WaveformLayer.h"
 #include "layer/TimeRulerLayer.h"
 #include "layer/TimeInstantLayer.h"
@@ -112,6 +113,8 @@
 #include <QCheckBox>
 #include <QRegExp>
 #include <QScrollArea>
+#include <QDesktopServices>
+#include <QFileSystemWatcher>
 
 #include <iostream>
 #include <cstdio>
@@ -137,6 +140,7 @@
     m_sliceMenu(0),
     m_recentFilesMenu(0),
     m_recentTransformsMenu(0),
+    m_templatesMenu(0),
     m_rightButtonMenu(0),
     m_rightButtonLayerMenu(0),
     m_rightButtonTransformsMenu(0),
@@ -158,7 +162,8 @@
     m_preferencesDialog(0),
     m_layerTreeDialog(0),
     m_activityLog(new ActivityLog()),
-    m_keyReference(new KeyReference())
+    m_keyReference(new KeyReference()),
+    m_templateWatcher(0)
 {
     Profiler profiler("MainWindow::MainWindow");
 
@@ -388,7 +393,7 @@
     m_keyReference->registerShortcut(action);
     menu->addAction(action);
     toolbar->addAction(action);
-
+/*
     icon = il.load("fileopensession");
     action = new QAction(icon, tr("&Open Session..."), this);
     action->setShortcut(tr("Ctrl+O"));
@@ -396,14 +401,39 @@
     connect(action, SIGNAL(triggered()), this, SLOT(openSession()));
     m_keyReference->registerShortcut(action);
     menu->addAction(action);
-
+*/
     icon = il.load("fileopen");
     icon.addPixmap(il.loadPixmap("fileopen-22"));
-
     action = new QAction(icon, tr("&Open..."), this);
+    action->setShortcut(tr("Ctrl+O"));
     action->setStatusTip(tr("Open a session file, audio file, or layer"));
     connect(action, SIGNAL(triggered()), this, SLOT(openSomething()));
     toolbar->addAction(action);
+    menu->addAction(action);
+
+    // We want this one to go on the toolbar now, if we add it at all,
+    // but on the menu later
+    QAction *iaction = new QAction(tr("&Import More Audio..."), this);
+    iaction->setShortcut(tr("Ctrl+I"));
+    iaction->setStatusTip(tr("Import an extra audio file into a new pane"));
+    connect(iaction, SIGNAL(triggered()), this, SLOT(importMoreAudio()));
+    connect(this, SIGNAL(canImportMoreAudio(bool)), iaction, SLOT(setEnabled(bool)));
+    m_keyReference->registerShortcut(iaction);
+
+    action = new QAction(tr("Open Lo&cation..."), this);
+    action->setShortcut(tr("Ctrl+Shift+O"));
+    action->setStatusTip(tr("Open or import a file from a remote URL"));
+    connect(action, SIGNAL(triggered()), this, SLOT(openLocation()));
+    m_keyReference->registerShortcut(action);
+    menu->addAction(action);
+
+    m_recentFilesMenu = menu->addMenu(tr("Open &Recent"));
+    m_recentFilesMenu->setTearOffEnabled(true);
+    setupRecentFilesMenu();
+    connect(&m_recentFiles, SIGNAL(recentChanged()),
+            this, SLOT(setupRecentFilesMenu()));
+
+    menu->addSeparator();
 
     icon = il.load("filesave");
     icon.addPixmap(il.loadPixmap("filesave-22"));
@@ -427,6 +457,7 @@
 
     menu->addSeparator();
 
+/*
     icon = il.load("fileopenaudio");
     action = new QAction(icon, tr("&Import Audio File..."), this);
     action->setShortcut(tr("Ctrl+I"));
@@ -434,14 +465,10 @@
     connect(action, SIGNAL(triggered()), this, SLOT(importAudio()));
     m_keyReference->registerShortcut(action);
     menu->addAction(action);
-
-    action = new QAction(tr("Import Secondary Audio File..."), this);
-    action->setShortcut(tr("Ctrl+Shift+I"));
-    action->setStatusTip(tr("Import an extra audio file as a separate layer"));
-    connect(action, SIGNAL(triggered()), this, SLOT(importMoreAudio()));
-    connect(this, SIGNAL(canImportMoreAudio(bool)), action, SLOT(setEnabled(bool)));
-    m_keyReference->registerShortcut(action);
-    menu->addAction(action);
+*/
+
+    // the Import action we made earlier
+    menu->addAction(iaction);
 
     action = new QAction(tr("&Export Audio File..."), this);
     action->setStatusTip(tr("Export selection as an audio file"));
@@ -475,22 +502,28 @@
 
     menu->addSeparator();
 
-    action = new QAction(tr("Open Lo&cation..."), this);
-    action->setShortcut(tr("Ctrl+Shift+O"));
-    action->setStatusTip(tr("Open or import a file from a remote URL"));
-    connect(action, SIGNAL(triggered()), this, SLOT(openLocation()));
-    m_keyReference->registerShortcut(action);
+    QString templatesMenuLabel = tr("Apply Session Template");
+    m_templatesMenu = menu->addMenu(templatesMenuLabel);
+    m_templatesMenu->setTearOffEnabled(true);
+    // We need to have a main model for this option to be useful:
+    // canExportAudio captures that
+    connect(this, SIGNAL(canExportAudio(bool)), m_templatesMenu, SLOT(setEnabled(bool)));
+
+    // Set up the menu in a moment, after m_manageTemplatesAction constructed
+
+    action = new QAction(tr("Export Session as Template..."), this);
+    connect(action, SIGNAL(triggered()), this, SLOT(saveSessionAsTemplate()));
+    // We need to have something in the session for this to be useful:
+    // canDeleteCurrentLayer captures that
+    connect(this, SIGNAL(canExportAudio(bool)), action, SLOT(setEnabled(bool)));
     menu->addAction(action);
 
-    menu->addSeparator();
-
-    m_recentFilesMenu = menu->addMenu(tr("&Recent Files"));
-    m_recentFilesMenu->setTearOffEnabled(true);
-    setupRecentFilesMenu();
-    connect(&m_recentFiles, SIGNAL(recentChanged()),
-            this, SLOT(setupRecentFilesMenu()));
-
-    menu->addSeparator();
+    m_manageTemplatesAction = new QAction(tr("Manage Exported Templates"), this);
+    connect(m_manageTemplatesAction, SIGNAL(triggered()), this, SLOT(manageSavedTemplates()));
+    menu->addAction(m_manageTemplatesAction);
+
+    setupTemplatesMenu();
+
     action = new QAction(tr("&Preferences..."), this);
     action->setStatusTip(tr("Adjust the application preferences"));
     connect(action, SIGNAL(triggered()), this, SLOT(preferences()));
@@ -1617,6 +1650,57 @@
 }
 
 void
+MainWindow::setupTemplatesMenu()
+{
+    m_templatesMenu->clear();
+
+    QAction *defaultAction = new QAction(tr("Standard Waveform"), this);
+    defaultAction->setObjectName("default");
+    connect(defaultAction, SIGNAL(triggered()), this, SLOT(applyTemplate()));
+    m_templatesMenu->addAction(defaultAction);
+
+    m_templatesMenu->addSeparator();
+
+    QAction *action = 0;
+
+    QStringList templates = ResourceFinder().getResourceFiles("templates", "svt");
+
+    bool havePersonal = false;
+
+    // (ordered by name)
+    std::set<QString> byName;
+    foreach (QString t, templates) {
+        if (!t.startsWith(":")) havePersonal = true;
+        byName.insert(QFileInfo(t).baseName());
+    }
+
+    foreach (QString t, byName) {
+        if (t.toLower() == "default") continue;
+        action = new QAction(t, this);
+        connect(action, SIGNAL(triggered()), this, SLOT(applyTemplate()));
+        m_templatesMenu->addAction(action);
+    }
+
+    if (!templates.empty()) m_templatesMenu->addSeparator();
+
+    if (!m_templateWatcher) {
+        m_templateWatcher = new QFileSystemWatcher(this);
+        m_templateWatcher->addPath(ResourceFinder().getResourceSaveDir("templates"));
+        connect(m_templateWatcher, SIGNAL(directoryChanged(const QString &)),
+                this, SLOT(setupTemplatesMenu()));
+    }
+
+    QAction *setDefaultAction = new QAction(tr("Choose Default Template..."), this);
+    setDefaultAction->setObjectName("set_default_template");
+    connect(setDefaultAction, SIGNAL(triggered()), this, SLOT(preferences()));
+    m_templatesMenu->addSeparator();
+    m_templatesMenu->addAction(setDefaultAction);
+
+    m_manageTemplatesAction->setEnabled(havePersonal);
+}
+
+
+void
 MainWindow::setupRecentTransformsMenu()
 {
     m_recentTransformsMenu->clear();
@@ -2149,7 +2233,7 @@
     QString path = getOpenFileName(FileFinder::AudioFile);
 
     if (path != "") {
-	if (openAudio(path, ReplaceMainModel) == FileOpenFailed) {
+	if (openAudio(path, ReplaceSession) == FileOpenFailed) {
             emit hideSplash();
 	    QMessageBox::critical(this, tr("Failed to open file"),
 				  tr("<b>File open failed</b><p>Audio file \"%1\" could not be opened").arg(path));
@@ -2673,7 +2757,7 @@
 
     if (path.isEmpty()) return;
 
-    FileOpenStatus status = open(path, AskUser);
+    FileOpenStatus status = open(path, ReplaceSession);
 
     if (status == FileOpenFailed) {
         emit hideSplash();
@@ -2705,7 +2789,7 @@
 
     if (text.isEmpty()) return;
 
-    FileOpenStatus status = open(text);
+    FileOpenStatus status = open(text, AskUser);
 
     if (status == FileOpenFailed) {
         emit hideSplash();
@@ -2733,7 +2817,7 @@
     QString path = action->text();
     if (path == "") return;
 
-    FileOpenStatus status = open(path);
+    FileOpenStatus status = open(path, ReplaceSession);
 
     if (status == FileOpenFailed) {
         emit hideSplash();
@@ -2747,6 +2831,70 @@
 }
 
 void
+MainWindow::applyTemplate()
+{
+    QObject *s = sender();
+    QAction *action = qobject_cast<QAction *>(s);
+
+    if (!action) {
+	std::cerr << "WARNING: MainWindow::applyTemplate: sender is not an action"
+		  << std::endl;
+	return;
+    }
+
+    QString n = action->objectName();
+    if (n == "") n = action->text();
+
+    if (n == "") {
+        std::cerr << "WARNING: MainWindow::applyTemplate: sender has no name"
+                  << std::endl;
+        return;
+    }
+
+    QString mainModelLocation;
+    WaveFileModel *mm = getMainModel();
+    if (mm) mainModelLocation = mm->getLocation();
+    if (mainModelLocation != "") {
+        openAudio(mainModelLocation, ReplaceSession, n);
+    } else {
+        openSessionTemplate(n);
+    }
+}
+
+void
+MainWindow::saveSessionAsTemplate()
+{
+    QString name = QInputDialog::getText
+        (this, tr("Enter template name"),
+         tr("Please enter a name for the saved template:"));
+    if (name == "") return;
+    
+    name.replace(QRegExp("[^\\w\\s\\.\"'-]"), "_");
+
+    ResourceFinder rf;
+    QString dir = rf.getResourceSaveDir("templates");
+    QString filename = QString("%1/%2.svt").arg(dir).arg(name);
+    if (QFile(filename).exists()) {
+        if (QMessageBox::warning(this,
+                                 tr("Template file exists"),
+                                 tr("<b>Template file exists</b><p>The template \"%1\" already exists.<br>Overwrite it?").arg(name),
+                                 QMessageBox::Ok | QMessageBox::Cancel,
+                                 QMessageBox::Cancel) != QMessageBox::Ok) {
+            return;
+        }
+    }
+
+    saveSessionTemplate(filename);
+}
+
+void
+MainWindow::manageSavedTemplates()
+{
+    ResourceFinder rf;
+    QDesktopServices::openUrl("file:" + rf.getResourceSaveDir("templates"));
+}
+
+void
 MainWindow::paneAdded(Pane *pane)
 {
     if (m_overview) m_overview->registerView(pane);
@@ -3914,9 +4062,15 @@
 void
 MainWindow::preferences()
 {
+    bool goToTemplateTab =
+        (sender() && sender()->objectName() == "set_default_template");
+
     if (!m_preferencesDialog.isNull()) {
         m_preferencesDialog->show();
         m_preferencesDialog->raise();
+        if (goToTemplateTab) {
+            m_preferencesDialog->switchToTab(PreferencesDialog::TemplateTab);
+        }
         return;
     }
 
@@ -3931,6 +4085,9 @@
     m_preferencesDialog->setAttribute(Qt::WA_DeleteOnClose);
 
     m_preferencesDialog->show();
+    if (goToTemplateTab) {
+        m_preferencesDialog->switchToTab(PreferencesDialog::TemplateTab);
+    }
 }
 
 void
--- a/main/MainWindow.h	Fri Jun 24 12:17:44 2011 +0100
+++ b/main/MainWindow.h	Sun Jun 26 19:56:01 2011 +0100
@@ -58,6 +58,7 @@
 class KeyReference;
 class Labeller;
 class ActivityLog;
+class QFileSystemWatcher;
 
 class MainWindow : public MainWindowBase
 {
@@ -83,6 +84,7 @@
     virtual void openSomething();
     virtual void openLocation();
     virtual void openRecentFile();
+    virtual void applyTemplate();
     virtual void exportAudio();
     virtual void importLayer();
     virtual void exportLayer();
@@ -140,6 +142,7 @@
 
     virtual void setupRecentFilesMenu();
     virtual void setupRecentTransformsMenu();
+    virtual void setupTemplatesMenu();
 
     virtual void playSpeedChanged(int);
     virtual void playSoloToggled();
@@ -169,6 +172,9 @@
     virtual void midiEventsAvailable();
     virtual void playStatusChanged(bool);
 
+    virtual void saveSessionAsTemplate();
+    virtual void manageSavedTemplates();
+
     virtual void website();
     virtual void help();
     virtual void about();
@@ -190,6 +196,7 @@
     QMenu                   *m_sliceMenu;
     QMenu                   *m_recentFilesMenu;
     QMenu                   *m_recentTransformsMenu;
+    QMenu                   *m_templatesMenu;
     QMenu                   *m_rightButtonMenu;
     QMenu                   *m_rightButtonLayerMenu;
     QMenu                   *m_rightButtonTransformsMenu;
@@ -206,6 +213,7 @@
     QAction                 *m_playAction;
     QAction                 *m_playSelectionAction;
     QAction                 *m_playLoopAction;
+    QAction                 *m_manageTemplatesAction;
 
     bool                     m_soloModified;
     bool                     m_prevSolo;
@@ -221,6 +229,8 @@
     ActivityLog             *m_activityLog;
     KeyReference            *m_keyReference;
 
+    QFileSystemWatcher      *m_templateWatcher;
+
     struct LayerConfiguration {
 	LayerConfiguration(LayerFactory::LayerType _layer
 			                       = LayerFactory::TimeRuler,
--- a/main/PreferencesDialog.cpp	Fri Jun 24 12:17:44 2011 +0100
+++ b/main/PreferencesDialog.cpp	Sun Jun 26 19:56:01 2011 +0100
@@ -31,12 +31,16 @@
 #include <QFileDialog>
 #include <QMessageBox>
 #include <QSpinBox>
+#include <QListWidget>
 #include <QSettings>
 
+#include <set>
+
 #include "widgets/WindowTypeSelector.h"
 #include "widgets/IconLoader.h"
 #include "base/Preferences.h"
 #include "audioio/AudioTargetFactory.h"
+#include "base/ResourceFinder.h"
 
 PreferencesDialog::PreferencesDialog(QWidget *parent, Qt::WFlags flags) :
     QDialog(parent, flags),
@@ -50,10 +54,10 @@
     QGridLayout *grid = new QGridLayout;
     setLayout(grid);
 
-    QTabWidget *tab = new QTabWidget;
-    grid->addWidget(tab, 0, 0);
+    m_tabs = new QTabWidget;
+    grid->addWidget(m_tabs, 0, 0);
     
-    tab->setTabPosition(QTabWidget::North);
+    m_tabs->setTabPosition(QTabWidget::North);
 
     // Create this first, as slots that get called from the ctor will
     // refer to it
@@ -257,7 +261,8 @@
 
     subgrid->setRowStretch(row, 10);
     
-    tab->addTab(frame, tr("&General"));
+    m_tabOrdering[GeneralTab] = m_tabs->count();
+    m_tabs->addTab(frame, tr("&General"));
 
     // Appearance tab
 
@@ -295,7 +300,8 @@
 
     subgrid->setRowStretch(row, 10);
     
-    tab->addTab(frame, tr("&Appearance"));
+    m_tabOrdering[AppearanceTab] = m_tabs->count();
+    m_tabs->addTab(frame, tr("&Appearance"));
 
     // Analysis tab
 
@@ -328,7 +334,53 @@
     
     subgrid->setRowStretch(row, 10);
     
-    tab->addTab(frame, tr("Anal&ysis"));
+    m_tabOrdering[AnalysisTab] = m_tabs->count();
+    m_tabs->addTab(frame, tr("Anal&ysis"));
+
+    // Template tab
+
+    frame = new QFrame;
+    subgrid = new QGridLayout;
+    frame->setLayout(subgrid);
+    row = 0;
+    
+    subgrid->addWidget(new QLabel(tr("Default session template for audio files:")), row++, 0);
+
+    QListWidget *lw = new QListWidget();
+    subgrid->addWidget(lw, row, 0);
+    subgrid->setRowStretch(row, 10);
+    row++;
+
+    settings.beginGroup("MainWindow");
+    m_currentTemplate = settings.value("sessiontemplate", "").toString();
+    settings.endGroup();
+
+    lw->addItem(tr("Standard Waveform"));
+    if (m_currentTemplate == "" || m_currentTemplate == "default") {
+        lw->setCurrentRow(lw->count()-1);
+    }
+    m_templates.push_back("");
+
+    QStringList templates = ResourceFinder().getResourceFiles("templates", "svt");
+
+    std::set<QString> byName;
+    foreach (QString t, templates) {
+        byName.insert(QFileInfo(t).baseName());
+    }
+
+    foreach (QString t, byName) {
+        if (t.toLower() == "default") continue;
+        m_templates.push_back(t);
+        lw->addItem(t);
+        if (m_currentTemplate == t) {
+            lw->setCurrentRow(lw->count()-1);
+        }
+    }
+
+    connect(lw, SIGNAL(currentRowChanged(int)), this, SLOT(defaultTemplateChanged(int)));
+
+    m_tabOrdering[TemplateTab] = m_tabs->count();
+    m_tabs->addTab(frame, tr("Session &Template"));
 
     QDialogButtonBox *bb = new QDialogButtonBox(Qt::Horizontal);
     grid->addWidget(bb, 1, 0);
@@ -351,6 +403,14 @@
 }
 
 void
+PreferencesDialog::switchToTab(Tab t)
+{
+    if (m_tabOrdering.contains(t)) {
+        m_tabs->setCurrentIndex(m_tabOrdering[t]);
+    }
+}
+
+void
 PreferencesDialog::windowTypeChanged(WindowType type)
 {
     m_windowType = type;
@@ -417,6 +477,13 @@
 }
 
 void
+PreferencesDialog::defaultTemplateChanged(int i)
+{
+    m_currentTemplate = m_templates[i];
+    m_applyButton->setEnabled(true);
+}
+
+void
 PreferencesDialog::tempDirRootChanged(QString r)
 {
     m_tempDirRoot = r;
@@ -488,10 +555,15 @@
         AudioTargetFactory::getInstance()->getCallbackTargetNames();
 
     QSettings settings;
+
     settings.beginGroup("Preferences");
     settings.setValue("audio-target", devices[m_audioDevice]);
     settings.endGroup();
 
+    settings.beginGroup("MainWindow");
+    settings.setValue("sessiontemplate", m_currentTemplate);
+    settings.endGroup();
+
     m_applyButton->setEnabled(false);
 
     if (m_changesOnRestart) {
--- a/main/PreferencesDialog.h	Fri Jun 24 12:17:44 2011 +0100
+++ b/main/PreferencesDialog.h	Sun Jun 26 19:56:01 2011 +0100
@@ -17,12 +17,14 @@
 #define _PREFERENCES_DIALOG_H_
 
 #include <QDialog>
+#include <QMap>
 
 #include "base/Window.h"
 
 class WindowTypeSelector;
 class QPushButton;
 class QLineEdit;
+class QTabWidget;
 
 class PreferencesDialog : public QDialog
 {
@@ -32,6 +34,14 @@
     PreferencesDialog(QWidget *parent = 0, Qt::WFlags flags = 0);
     virtual ~PreferencesDialog();
 
+    enum Tab {
+        GeneralTab,
+        AppearanceTab,
+        AnalysisTab,
+        TemplateTab
+    };
+    void switchToTab(Tab tab);
+
 public slots:
     void applicationClosing(bool quickly);
 
@@ -49,6 +59,7 @@
     void timeToTextModeChanged(int mode);
     void viewFontSizeChanged(int sz);
     void showSplashChanged(int state);
+    void defaultTemplateChanged(int);
 
     void tempDirButtonClicked();
 
@@ -60,7 +71,13 @@
     WindowTypeSelector *m_windowTypeSelector;
     QPushButton *m_applyButton;
 
+    QTabWidget *m_tabs;
+    QMap<Tab, int> m_tabOrdering;
+
     QLineEdit *m_tempDirRootEdit;
+
+    QString m_currentTemplate;
+    QStringList m_templates;
     
     WindowType m_windowType;
     int m_spectrogramSmoothing;
--- a/main/main.cpp	Fri Jun 24 12:17:44 2011 +0100
+++ b/main/main.cpp	Sun Jun 26 19:56:01 2011 +0100
@@ -468,7 +468,7 @@
     }
     if (status != MainWindow::FileOpenSucceeded) {
         if (!haveMainModel) {
-            status = m_mainWindow->open(path, MainWindow::ReplaceMainModel);
+            status = m_mainWindow->open(path, MainWindow::ReplaceSession);
             if (status == MainWindow::FileOpenSucceeded) {
                 haveMainModel = true;
             }
Binary file samples/silent.wav has changed
--- a/sonic-visualiser.qrc	Fri Jun 24 12:17:44 2011 +0100
+++ b/sonic-visualiser.qrc	Sun Jun 26 19:56:01 2011 +0100
@@ -100,10 +100,15 @@
     <file>samples/kick.wav</file>
     <file>samples/organ.wav</file>
     <file>samples/piano.wav</file>
+    <file>samples/silent.wav</file>
     <file>samples/snare.wav</file>
     <file>samples/stick.wav</file>
     <file>samples/strike.wav</file>
     <file>samples/tap.wav</file>
+    <file>templates/default.svt</file>
+    <file>templates/Scrolling Waveforms.svt</file>
+    <file>templates/Spectrograms.svt</file>
+    <file>templates/Waveform and Melodic Range Spectrogram.svt</file>
     <file>i18n/sonic-visualiser_ru.qm</file>
     <file>i18n/sonic-visualiser_en_GB.qm</file>
     <file>i18n/sonic-visualiser_en_US.qm</file>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/templates/Scrolling Waveforms.svt	Sun Jun 26 19:56:01 2011 +0100
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE sonic-visualiser>
+<sv>
+<data>
+  <model id="7" name="placeholder" sampleRate="44100" type="wavefile" file=":samples/silent.wav" mainModel="true"/>
+  <playparameters mute="false" pan="0" gain="1" pluginId="" model="7"/>
+  <layer id="8" type="timeruler" name="Ruler &lt;2&gt;" model="7"  colourName="Black" colour="#000000" darkBackground="false" />
+  <layer id="3" type="spectrogram" name="Spectrogram &lt;3&gt;" model="7"  channel="0" windowSize="1024" windowHopLevel="2" gain="1" threshold="0" minFrequency="10" maxFrequency="0" colourScale="3" colourScheme="0" colourRotation="0" frequencyScale="0" binDisplay="0" normalizeColumns="false" normalizeVisibleArea="false"/>
+  <layer id="9" type="timeruler" name="Ruler &lt;4&gt;" model="7"  colourName="Black" colour="#000000" darkBackground="false" />
+  <layer id="10" type="waveform" name="Waveform &lt;3&gt;" model="7"  gain="1" showMeans="1" greyscale="1" channelMode="0" channel="-1" scale="0" aggressive="0" autoNormalize="0" colourName="Purple" colour="#c832ff" darkBackground="false" />
+  <layer id="11" type="timeruler" name="Ruler &lt;3&gt;" model="7"  colourName="Black" colour="#000000" darkBackground="false" />
+  <layer id="12" type="spectrogram" name="Spectrogram &lt;2&gt;" model="7"  channel="-1" windowSize="4096" windowHopLevel="5" gain="1" threshold="0" minFrequency="40" maxFrequency="2000" colourScale="0" colourScheme="0" colourRotation="0" frequencyScale="1" binDisplay="2" normalizeColumns="true" normalizeVisibleArea="false"/>
+  <layer id="13" type="waveform" name="Waveform" model="7"  gain="1" showMeans="1" greyscale="0" channelMode="0" channel="-1" scale="0" aggressive="0" autoNormalize="0" colourName="Black" colour="#000000" darkBackground="false" />
+  <layer id="14" type="waveform" name="Waveform &lt;2&gt;" model="7"  gain="1" showMeans="1" greyscale="1" channelMode="0" channel="-1" scale="0" aggressive="0" autoNormalize="0" colourName="Blue" colour="#000080" darkBackground="false" />
+  <layer id="15" type="spectrogram" name="Spectrogram" model="7"  channel="-1" windowSize="8192" windowHopLevel="4" gain="1" threshold="0" minFrequency="40" maxFrequency="1500" colourScale="0" colourScheme="1" colourRotation="0" frequencyScale="1" binDisplay="0" normalizeColumns="false" normalizeVisibleArea="false"/>
+  <layer id="16" type="timeruler" name="Ruler" model="7"  colourName="Black" colour="#000000" darkBackground="false" />
+</data>
+<display>
+  <window width="1216" height="701"/>
+  <view centre="0" zoom="1024" followPan="1" followZoom="1" tracking="scroll"  type="pane" centreLineVisible="1" height="210" >
+    <layer id="14" type="waveform" name="Waveform &lt;2&gt;" model="7" visible="true"/>
+    <layer id="11" type="timeruler" name="Ruler &lt;3&gt;" model="7" visible="true"/>
+  </view>
+  <view centre="0" zoom="16" followPan="1" followZoom="0" tracking="scroll"  type="pane" centreLineVisible="1" height="335" >
+    <layer id="9" type="timeruler" name="Ruler &lt;4&gt;" model="7" visible="false"/>
+    <layer id="10" type="waveform" name="Waveform &lt;3&gt;" model="7" visible="true"/>
+  </view>
+</display>
+<selections >
+</selections>
+</sv>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/templates/Spectrograms.svt	Sun Jun 26 19:56:01 2011 +0100
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE sonic-visualiser>
+<sv>
+<data>
+  <model id="0" name="placeholder" sampleRate="44100" type="wavefile" file=":samples/silent.wav" mainModel="true"/>
+  <playparameters mute="false" pan="0" gain="1" pluginId="" model="0"/>
+  <layer id="1" type="timeruler" name="Ruler" model="0"  colourName="Black" colour="#000000" darkBackground="false" />
+  <layer id="2" type="waveform" name="Waveform" model="0"  gain="1" showMeans="1" greyscale="0" channelMode="0" channel="-1" scale="0" aggressive="0" autoNormalize="0" colourName="Black" colour="#000000" darkBackground="false" />
+  <layer id="3" type="spectrogram" name="Spectrogram" model="0"  channel="-1" windowSize="8192" windowHopLevel="4" gain="1" threshold="0" minFrequency="40" maxFrequency="1500" colourScale="0" colourScheme="1" colourRotation="0" frequencyScale="1" binDisplay="0" normalizeColumns="false" normalizeVisibleArea="false"/>
+  <layer id="4" type="timeruler" name="Ruler &lt;2&gt;" model="0"  colourName="Black" colour="#000000" darkBackground="false" />
+  <layer id="5" type="spectrogram" name="Spectrogram &lt;2&gt;" model="0"  channel="-1" windowSize="4096" windowHopLevel="5" gain="1" threshold="0" minFrequency="40" maxFrequency="2000" colourScale="0" colourScheme="0" colourRotation="0" frequencyScale="1" binDisplay="2" normalizeColumns="true" normalizeVisibleArea="false"/>
+  <layer id="6" type="spectrogram" name="Spectrogram &lt;3&gt;" model="0"  channel="0" windowSize="1024" windowHopLevel="2" gain="1" threshold="0" minFrequency="10" maxFrequency="0" colourScale="3" colourScheme="0" colourRotation="0" frequencyScale="0" binDisplay="0" normalizeColumns="false" normalizeVisibleArea="false"/>
+</data>
+<display>
+  <window width="1413" height="925"/>
+  <view centre="0" zoom="512" followPan="1" followZoom="1" tracking="page"  type="pane" centreLineVisible="1" height="254" >
+    <layer id="1" type="timeruler" name="Ruler" model="0" visible="true"/>
+    <layer id="2" type="waveform" name="Waveform" model="0" visible="true"/>
+    <layer id="6" type="spectrogram" name="Spectrogram &lt;3&gt;" model="0" visible="true"/>
+  </view>
+  <view centre="0" zoom="512" followPan="1" followZoom="1" tracking="page"  type="pane" centreLineVisible="1" height="255" >
+    <layer id="4" type="timeruler" name="Ruler &lt;2&gt;" model="0" visible="true"/>
+    <layer id="3" type="spectrogram" name="Spectrogram" model="0" visible="true"/>
+  </view>
+  <view centre="0" zoom="512" followPan="1" followZoom="1" tracking="page"  type="pane" centreLineVisible="1" height="254" >
+    <layer id="4" type="timeruler" name="Ruler &lt;2&gt;" model="0" visible="true"/>
+    <layer id="5" type="spectrogram" name="Spectrogram &lt;2&gt;" model="0" visible="true"/>
+  </view>
+</display>
+<selections >
+</selections>
+</sv>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/templates/Waveform and Melodic Range Spectrogram.svt	Sun Jun 26 19:56:01 2011 +0100
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE sonic-visualiser>
+<sv>
+<data>
+  <model id="18" name="placeholder" sampleRate="44100" type="wavefile" file=":samples/silent.wav" mainModel="true"/>
+  <playparameters mute="false" pan="0" gain="1" pluginId="" model="18"/>
+  <layer id="19" type="waveform" name="Waveform" model="18"  gain="1" showMeans="1" greyscale="0" channelMode="0" channel="-1" scale="0" aggressive="0" autoNormalize="0" colourName="Bright Blue" colour="#1e96ff" darkBackground="true" />
+  <layer id="20" type="timeruler" name="Ruler" model="18"  colourName="Bright Orange" colour="#ffbc50" darkBackground="true" />
+  <layer id="21" type="timeruler" name="Ruler &lt;2&gt;" model="18"  colourName="Bright Orange" colour="#ffbc50" darkBackground="true" />
+  <layer id="22" type="spectrogram" name="Spectrogram" model="18"  channel="-1" windowSize="8192" windowHopLevel="4" gain="1" threshold="0" minFrequency="40" maxFrequency="1500" colourScale="0" colourScheme="1" colourRotation="0" frequencyScale="1" binDisplay="0" normalizeColumns="false" normalizeVisibleArea="false"/>
+</data>
+<display>
+  <window width="1147" height="686"/>
+  <view centre="0" zoom="1024" followPan="1" followZoom="1" tracking="page"  type="pane" centreLineVisible="1" height="197" >
+    <layer id="20" type="timeruler" name="Ruler" model="18" visible="true"/>
+    <layer id="19" type="waveform" name="Waveform" model="18" visible="true"/>
+  </view>
+  <view centre="0" zoom="1024" followPan="1" followZoom="1" tracking="page"  type="pane" centreLineVisible="1" height="333" >
+    <layer id="21" type="timeruler" name="Ruler &lt;2&gt;" model="18" visible="true"/>
+    <layer id="22" type="spectrogram" name="Spectrogram" model="18" visible="true"/>
+  </view>
+</display>
+<selections >
+</selections>
+</sv>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/templates/default.svt	Sun Jun 26 19:56:01 2011 +0100
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE sonic-visualiser>
+<sv>
+<data>
+  <layer id="1" type="timeruler" name="Ruler" model="0"  colourName="Black" colour="#000000" darkBackground="false" />
+  <layer id="2" type="waveform" name="Waveform" model="0"  gain="1" showMeans="1" greyscale="1" channelMode="0" channel="-1" scale="0" aggressive="0" autoNormalize="0" colourName="Black" colour="#000000" darkBackground="false" />
+</data>
+<display>
+  <view centre="0" zoom="1024" followPan="1" followZoom="1" tracking="page"  type="pane" centreLineVisible="1">
+    <layer id="1" type="timeruler" name="Ruler" model="0" visible="true"/>
+    <layer id="2" type="waveform" name="Waveform" model="0" visible="true"/>
+  </view>
+</display>
+</sv>