To check out this repository please hg clone the following URL, or open the URL using EasyMercurial or your preferred Mercurial client.
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 |
} |