# HG changeset patch # User Chris Cannam # Date 1558084665 -3600 # Node ID 920f09115ca823b687093eca4c6a75998092208f # Parent e05f876eba3d9c8a2372aa17256ee36fe98548fe# Parent 152fafb094f97b5e12a5762a413885295ea97f4e Merge from branch single-point diff -r e05f876eba3d -r 920f09115ca8 README.OSC --- a/README.OSC Thu May 16 12:55:29 2019 +0100 +++ b/README.OSC Fri May 17 10:17:45 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 + + 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 /jump end /jump selection diff -r e05f876eba3d -r 920f09115ca8 deploy/win32/build-32.bat --- a/deploy/win32/build-32.bat Thu May 16 12:55:29 2019 +0100 +++ b/deploy/win32/build-32.bat Fri May 17 10:17:45 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% diff -r e05f876eba3d -r 920f09115ca8 main/MainWindow.cpp --- a/main/MainWindow.cpp Thu May 16 12:55:29 2019 +0100 +++ b/main/MainWindow.cpp Fri May 17 10:17:45 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(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 << "\n" - << "\n" - << "\n" - << " \n"; - - model->toXml(out, " "); - - out << " \n" - << " \n"; - - layer->toXml(out, " "); - - out << " \n" - << "\n"; - } - - } else if (suffix == "mid" || suffix == "midi") { - - NoteModel *nm = dynamic_cast(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(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); } diff -r e05f876eba3d -r 920f09115ca8 main/OSCHandler.cpp --- a/main/OSCHandler.cpp Thu May 16 12:55:29 2019 +0100 +++ b/main/OSCHandler.cpp Fri May 17 10:17:45 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") { diff -r e05f876eba3d -r 920f09115ca8 main/main.cpp --- a/main/main.cpp Thu May 16 12:55:29 2019 +0100 +++ b/main/main.cpp Fri May 17 10:17:45 2019 +0100 @@ -42,6 +42,7 @@ #include #include #include +#include #include #include @@ -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 + ("[ ...]", 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] [ ...]\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 : 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(); diff -r e05f876eba3d -r 920f09115ca8 repoint-lock.json --- a/repoint-lock.json Thu May 16 12:55:29 2019 +0100 +++ b/repoint-lock.json Fri May 17 10:17:45 2019 +0100 @@ -1,16 +1,16 @@ { "libraries": { "vamp-plugin-sdk": { - "pin": "b650289c47b4" + "pin": "62987b6d6a3b" }, "svcore": { - "pin": "ab4fd193262b" + "pin": "978c143c767f" }, "svgui": { - "pin": "8d5bf4ab98ef" + "pin": "42c87368287c" }, "svapp": { - "pin": "e19c609a7bec" + "pin": "d62fd61082a1" }, "checker": { "pin": "c8c17e51aab0" diff -r e05f876eba3d -r 920f09115ca8 test-svcore-base.pro --- a/test-svcore-base.pro Thu May 16 12:55:29 2019 +0100 +++ b/test-svcore-base.pro Fri May 17 10:17:45 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} } diff -r e05f876eba3d -r 920f09115ca8 test-svcore-data-fileio.pro --- a/test-svcore-data-fileio.pro Thu May 16 12:55:29 2019 +0100 +++ b/test-svcore-data-fileio.pro Fri May 17 10:17:45 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} } diff -r e05f876eba3d -r 920f09115ca8 test-svcore-data-model.pro --- a/test-svcore-data-model.pro Thu May 16 12:55:29 2019 +0100 +++ b/test-svcore-data-model.pro Fri May 17 10:17:45 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} } diff -r e05f876eba3d -r 920f09115ca8 test-svcore-system.pro --- a/test-svcore-system.pro Thu May 16 12:55:29 2019 +0100 +++ b/test-svcore-system.pro Fri May 17 10:17:45 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} } diff -r e05f876eba3d -r 920f09115ca8 test/all-3.2.1.sv Binary file test/all-3.2.1.sv has changed diff -r e05f876eba3d -r 920f09115ca8 test/all.sv Binary file test/all.sv has changed diff -r e05f876eba3d -r 920f09115ca8 test/s1.ogg Binary file test/s1.ogg has changed diff -r e05f876eba3d -r 920f09115ca8 test/s2.ogg Binary file test/s2.ogg has changed diff -r e05f876eba3d -r 920f09115ca8 test/test-session-export.sh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/test-session-export.sh Fri May 17 10:17:45 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 " 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" <&2 + exit 1 +fi + +bunzip2 -c "$input" | xmllint --format - > "$inxml" +bunzip2 -c "$output" | xmllint --format - > "$outxml" + +diff -u "$inxml" "$outxml" + diff -r e05f876eba3d -r 920f09115ca8 version.h --- a/version.h Thu May 16 12:55:29 2019 +0100 +++ b/version.h Fri May 17 10:17:45 2019 +0100 @@ -1,1 +1,1 @@ -#define SV_VERSION "3.3-pre2" +#define SV_VERSION "3.4"