diff main/main.cpp @ 580:f52766aa747b

Rename src -> main for consistency with SV/Sonic Lineup
author Chris Cannam
date Wed, 14 Aug 2019 11:57:06 +0100
parents src/main.cpp@47f96711069f
children 2090dd9bfbc1
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/main.cpp	Wed Aug 14 11:57:06 2019 +0100
@@ -0,0 +1,406 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    Tony
+    An intonation analysis and annotation tool
+    Centre for Digital Music, Queen Mary, University of London.
+    This file copyright 2006-2012 Chris Cannam and QMUL.
+    
+    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 "MainWindow.h"
+
+#include "system/System.h"
+#include "system/Init.h"
+#include "base/TempDirectory.h"
+#include "base/PropertyContainer.h"
+#include "base/Preferences.h"
+#include "widgets/TipDialog.h"
+#include "widgets/InteractiveFileFinder.h"
+#include "transform/TransformFactory.h"
+#include "svcore/plugin/PluginScan.h"
+
+#include <QMetaType>
+#include <QApplication>
+#include <QScreen>
+#include <QMessageBox>
+#include <QTranslator>
+#include <QLocale>
+#include <QSettings>
+#include <QIcon>
+#include <QSessionManager>
+#include <QSplashScreen>
+#include <QFileOpenEvent>
+#include <QDir>
+
+#include <iostream>
+#include <signal.h>
+#include <cstdlib>
+
+#include <vamp-hostsdk/PluginHostAdapter.h>
+
+static QMutex cleanupMutex;
+static bool cleanedUp = false;
+
+static void
+signalHandler(int /* signal */)
+{
+    // Avoid this happening more than once across threads
+
+    cerr << "signalHandler: cleaning up and exiting" << endl;
+    cleanupMutex.lock();
+    if (!cleanedUp) {
+        TempDirectory::getInstance()->cleanup();
+        cleanedUp = true;
+    }
+    cleanupMutex.unlock();
+    exit(0);
+}
+
+class TonyApplication : public QApplication
+{
+public:
+    TonyApplication(int &argc, char **argv) :
+        QApplication(argc, argv),
+        m_mainWindow(0),
+        m_readyForFiles(false)
+    {
+        // tidier without, I reckon
+        setAttribute(Qt::AA_DontShowIconsInMenus);
+    }
+    virtual ~TonyApplication() {
+    }
+
+    void setMainWindow(MainWindow *mw) { m_mainWindow = mw; }
+    void releaseMainWindow() { m_mainWindow = 0; }
+
+    virtual void commitData(QSessionManager &manager) {
+        if (!m_mainWindow) return;
+        bool mayAskUser = manager.allowsInteraction();
+        bool success = m_mainWindow->commitData(mayAskUser);
+        manager.release();
+        if (!success) manager.cancel();
+    }
+
+    void readyForFiles() {
+        m_readyForFiles = true;
+    }
+
+    void handleFilepathArgument(QString path, QSplashScreen *splash);
+
+    void handleQueuedPaths(QSplashScreen *splash) {
+        foreach (QString f, m_filepathQueue) {
+            handleFilepathArgument(f, splash);
+        }
+    }
+    
+protected:
+    MainWindow *m_mainWindow;
+    
+    bool m_readyForFiles;
+    QStringList m_filepathQueue;
+
+    virtual bool event(QEvent *event) {
+
+        if (event->type() == QEvent::FileOpen) {
+            QString path = static_cast<QFileOpenEvent *>(event)->file();
+            if (m_readyForFiles) {
+                handleFilepathArgument(path, NULL);
+            } else {
+                m_filepathQueue.append(path);
+            }
+            return true;
+        } else {
+            return QApplication::event(event);
+        }
+    }
+};
+
+static QString
+getEnvQStr(QString variable)
+{
+#ifdef Q_OS_WIN32
+    std::wstring wvar = variable.toStdWString();
+    wchar_t *value = _wgetenv(wvar.c_str());
+    if (!value) return QString();
+    else return QString::fromStdWString(std::wstring(value));
+#else
+    std::string var = variable.toStdString();
+    return QString::fromUtf8(qgetenv(var.c_str()));
+#endif
+}
+
+static void
+putEnvQStr(QString assignment)
+{
+#ifdef Q_OS_WIN32
+    std::wstring wassignment = assignment.toStdWString();
+    _wputenv(_wcsdup(wassignment.c_str()));
+#else
+    putenv(strdup(assignment.toUtf8().data()));
+#endif
+}
+
+static void
+setupTonyVampPath()
+{
+    QString tonyVampPath = getEnvQStr("TONY_VAMP_PATH");
+
+#ifdef Q_OS_WIN32
+    QChar sep(';');
+#else
+    QChar sep(':');
+#endif
+    
+    if (tonyVampPath == "") {
+        tonyVampPath = QApplication::applicationDirPath();
+
+#ifdef Q_OS_WIN32
+        QString programFiles = getEnvQStr("ProgramFiles");
+        if (programFiles == "") programFiles = "C:\\Program Files";
+        QString defaultTonyPath(programFiles + "\\Tony");
+        tonyVampPath = tonyVampPath + sep + defaultTonyPath;
+#else
+#ifdef Q_OS_MAC
+        tonyVampPath = tonyVampPath + "/../Resources:" + tonyVampPath;
+#else
+        QString defaultTonyPath("/usr/local/lib/tony:/usr/lib/tony");
+        tonyVampPath = tonyVampPath + sep + defaultTonyPath;
+#endif
+#endif
+    }
+
+    std::vector<std::string> vampPathList = 
+        Vamp::PluginHostAdapter::getPluginPath();
+
+    for (auto p: vampPathList) {
+        tonyVampPath = tonyVampPath + sep + QString::fromUtf8(p.c_str());
+    }
+
+    SVCERR << "Setting VAMP_PATH to " << tonyVampPath
+           << " for Tony plugins" << endl;
+
+    QString env = "VAMP_PATH=" + tonyVampPath;
+
+    // Windows lacks setenv, must use putenv (different arg convention)
+    putEnvQStr(env);
+}
+        
+int
+main(int argc, char **argv)
+{
+    svSystemSpecificInitialisation();
+
+#ifdef Q_OS_MAC
+    if (QSysInfo::MacintoshVersion > QSysInfo::MV_10_8) {
+        // Fix for OS/X 10.9 font problem
+        QFont::insertSubstitution(".Lucida Grande UI", "Lucida Grande");
+    }
+#endif
+
+    TonyApplication application(argc, argv);
+
+    QApplication::setOrganizationName("sonic-visualiser");
+    QApplication::setOrganizationDomain("sonicvisualiser.org");
+    QApplication::setApplicationName("Tony");
+
+    setupTonyVampPath();
+
+    QStringList args = application.arguments();
+
+    signal(SIGINT,  signalHandler);
+    signal(SIGTERM, signalHandler);
+
+#ifndef Q_OS_WIN32
+    signal(SIGHUP,  signalHandler);
+    signal(SIGQUIT, signalHandler);
+#endif
+
+    bool audioOutput = true;
+    bool sonification = true;
+    bool spectrogram = true;
+
+    if (args.contains("--help") || args.contains("-h") || args.contains("-?")) {
+        std::cerr << QApplication::tr(
+            "\nTony is a program for interactive note and pitch analysis and annotation.\n\nUsage:\n\n  %1 [--no-audio] [--no-sonification] [--no-spectrogram] [<file> ...]\n\n  --no-audio: Do not attempt to open an audio output device\n  --no-sonification: Disable sonification of pitch tracks and notes and hide their toggles.\n  --no-spectrogram: Disable spectrogram.\n  <file>: One or more Tony (.ton) and audio files may be provided.").arg(argv[0]).toStdString() << std::endl;
+        exit(2);
+    }
+
+    if (args.contains("--no-audio")) audioOutput = false;
+
+    if (args.contains("--no-sonification")) sonification = false;
+
+    if (args.contains("--no-spectrogram")) spectrogram = false;
+
+    InteractiveFileFinder::getInstance()->setApplicationSessionExtension("ton");
+
+    QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
+
+    QSplashScreen *splash = 0;
+    // If we had a splash screen, we would show it here
+
+    QIcon icon;
+    int sizes[] = { 16, 22, 24, 32, 48, 64, 128 };
+    for (size_t i = 0; i < sizeof(sizes)/sizeof(sizes[0]); ++i) {
+        icon.addFile(QString(":icons/tony-%1x%2.png").arg(sizes[i]).arg(sizes[i]));
+    }
+    QApplication::setWindowIcon(icon);
+
+    QString language = QLocale::system().name();
+
+    QTranslator qtTranslator;
+    QString qtTrName = QString("qt_%1").arg(language);
+    std::cerr << "Loading " << qtTrName.toStdString() << "..." << std::endl;
+    bool success = false;
+    if (!(success = qtTranslator.load(qtTrName))) {
+        QString qtDir = getenv("QTDIR");
+        if (qtDir != "") {
+            success = qtTranslator.load
+                (qtTrName, QDir(qtDir).filePath("translations"));
+        }
+    }
+    if (!success) {
+        std::cerr << "Failed to load Qt translation for locale" << std::endl;
+    }
+    application.installTranslator(&qtTranslator);
+
+    StoreStartupLocale();
+    
+    // Permit size_t and PropertyName to be used as args in queued signal calls
+    qRegisterMetaType<size_t>("size_t");
+    qRegisterMetaType<PropertyContainer::PropertyName>("PropertyContainer::PropertyName");
+
+    MainWindow::SoundOptions options = MainWindow::WithEverything;
+    if (!audioOutput) options = 0;
+    
+    MainWindow *gui = new MainWindow(options, sonification, spectrogram);
+    application.setMainWindow(gui);
+    if (splash) {
+        QObject::connect(gui, SIGNAL(hideSplash()), splash, SLOT(hide()));
+    }
+
+    QScreen *screen = QApplication::primaryScreen();
+    QRect available = screen->availableGeometry();
+
+    int width = (available.width() * 2) / 3;
+    int height = available.height() / 2;
+    if (height < 450) height = (available.height() * 2) / 3;
+    if (width > height * 2) width = height * 2;
+
+    QSettings settings;
+    settings.beginGroup("MainWindow");
+    QSize size = settings.value("size", QSize(width, height)).toSize();
+    gui->resizeConstrained(size);
+    if (settings.contains("position")) {
+        QRect prevrect(settings.value("position").toPoint(), size);
+        if (!(available & prevrect).isEmpty()) {
+            gui->move(prevrect.topLeft());
+        }
+    }
+    settings.endGroup();
+    
+    gui->show();
+
+    application.readyForFiles();
+    
+    for (QStringList::iterator i = args.begin(); i != args.end(); ++i) {
+
+        if (i == args.begin()) continue;
+        if (i->startsWith('-')) continue;
+
+        QString path = *i;
+
+        application.handleFilepathArgument(path, splash);
+    }
+
+    application.handleQueuedPaths(splash);
+        
+    if (splash) splash->finish(gui);
+    delete splash;
+
+    int rv = application.exec();
+
+    gui->hide();
+
+    cleanupMutex.lock();
+
+    if (!cleanedUp) {
+        TransformFactory::deleteInstance();
+        TempDirectory::getInstance()->cleanup();
+        cleanedUp = true;
+    }
+
+    application.releaseMainWindow();
+
+    delete gui;
+
+    cleanupMutex.unlock();
+
+    return rv;
+}
+
+/** Application-global handler for filepaths passed in, e.g. as
+ * command-line arguments or apple events */
+
+void TonyApplication::handleFilepathArgument(QString path,
+                                             QSplashScreen *splash)
+{
+    static bool haveSession = false;
+    static bool haveMainModel = false;
+    static bool havePriorCommandLineModel = false;
+
+    if (!m_mainWindow) return;
+
+    MainWindow::FileOpenStatus status = MainWindow::FileOpenFailed;
+
+#ifdef Q_OS_WIN32
+    path.replace("\\", "/");
+#endif
+
+    if (path.endsWith("ton")) {
+        if (!haveSession) {
+            status = m_mainWindow->openSessionPath(path);
+            if (status == MainWindow::FileOpenSucceeded) {
+                haveSession = true;
+                haveMainModel = true;
+            }
+        } else {
+            std::cerr << "WARNING: Ignoring additional session file argument \"" << path << "\"" << std::endl;
+            status = MainWindow::FileOpenSucceeded;
+        }
+    }
+    if (status != MainWindow::FileOpenSucceeded) {
+        if (!haveMainModel) {
+            status = m_mainWindow->openPath(path, MainWindow::ReplaceSession);
+            if (status == MainWindow::FileOpenSucceeded) {
+                haveMainModel = true;
+            }
+        } else {
+            if (haveSession && !havePriorCommandLineModel) {
+                status = m_mainWindow->openPath(path, MainWindow::AskUser);
+                if (status == MainWindow::FileOpenSucceeded) {
+                    havePriorCommandLineModel = true;
+                }
+            } else {
+                status = m_mainWindow->openPath(path, MainWindow::CreateAdditionalModel);
+            }
+        }
+    }
+    if (status == MainWindow::FileOpenFailed) {
+        if (splash) splash->hide();
+        QMessageBox::critical
+            (m_mainWindow, QMessageBox::tr("Failed to open file"),
+             QMessageBox::tr("File or URL \"%1\" could not be opened").arg(path));
+    } else if (status == MainWindow::FileOpenWrongMode) {
+        if (splash) splash->hide();
+        QMessageBox::critical
+            (m_mainWindow, QMessageBox::tr("Failed to open file"),
+             QMessageBox::tr("<b>Audio required</b><p>Please load at least one audio file before importing annotation data"));
+    }
+}
+