changeset 2277:b0ad5a8cabb9 single-point

Merge from default branch
author Chris Cannam
date Thu, 16 May 2019 11:14:53 +0100
parents 9855afb55b7a (diff) 1a6f786c1eab (current diff)
children 793e0010c52e
files
diffstat 17 files changed, 203 insertions(+), 116 deletions(-) [+]
line wrap: on
line diff
--- a/README.OSC	Thu May 16 11:14:39 2019 +0100
+++ b/README.OSC	Thu May 16 11:14:53 2019 +0100
@@ -86,6 +86,13 @@
      WAV file.  This action will try to fail rather than overwrite
      an existing file, but you probably shouldn't rely on that.
 
+  /exportlayer <filename>
+
+     Export the current layer to a file, of type determined from the
+     file's suffix. See /setcurrent for how to change which layer is
+     current. This action will try to fail rather than overwrite an
+     existing file, but you probably shouldn't rely on that.
+
   /jump <t>
   /jump end
   /jump selection
--- a/deploy/win32/build-32.bat	Thu May 16 11:14:39 2019 +0100
+++ b/deploy/win32/build-32.bat	Thu May 16 11:14:53 2019 +0100
@@ -1,4 +1,4 @@
-rem  Run this from within the top-level SV dir: deploy\win64\build-32.bat
+rem  Run this from within the top-level SV dir: deploy\win32\build-32.bat
 rem  To build from clean, delete the folder build_win32
 
 set STARTPWD=%CD%
--- a/main/MainWindow.cpp	Thu May 16 11:14:39 2019 +0100
+++ b/main/MainWindow.cpp	Thu May 16 11:14:53 2019 +0100
@@ -85,7 +85,6 @@
 #include "layer/ColourDatabase.h"
 #include "widgets/ModelDataTableDialog.h"
 #include "rdf/PluginRDFIndexer.h"
-#include "rdf/RDFExporter.h"
 
 #include "Surveyer.h"
 #include "NetworkPermissionTester.h"
@@ -332,11 +331,6 @@
     NetworkPermissionTester tester(withOSCSupport);
     bool networkPermission = tester.havePermission();
     if (networkPermission) {
-        if (withOSCSupport) {
-            SVDEBUG << "MainWindow: Creating OSC queue" << endl;
-            startOSCQueue();
-        }
-
         SVDEBUG << "MainWindow: Starting transform population thread" << endl;
         TransformFactory::getInstance()->startPopulationThread();
 
@@ -354,6 +348,16 @@
         m_versionTester = nullptr;
     }
 
+    if (withOSCSupport && networkPermission) {
+        SVDEBUG << "MainWindow: Creating OSC queue with network port"
+                << endl;
+        startOSCQueue(true);
+    } else {
+        SVDEBUG << "MainWindow: Creating internal-only OSC queue without port"
+                << endl;
+        startOSCQueue(false);
+    }
+    
 /*
     QTimer::singleShot(500, this, SLOT(betaReleaseWarning()));
 */
@@ -2997,81 +3001,14 @@
     if (!model) return;
 
     FileFinder::FileType type = FileFinder::LayerFileNoMidi;
-
     if (dynamic_cast<NoteModel *>(model)) type = FileFinder::LayerFile;
-
     QString path = getSaveFileName(type);
 
     if (path == "") return;
 
-    if (QFileInfo(path).suffix() == "") path += ".svl";
-
-    QString suffix = QFileInfo(path).suffix().toLower();
-
     QString error;
 
