To check out this repository please hg clone the following URL, or open the URL using EasyMercurial or your preferred Mercurial client.

Statistics Download as Zip
| Branch: | Tag: | Revision:

root / main / main.cpp

History | View | Annotate | Download (11.2 KB)

1
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
2

    
3
/*
4
    Sonic Lineup
5
    Comparative visualisation and alignment of related audio recordings
6
    Centre for Digital Music, Queen Mary, University of London.
7
    
8
    This program is free software; you can redistribute it and/or
9
    modify it under the terms of the GNU General Public License as
10
    published by the Free Software Foundation; either version 2 of the
11
    License, or (at your option) any later version.  See the file
12
    COPYING included with this distribution for more information.
13
*/
14

    
15
#include "MainWindow.h"
16

    
17
#include "system/System.h"
18
#include "system/Init.h"
19
#include "base/TempDirectory.h"
20
#include "base/PropertyContainer.h"
21
#include "base/Preferences.h"
22
#include "data/fileio/PlaylistFileReader.h"
23
#include "widgets/TipDialog.h"
24
#include "svcore/plugin/PluginScan.h"
25

    
26
#include <QMetaType>
27
#include <QApplication>
28
#include <QScreen>
29
#include <QMessageBox>
30
#include <QTranslator>
31
#include <QLocale>
32
#include <QSettings>
33
#include <QIcon>
34
#include <QSessionManager>
35
#include <QDir>
36

    
37
#include <iostream>
38
#include <signal.h>
39

    
40
#include "../version.h"
41

    
42
#include <vamp-hostsdk/PluginHostAdapter.h>
43

    
44
static QMutex cleanupMutex;
45
static bool cleanedUp = false;
46

    
47
static void
48
signalHandler(int /* signal */)
49
{
50
    // Avoid this happening more than once across threads
51

    
52
    cerr << "signalHandler: cleaning up and exiting" << endl;
53

    
54
    if (cleanupMutex.tryLock(5000)) {
55
        if (!cleanedUp) {
56
            TempDirectory::getInstance()->cleanup();
57
            cleanedUp = true;
58
        }
59
        cleanupMutex.unlock();
60
    }
61
    
62
    exit(0);
63
}
64

    
65
class VectApplication : public QApplication
66
{
67
public:
68
    VectApplication(int &argc, char **argv) :
69
        QApplication(argc, argv),
70
        m_mainWindow(0) { }
71
    virtual ~VectApplication() { }
72

    
73
    void setMainWindow(MainWindow *mw) { m_mainWindow = mw; }
74
    void releaseMainWindow() { m_mainWindow = 0; }
75

    
76
    virtual void commitData(QSessionManager &manager) {
77
        if (!m_mainWindow) return;
78
        bool mayAskUser = manager.allowsInteraction();
79
        bool success = m_mainWindow->commitData(mayAskUser);
80
        manager.release();
81
        if (!success) manager.cancel();
82
    }
83

    
84
protected:
85
    MainWindow *m_mainWindow;
86
};
87

    
88
static QString
89
getEnvQStr(QString variable)
90
{
91
#ifdef Q_OS_WIN32
92
    std::wstring wvar = variable.toStdWString();
93
    wchar_t *value = _wgetenv(wvar.c_str());
94
    if (!value) return QString();
95
    else return QString::fromStdWString(std::wstring(value));
96
#else
97
    std::string var = variable.toStdString();
98
    return QString::fromUtf8(qgetenv(var.c_str()));
99
#endif
100
}
101

    
102
static void
103
putEnvQStr(QString assignment)
104
{
105
#ifdef Q_OS_WIN32
106
    std::wstring wassignment = assignment.toStdWString();
107
    _wputenv(_wcsdup(wassignment.c_str()));
108
#else
109
    putenv(strdup(assignment.toUtf8().data()));
110
#endif
111
}
112

    
113
static void
114
setupMyVampPath()
115
{
116
    // This based on similar logic from the Tony application
117
    
118
    QString myVampPath = getEnvQStr("SONIC_LINEUP_VAMP_PATH");
119

    
120
#ifdef Q_OS_WIN32
121
    QChar sep(';');
122
#else
123
    QChar sep(':');
124
#endif
125
    
126
    if (myVampPath == "") {
127

    
128
        QString appName = QApplication::applicationName();
129
        QString myDir = QApplication::applicationDirPath();
130
        QString binaryName = QFileInfo(QCoreApplication::arguments().at(0))
131
            .fileName();
132

    
133
#ifdef Q_OS_WIN32
134
        QString programFiles = getEnvQStr("ProgramFiles");
135
        if (programFiles == "") programFiles = "C:\\Program Files";
136
        QString pfPath(programFiles + "\\" + appName);
137
        myVampPath = myDir + sep + pfPath;
138
#else
139
#ifdef Q_OS_MAC
140
        myVampPath = myDir + "/../Resources";
141
        (void)sep; // unused
142
#else
143
        if (binaryName != "") {
144
            myVampPath =
145
                myDir + "/../lib/" + binaryName + sep;
146
        }
147
        myVampPath = myVampPath +
148
            myDir + "/../lib/" + appName + sep +
149
            myDir;
150
#endif
151
#endif
152
    }
153

    
154
    SVCERR << "Setting VAMP_PATH to " << myVampPath
155
           << " for Sonic Lineup plugins" << endl;
156

    
157
    QString env = "VAMP_PATH=" + myVampPath;
158

    
159
    // Windows lacks setenv, must use putenv (different arg convention)
160
    putEnvQStr(env);
161
}
162

    
163
int
164
main(int argc, char **argv)
165
{
166
    if (argc == 2 && (QString(argv[1]) == "--version" ||
167
                      QString(argv[1]) == "-v")) {
168
        cerr << VECT_VERSION << endl;
169
        exit(0);
170
    }
171

    
172
    svSystemSpecificInitialisation();
173

    
174
    VectApplication application(argc, argv);
175

    
176
    QApplication::setOrganizationName("sonic-visualiser");
177
    QApplication::setOrganizationDomain("sonicvisualiser.org");
178
    QApplication::setApplicationName("Sonic Lineup");
179

    
180
    setupMyVampPath();
181

    
182
    QStringList args = application.arguments();
183

    
184
    signal(SIGINT,  signalHandler);
185
    signal(SIGTERM, signalHandler);
186

    
187
#ifndef Q_OS_WIN32
188
    signal(SIGHUP,  signalHandler);
189
    signal(SIGQUIT, signalHandler);
190
#endif
191

    
192
    svSystemSpecificInitialisation();
193

    
194
    bool audioOutput = true;
195

    
196
    if (args.contains("--help") || args.contains("-h") || args.contains("-?")) {
197
        std::cerr << QApplication::tr(
198
            "\nSonic Lineup is a comparative viewer for sets of related audio recordings.\n\nUsage:\n\n  %1 [--no-audio] [<file1>, <file2>...]\n\n  --no-audio: Do not attempt to open an audio output device\n  <file1>, <file2>...: Audio files; Sonic Lineup is designed for comparative\nviewing of multiple recordings of the same music or other related material.\n").arg(argv[0]).toStdString() << std::endl;
199
        exit(2);
200
    }
201

    
202
    if (args.contains("--no-audio")) {
203
        audioOutput = false;
204
    }
205

    
206
    if (args.contains("--first-run")) {
207
        QSettings settings;
208
        settings.clear();
209
    }
210

    
211
    InteractiveFileFinder::getInstance()->setApplicationSessionExtension("vect");
212

    
213
    QSettings settings;
214
    settings.beginGroup("Preferences");
215
    // Running plugins in-process allows us to use the serialise
216
    // option in the MATCH plugin, which cuts down on memory pressure
217
    // and makes things go more smoothly
218
    settings.setValue("run-vamp-plugins-in-process", true);
219
    settings.endGroup();
220

    
221
    QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
222
    QApplication::setAttribute(Qt::AA_DontShowIconsInMenus);
223

    
224
    QIcon icon;
225
    int sizes[] = { 16, 22, 24, 32, 48, 64, 128 };
226
    for (int i = 0; i < (int)(sizeof(sizes)/sizeof(sizes[0])); ++i) {
227
        icon.addFile(QString(":icons/sv-%1x%2.png").arg(sizes[i]).arg(sizes[i]));
228
    }
229
    QApplication::setWindowIcon(icon);
230

    
231
    QString language = QLocale::system().name();
232

    
233
    QTranslator qtTranslator;
234
    QString qtTrName = QString("qt_%1").arg(language);
235
    SVCERR << "Loading " << qtTrName.toStdString() << "..." << endl;
236
    bool success = false;
237
    if (!(success = qtTranslator.load(QLocale(), qtTrName))) {
238
        QString qtDir = getenv("QTDIR");
239
        if (qtDir != "") {
240
            success = qtTranslator.load
241
                (QLocale(), qtTrName, QDir(qtDir).filePath("translations"));
242
        }
243
    }
244
    if (!success) {
245
        SVCERR << "Failed to load Qt translation for locale" << endl;
246
    }
247
    application.installTranslator(&qtTranslator);
248

    
249
    QTranslator svecTranslator;
250
    QString svecTrName = QString("vect_%1").arg(language);
251
    SVCERR << "Loading " << svecTrName << "..." << endl;
252
    if (svecTranslator.load(svecTrName, ":i18n")) {
253
        application.installTranslator(&svecTranslator);
254
    } else {
255
        SVCERR << "Failed to load translation" << endl;
256
    }
257

    
258
    StoreStartupLocale();
259

    
260
    // Make known-plugins query as early as possible
261
    PluginScan::getInstance()->scan();
262
    
263
    // Permit size_t and PropertyName to be used as args in queued signal calls
264
    qRegisterMetaType<PropertyContainer::PropertyName>("PropertyContainer::PropertyName");
265

    
266
    MainWindow::AudioMode audioMode = 
267
        MainWindow::AUDIO_PLAYBACK_NOW_RECORD_LATER;
268

    
269
    if (!audioOutput) {
270
        audioMode = MainWindow::AUDIO_NONE;
271
    } 
272
    
273
    MainWindow *gui = new MainWindow(audioMode);
274
    application.setMainWindow(gui);
275

    
276
    QScreen *screen = QApplication::primaryScreen();
277
    QRect available = screen->availableGeometry();
278

    
279
    int width = available.width() * 2 / 3;
280
    int height = available.height() / 2;
281
    if (height < 450) height = available.height() * 2 / 3;
282
    if (width > height * 2) width = height * 2;
283

    
284
    settings.beginGroup("MainWindow");
285

    
286
    QSize size = settings.value("size", QSize(width, height)).toSize();
287
    gui->resizeConstrained(size);
288

    
289
    if (settings.contains("position")) {
290
        QRect prevrect(settings.value("position").toPoint(), size);
291
        if (!(available & prevrect).isEmpty()) {
292
            gui->move(prevrect.topLeft());
293
        }
294
    }
295

    
296
    if (settings.value("maximised", false).toBool()) {
297
        gui->setWindowState(Qt::WindowMaximized);
298
    }
299

    
300
    settings.endGroup();
301
    
302
    gui->show();
303

    
304
    SmallSession session;
305
    bool haveSession = false;
306

    
307
    QStringList filePaths;
308
    
309
    for (QStringList::iterator i = args.begin(); i != args.end(); ++i) {
310

    
311
        if (i == args.begin()) continue;
312
        if (i->startsWith('-')) continue;
313

    
314
        QString arg = *i;
315

    
316
        // If an arg is a playlist file, we can streamline things and
317
        // make sure we get the proper absolute paths by expanding it
318
        // here, rather than adding it to the session and waiting for
319
        // it to be expanded in the main application logic. (That
320
        // would work too, it's just not so clean a user experience.)
321

    
322
        if (PlaylistFileReader::isSupported(arg)) {
323
            PlaylistFileReader reader(arg);
324
            if (!reader.isOK()) {
325
                // But if we can't open the playlist file, add it to
326
                // the session as if it were just any old file and let
327
                // the main application worry about it later - we
328
                // don't want to be popping up dialogs before the app
329
                // has been exec'd
330
                filePaths.push_back(arg);
331
            } else {
332
                auto playlist = reader.load();
333
                for (auto entry: playlist) {
334
                    filePaths.push_back(entry);
335
                }
336
            }
337
        } else {
338
            filePaths.push_back(arg);
339
        }
340
    }
341

    
342
    for (auto filePath: filePaths) {
343

    
344
        // Add the argument to our session as a file path or URL to be
345
        // opened. We want to avoid relative file paths, but to do so
346
        // we must first check that they are not absolute URLs.
347
        
348
        QUrl url(filePath);
349
        if (url.isRelative()) {
350
            filePath = QFileInfo(filePath).absoluteFilePath();
351
        }
352
        
353
        if (session.mainFile == "") {
354
            session.mainFile = filePath;
355
        } else {
356
            session.additionalFiles.push_back(filePath);
357
        }
358

    
359
        haveSession = true;
360
    }
361

    
362
    if (haveSession) {
363
        gui->openSmallSession(session);
364
    } else if (!gui->reopenLastSession()) {
365
        QTimer::singleShot(400, gui, SLOT(introDialog()));
366
    } else {
367
        // Do this here only if not showing the intro dialog -
368
        // otherwise the introDialog function will do this after it
369
        // has shown the dialog, so we don't end up with both at once
370
        gui->checkForNewerVersion();
371
    }
372

    
373
    int rv = application.exec();
374

    
375
    cleanupMutex.lock();
376
    TempDirectory::getInstance()->cleanup();
377
    application.releaseMainWindow();
378

    
379
    delete gui;
380

    
381
    cleanupMutex.unlock();
382

    
383
    return rv;
384
}