-    if (suffix == "xml" || suffix == "svl") {
-
-        QFile file(path);
-        if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
-            error = tr("Failed to open file %1 for writing").arg(path);
-        } else {
-            QTextStream out(&file);
-            out.setCodec(QTextCodec::codecForName("UTF-8"));
-            out << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
-                << "<!DOCTYPE sonic-visualiser>\n"
-                << "<sv>\n"
-                << "  <data>\n";
-
-            model->toXml(out, "    ");
-
-            out << "  </data>\n"
-                << "  <display>\n";
-
-            layer->toXml(out, "    ");
-
-            out << "  </display>\n"
-                << "</sv>\n";
-        }
-
-    } else if (suffix == "mid" || suffix == "midi") {
-
-        NoteModel *nm = dynamic_cast<NoteModel *>(model);
-
-        if (!nm) {
-            error = tr("Can't export non-note layers to MIDI");
-        } else {
-            MIDIFileWriter writer(path, nm, nm->getSampleRate());
-            writer.write();
-            if (!writer.isOK()) {
-                error = writer.getError();
-            }
-        }
-
-    } else if (suffix == "ttl" || suffix == "n3") {
-
-        if (!RDFExporter::canExportModel(model)) {
-            error = tr("Sorry, cannot export this layer type to RDF (supported types are: region, note, text, time instants, time values)");
-        } else {
-            RDFExporter exporter(path, model);
-            exporter.write();
-            if (!exporter.isOK()) {
-                error = exporter.getError();
-            }
-        }
-
-    } else {
-
-        CSVFileWriter writer(path, model,
-                             ((suffix == "csv") ? "," : "\t"));
-        writer.write();
-
-        if (!writer.isOK()) {
-            error = writer.getError();
-        }
-    }
-
-    if (error != "") {
+    if (!exportLayerTo(layer, path, error)) {
         QMessageBox::critical(this, tr("Failed to write file"), error);
     } else {
         m_recentFiles.addFile(path);
@@ -4653,11 +4590,8 @@
             SparseTimeValueModel *tvm =
                 dynamic_cast<SparseTimeValueModel *>(model);
             if (tvm) {
-                SparseTimeValueModel::Point point(frame,
-                                                  float(ev.getPitch() % 12),
-                                                  "");
-                SparseTimeValueModel::AddPointCommand *command =
-                    new SparseTimeValueModel::AddPointCommand
+                Event point(frame, float(ev.getPitch() % 12), "");
+                AddEventCommand *command = new AddEventCommand
                     (tvm, point, tr("Add Point"));
                 CommandHistory::getInstance()->addCommand(command);
             }
--- a/main/OSCHandler.cpp	Thu May 16 11:14:39 2019 +0100
+++ b/main/OSCHandler.cpp	Thu May 16 11:14:53 2019 +0100
@@ -40,8 +40,6 @@
     SVDEBUG << "OSCHandler: method = \""
             << message.getMethod() << "\"" << endl;
 
-    // This large function should really be abstracted out.
-
     if (message.getMethod() == "open") {
 
         if (message.getArgCount() == 1 &&
@@ -120,6 +118,30 @@
             }
         }
 
+    } else if (message.getMethod() == "exportlayer") {
+
+        QString path;
+        if (message.getArgCount() == 1 &&
+            message.getArg(0).canConvert(QVariant::String)) {
+            path = message.getArg(0).toString();
+            if (QFileInfo(path).exists()) {
+                SVDEBUG << "OSCHandler: Refusing to overwrite existing file in layer export" << endl;
+            } else {
+                Pane *currentPane = nullptr;
+                Layer *currentLayer = nullptr;
+                if (m_paneStack) currentPane = m_paneStack->getCurrentPane();
+                if (currentPane) currentLayer = currentPane->getSelectedLayer();
+                if (currentLayer) {
+                    QString error;
+                    if (!exportLayerTo(currentLayer, path, error)) {
+                        SVCERR << "OSCHandler: Failed to export current layer to " << path << ": " << error << endl;
+                    }
+                } else {
+                    SVCERR << "OSCHandler: No current layer to export" << endl;
+                }
+            }
+        }
+
     } else if (message.getMethod() == "jump" ||
                message.getMethod() == "play") {
 
--- a/main/main.cpp	Thu May 16 11:14:39 2019 +0100
+++ b/main/main.cpp	Thu May 16 11:14:53 2019 +0100
@@ -42,6 +42,7 @@
 #include <QTimer>
 #include <QPainter>
 #include <QFileOpenEvent>
+#include <QCommandLineParser>
 
 #include <iostream>
 #include <signal.h>
@@ -90,6 +91,8 @@
 
 \section model Data sources: the Model hierarchy
 
+***!!! todo: update this
+
    A Model is something containing, or knowing how to obtain, data.
 
    For example, WaveFileModel is a model that knows how to get data
@@ -238,12 +241,6 @@
 
     svSystemSpecificInitialisation();
 
-#ifdef Q_WS_X11
-#if QT_VERSION >= 0x040500
-//    QApplication::setGraphicsSystem("raster");
-#endif
-#endif
-
 #ifdef Q_OS_MAC
     if (QSysInfo::MacintoshVersion > QSysInfo::MV_10_8) {
         // Fix for OS/X 10.9 font problem
@@ -253,7 +250,58 @@
 
     SVApplication application(argc, argv);
 
+    QApplication::setOrganizationName("sonic-visualiser");
+    QApplication::setOrganizationDomain("sonicvisualiser.org");
+    QApplication::setApplicationName(QApplication::tr("Sonic Visualiser"));
+    QApplication::setApplicationVersion(SV_VERSION);
+
+    //!!! todo hand-update translations
+    QCommandLineParser parser;
+    parser.setApplicationDescription(QApplication::tr("\nSonic Visualiser is a program for viewing and exploring audio data\nfor semantic music analysis and annotation."));
+    parser.addHelpOption();
+    parser.addVersionOption();
+
+    parser.addOptions({
+            { "no-audio", QApplication::tr
+              ("Do not attempt to open an audio output device.") },
+            { "no-osc", QApplication::tr
+              ("Do not provide an Open Sound Control port for remote control.") },
+            { "no-splash", QApplication::tr
+              ("Do not show a splash screen.") },
+            { "osc-script", QApplication::tr
+              ("Batch run the Open Sound Control script found in the given file. Supply \"-\" as file to read from stdin. Scripts consist of /command arg1 arg2 ... OSC control lines, optionally interleaved with numbers to specify pauses in seconds."),
+              "osc.txt" }
+        });
+
+    parser.addPositionalArgument
+        ("[<file> ...]", QApplication::tr("One or more Sonic Visualiser (.sv) and audio files may be provided."));
+    
     QStringList args = application.arguments();
+    if (!parser.parse(args)) {
+        if (parser.unknownOptionNames().contains("?")) {
+            // QCommandLineParser only understands -? for help on Windows,
+            // but we historically accepted it everywhere - provide this
+            // backward compatibility
+            parser.showHelp();
+        }
+    }        
+        
+    parser.process(args);
+
+    bool audioOutput = !(parser.isSet("no-audio"));
+    bool oscSupport = !(parser.isSet("no-osc"));
+    bool showSplash = !(parser.isSet("no-splash"));
+
+    if (!audioOutput) {
+        SVDEBUG << "Note: --no-audio flag set, will not use audio device" << endl;
+    }
+    if (!oscSupport) {
+        SVDEBUG << "Note: --no-osc flag set, will not open OSC port" << endl;
+    }
+    
+    args = parser.positionalArguments();
+
+    QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
 
     signal(SIGINT,  signalHandler);
     signal(SIGTERM, signalHandler);
@@ -263,24 +311,6 @@
     signal(SIGQUIT, signalHandler);
 #endif
 
-    bool audioOutput = true;
-    bool oscSupport = true;
-
-    if (args.contains("--help") || args.contains("-h") || args.contains("-?")) {
-        cerr << QApplication::tr(
-            "\nSonic Visualiser is a program for viewing and exploring audio data\nfor semantic music analysis and annotation.\n\nUsage:\n\n  %1 [--no-audio] [--no-osc] [<file> ...]\n\n  --no-audio: Do not attempt to open an audio output device\n  --no-osc: Do not provide an Open Sound Control port for remote control\n  <file>: One or more Sonic Visualiser (.sv) and audio files may be provided.\n").arg(argv[0]) << endl;
-        exit(2);
-    }
-
-    if (args.contains("--no-audio")) audioOutput = false;
-    if (args.contains("--no-osc")) oscSupport = false;
-
-    QApplication::setOrganizationName("sonic-visualiser");
-    QApplication::setOrganizationDomain("sonicvisualiser.org");
-    QApplication::setApplicationName(QApplication::tr("Sonic Visualiser"));
-
-    QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
-
     SVSplash *splash = nullptr;
 
     QSettings settings;
@@ -293,7 +323,7 @@
     settings.endGroup();
 
     settings.beginGroup("Preferences");
-    if (settings.value("show-splash", true).toBool()) {
+    if (showSplash && settings.value("show-splash", true).toBool()) {
         splash = new SVSplash();
         splash->show();
         QTimer::singleShot(5000, splash, SLOT(hide()));
@@ -411,7 +441,11 @@
 
     for (QStringList::iterator i = args.begin(); i != args.end(); ++i) {
 
-        if (i == args.begin()) continue;
+        // Note QCommandLineParser has now pulled out argv[0] and all
+        // the options, so in theory everything here from the very
+        // first arg should be relevant. But let's reject names
+        // starting with "-" just in case.
+        
         if (i->startsWith('-')) continue;
 
         QString path = *i;
@@ -419,7 +453,8 @@
         application.handleFilepathArgument(path, splash);
     }
     
-    for (QStringList::iterator i = application.m_filepathQueue.begin(); i != application.m_filepathQueue.end(); ++i) {
+    for (QStringList::iterator i = application.m_filepathQueue.begin();
+         i != application.m_filepathQueue.end(); ++i) {
         QString path = *i;
         application.handleFilepathArgument(path, splash);
     }
@@ -439,6 +474,13 @@
     settings.endGroup();
 #endif
 
+    QString scriptFile = parser.value("osc-script");
+    if (scriptFile != "") {
+        SVDEBUG << "Note: Cueing OSC script from filename \"" << scriptFile
+                << "\"" << endl;
+        gui->cueOSCScript(scriptFile);
+    }
+    
     int rv = application.exec();
 
     gui->hide();
--- a/repoint-lock.json	Thu May 16 11:14:39 2019 +0100
+++ b/repoint-lock.json	Thu May 16 11:14:53 2019 +0100
@@ -4,13 +4,13 @@
       "pin": "b650289c47b4"
     },
     "svcore": {
-      "pin": "5d4831c2e8aa"
+      "pin": "1e7b7a62d373"
     },
     "svgui": {
-      "pin": "8d5bf4ab98ef"
+      "pin": "98157ea8a3d2"
     },
     "svapp": {
-      "pin": "e19c609a7bec"
+      "pin": "331be52cd473"
     },
     "checker": {
       "pin": "c8c17e51aab0"
--- a/repoint-project.json	Thu May 16 11:14:39 2019 +0100
+++ b/repoint-project.json	Thu May 16 11:14:53 2019 +0100
@@ -16,15 +16,18 @@
         },
         "svcore": {
             "vcs": "hg",
-            "service": "soundsoftware"
+            "service": "soundsoftware",
+            "branch": "single-point"
         },
         "svgui": {
             "vcs": "hg",
-            "service": "soundsoftware"
+            "service": "soundsoftware",
+            "branch": "single-point"
         },
         "svapp": {
             "vcs": "hg",
-	    "service": "soundsoftware"
+	    "service": "soundsoftware",
+            "branch": "single-point"
         },
         "checker": {
             "vcs": "hg",
--- a/test-svcore-base.pro	Thu May 16 11:14:39 2019 +0100
+++ b/test-svcore-base.pro	Thu May 16 11:14:53 2019 +0100
@@ -29,5 +29,6 @@
 for (file, TEST_HEADERS) { HEADERS += $$sprintf("svcore/base/test/%1", $$file) }
 
 !win32* {
+    POST_TARGETDEPS += $$PWD/libbase.a
     QMAKE_POST_LINK = ./$${TARGET}
 }
--- a/test-svcore-data-fileio.pro	Thu May 16 11:14:39 2019 +0100
+++ b/test-svcore-data-fileio.pro	Thu May 16 11:14:53 2019 +0100
@@ -29,5 +29,6 @@
 for (file, TEST_HEADERS) { HEADERS += $$sprintf("svcore/data/fileio/test/%1", $$file) }
 
 !win32* {
+    POST_TARGETDEPS += $$PWD/libbase.a
     QMAKE_POST_LINK = ./$${TARGET}
 }
--- a/test-svcore-data-model.pro	Thu May 16 11:14:39 2019 +0100
+++ b/test-svcore-data-model.pro	Thu May 16 11:14:53 2019 +0100
@@ -29,5 +29,6 @@
 for (file, TEST_HEADERS) { HEADERS += $$sprintf("svcore/data/model/test/%1", $$file) }
 
 !win32* {
+    POST_TARGETDEPS += $$PWD/libbase.a
     QMAKE_POST_LINK = ./$${TARGET}
 }
--- a/test-svcore-system.pro	Thu May 16 11:14:39 2019 +0100
+++ b/test-svcore-system.pro	Thu May 16 11:14:53 2019 +0100
@@ -29,5 +29,6 @@
 for (file, TEST_HEADERS) { HEADERS += $$sprintf("svcore/system/test/%1", $$file) }
 
 !win32* {
+    POST_TARGETDEPS += $$PWD/libbase.a
     QMAKE_POST_LINK = ./$${TARGET}
 }
Binary file test/all-3.2.1.sv has changed
Binary file test/all.sv has changed
Binary file test/s1.ogg has changed
Binary file test/s2.ogg has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/test-session-export.sh	Thu May 16 11:14:53 2019 +0100
@@ -0,0 +1,75 @@
+#!/bin/bash
+#
+# Test that loading and re-saving a session does not change its contents
+# Must be run from same directory as the SV binary
+
+set -e
+
+session="$1"
+
+set -u
+
+sv="../sonic-visualiser"
+if [ ! -x "$sv" ]; then
+    echo "This script must be run from the sonic-visualiser/test directory" 1>&2
+    exit 1
+fi
+
+if ! xmllint --version 2>/dev/null ; then
+    echo "Can't find required xmllint program (from libxml2 distribution)" 1>&2
+    exit 1
+fi
+
+version=$("$sv" -v 2>&1)
+adequate=no
+case "$version" in
+    [012].*) ;;
+    3.[012]) ;;
+    3.[012].*) ;;
+    [1-9]*) adequate=yes ;;
+    *) echo "Failed to query Sonic Visualiser version" 1>&2
+       exit 1 ;;
+esac
+if [ "$adequate" = "no" ]; then
+    echo "Sonic Visualiser version must be at least 3.3 (supporting --osc-script option)" 1>&2
+    exit 1
+fi
+
+if [ -z "$session" ]; then
+    echo "Usage: $0 <session.sv>" 1>&2
+    exit 2
+fi
+
+if [ ! -f "$session" ]; then
+    echo "Session file $session not found" 1>&2
+    exit 1
+fi
+
+tmpdir=$(mktemp -d)
+trap "rm -rf $tmpdir" 0
+
+input="$tmpdir/input.sv"
+inxml="$tmpdir/input.xml"
+output="$tmpdir/output.sv"
+outxml="$tmpdir/output.xml"
+
+cp "$session" "$input"
+
+cat > "$tmpdir/script" <<EOF
+/open "$input"
+/save "$output"
+/quit
+EOF
+
+"$sv" --no-splash --osc-script "$tmpdir/script"
+
+if [ ! -f "$output" ]; then
+    echo "ERROR: Failed to save session to $output at all!" 1>&2
+    exit 1
+fi
+
+bunzip2 -c "$input" | xmllint --format - > "$inxml"
+bunzip2 -c "$output" | xmllint --format - > "$outxml"
+
+diff -u "$inxml" "$outxml"
+
--- a/version.h	Thu May 16 11:14:39 2019 +0100
+++ b/version.h	Thu May 16 11:14:53 2019 +0100
@@ -1,1 +1,1 @@
-#define SV_VERSION "3.3-pre2"
+#define SV_VERSION "3.4"