Entropy Piano Tuner  1.1.3 (documentation not yet complete)
An open-source experimental software for piano tuning by entropy minimization
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
tunerapplication.cpp
Go to the documentation of this file.
1 /*****************************************************************************
2  * Copyright 2015 Haye Hinrichsen, Christoph Wick
3  *
4  * This file is part of Entropy Piano Tuner.
5  *
6  * Entropy Piano Tuner is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by the
8  * Free Software Foundation, either version 3 of the License, or (at your
9  * option) any later version.
10  *
11  * Entropy Piano Tuner is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14  * more details.
15  *
16  * You should have received a copy of the GNU General Public License along with
17  * Entropy Piano Tuner. If not, see http://www.gnu.org/licenses/.
18  *****************************************************************************/
19 
20 #include "tunerapplication.h"
21 #include <iostream>
22 #include <QDebug>
23 #include <QThread>
24 #include <QMediaPlayer>
25 #include <QFile>
26 #include <QResource>
27 #include <QFileOpenEvent>
28 #include <QStandardPaths>
29 #include <QScreen>
30 #include <QMessageBox>
31 #include "../core/config.h"
32 #include "../core/messages/messagehandler.h"
33 #include "filemanagerforqt.h"
34 #include "platformtools.h"
35 #include "projectmanagerforqt.h"
36 #include "logforqt.h"
37 #include "settingsforqt.h"
38 #include "../core/system/eptexception.h"
39 #include "logviewer.h"
40 
42 
43 TunerApplication::TunerApplication(int & argc, char ** argv)
44  : QApplication(argc, argv),
45  mMessageHandlerTimerId(0),
46  mAudioRecorder(this),
47  mAudioPlayer(this) {
48 
49  EptAssert(!mSingleton, "Singleton class already created");
50  mSingleton = this;
51 
52  QIcon::setThemeSearchPaths(QIcon::themeSearchPaths() << ":/media" << ":/media/icons");
53 
54  if (primaryScreen()->devicePixelRatio() >= 1.5) {
55  setAttribute(Qt::AA_UseHighDpiPixmaps);
56  }
57 
58  new FileManagerForQt();
59 
60  // get last exit code
61  QSettings settings;
62  mLastExitCode = settings.value("application/lastExitCode", EXIT_SUCCESS).toInt();
63  // and set the last exit code to failure (this session has not ended)
64  settings.setValue("application/lastExitCode", EXIT_FAILURE);
65 
66  QObject::connect(this, SIGNAL(aboutToQuit()), this, SLOT(onAboutToQuit()));
67 }
68 
70 {
71  stop();
72  exit();
73  mCore.reset();
75 
76  mSingleton = nullptr;
77 }
78 
80  EptAssert(mSingleton, "Class has to be created");
81  return *mSingleton;
82 }
83 
85  return mSingleton;
86 }
87 
89  // store the error code for next session
90  QSettings settings;
91  settings.setValue("application/lastExitCode", errorcode);
92 }
93 
95  // check if there was a crash last session
96  if (mLastExitCode != EXIT_SUCCESS) {
97  if (QMessageBox::information(nullptr, tr("Crash handler"), tr("The application exited unexpectedly on the last run. Do you want to view the last log?"), QMessageBox::Yes | QMessageBox::No)
98  == QMessageBox::Yes) {
99  LogViewer v;
100  v.exec();
101  }
102  }
103 
104  // open the main window with the startup file
105  mMainWindow.reset(new MainWindow());
106 #ifdef Q_OS_MOBILE
107  // fix fullscreen size on mobile devices
108  mMainWindow->setFixedSize(primaryScreen()->size());
109 #endif
110 
111  auto log = new LogForQt();
112  // writeout args to log
113  LogI("Number of arguments: %d", arguments().size());
114  LogI("Program arguments: %s", arguments().join(", ").toStdString().c_str());
115 
116  // create core
117  mCore.reset(new Core(
118  new ProjectManagerForQt(mMainWindow.get()),
120  &mAudioPlayer,
121  log));
122 
124 
125 
126  EptAssert(mCore, "Core has to be created before entering init");
127 
128  // init the window
129  mMainWindow->init(mCore.get());
130 
131  // init platform components
133 
134  // then init the core
135  initCore();
136 
137 }
138 
140  stop();
141 
142  if (!mCore) {return;}
143  mCore->exit();
144 }
145 
148  startCore();
149 
150  mMainWindow->start();
151 
152  QObject::connect(this, SIGNAL(applicationStateChanged(Qt::ApplicationState)),
153  this, SLOT(onApplicationStateChanged(Qt::ApplicationState)));
154 
155 
156  // if the user opened this program by double clicking a file, we can set this file
157  // as startup file, that is loaded when the program has started.
158  // if startupFile is empty, we open the empty default file
159  if (mStartupFile.size() > 0) {
160  openFile(mStartupFile, false);
161  mStartupFile.clear();
162  } else {
164  }
165 }
166 
168  if (!mCore) {return;}
169  mCore->stop();
170 }
171 
173  // play a startup sound
174 #ifdef __linux__
175  QString fileName="startup_sound.ogg";
176 #else
177  QString fileName="startup_sound.mp3";
178 #endif
179 
180  // first copy startup sound from resource location to disk
181  // it cannot be played out a resource file
182  QFile audioFile(QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + "/" + fileName);
183  if (!audioFile.exists()) {
184  // file does not exists yet, copy it
185  QFile::copy(":/media/audio/" + fileName, audioFile.fileName());
186  } else {
187  if (audioFile.size() != QResource(":/media/audio/" + fileName).size()) {
188  // size changed, this usually means a new size, remove and copy
189  audioFile.remove();
190  QFile::copy(":/media/audio/" + fileName, audioFile.fileName());
191  }
192  }
193 
194  // file should exist now
195  EptAssert(audioFile.exists(), "Audio file should exist now");
196 
197  // play the actual sound
198  QMediaPlayer *player = new QMediaPlayer(this);
199  player->setMedia(QUrl::fromLocalFile(audioFile.fileName()));
200  player->setVolume(50);
201  player->play();
202  if (player->error() != QMediaPlayer::NoError) {
203  LogW("Error in QMediaPlayer: %s", player->errorString().toStdString().c_str());
204  }
205 }
206 
207 bool TunerApplication::openFile(QString filePath, bool cached) {
208  if (!mCore || !mCore->getProjectManager() || !mCore->isInitialized()) {
209  // not initiated, save file for later use
210  LogI("Storing startup file: %s", filePath.toStdString().c_str());
211  mStartupFile = filePath;
212  return true;
213  }
214  return mCore->getProjectManager()->openFile(filePath.toStdString(), cached) == ProjectManagerAdapter::R_ACCEPTED;
215 }
216 
217 bool TunerApplication::event(QEvent *e) {
218  switch (e->type()) {
219  case QEvent::FileOpen:
220  // note: this only supported in Mac OS X so far!
221  return openFile(static_cast<QFileOpenEvent *>(e)->file(), false);
222  default:
223  return QApplication::event(e);
224  }
225 }
226 
227 void TunerApplication::timerEvent(QTimerEvent *event) {
229  (void)event; // event not used, suppress warning
230 }
231 
232 bool TunerApplication::notify(QObject* receiver, QEvent* event) {
233  try {
234  return QApplication::notify(receiver, event);
235  }
236  catch (const EptException &e) {
237  qCritical() << "Unhandled exception: ";
238  qCritical() << QString::fromStdString(e.getFullDescription());
239 
240  }
241  catch (const std::exception &e) {
242  qCritical() << "Unhandled exception: ";
243  qCritical() << QString::fromStdString(e.what());
244  }
245  catch (...) {
246  qCritical() << "Unhandled exception: ";
247  qCritical() << "unknown exception";
248  }
249  return true;
250 }
251 
253  if (mCore && !mCore->isInitialized()) {
254  // disable main window during init
255  mMainWindow->setEnabled(false);
256 
257  mCore->init(new QtCoreInitialisation(mMainWindow.get()));
258 
259  // enable the main dialog again
260  mMainWindow->setEnabled(true);
261 
262  // recativate window after closing the dialog
263  mMainWindow->activateWindow();
264  }
265 }
266 
268  if (mCore && mCore->isInitialized()) {
269  mCore->exit();
270  }
271 
272 }
273 
275  if (mCore) {
276  mCore->start();
277  }
278 
279 
280  // custom message loop
281  mMessageHandlerTimerId = startTimer(10);
282 }
283 
285  if (mCore) {
286  mCore->stop();
287  }
288 
289  // kill the custom timer
291  killTimer(mMessageHandlerTimerId);
293  }
294 }
295 
296 void TunerApplication::onApplicationStateChanged(Qt::ApplicationState state) {
297  if (state & Qt::ApplicationSuspended) {
298  // called if application is 'shut down'
299  LogI("Application suspended: exiting core");
300  setApplicationExitState(EXIT_SUCCESS);
301  stopCore();
302  exitCore();
303  } else if (state & Qt::ApplicationActive) {
304  // init and start core components
305  LogI("Application gone active: starting core");
306  setApplicationExitState(EXIT_FAILURE);
307  initCore();
308  startCore();
309  } else if (state & Qt::ApplicationHidden ) {
310  // delete core components
311  LogI("Application gone hidden: exiting core");
312  exitCore();
313  } else if (state & Qt::ApplicationInactive) {
314  // stop the core on mobile platforms
315 #if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) || defined(Q_OS_WINPHONE)
316  LogI("Application gone inactive: stopping core");
317  stopCore();
318  setApplicationExitState(EXIT_SUCCESS);
319 #endif
320  }
321 }
322 
324  setApplicationExitState(EXIT_SUCCESS);
325 }
bool notify(QObject *receiver, QEvent *event)
Reimplemented to catch exceptions.
static TunerApplication * mSingleton
The one and only instance.
int mMessageHandlerTimerId
Id of the timer that progresses the MessageHandler.
bool openFile(QString filePath, bool cached)
Open the given file.
QString mStartupFile
Absolute path to the startup file or an empty string.
static MessageHandler & getSingleton()
get a reference to the singleton class
virtual void init()
Initialise native components (e.g. midi on android/iOS)
Definition: platformtools.h:41
static PlatformTools * getSingleton()
Definition: platformtools.h:38
AudioRecorderForQt mAudioRecorder
Instance of the Qt audio recorder.
Implementation of the ProjectManagerAdapter in Qt.
#define LogW(...)
Definition: log.h:56
TunerApplication(int &argc, char **argv)
Constructor for the application.
std::shared_ptr< MainWindow > mMainWindow
Shared pointer of the MainWindow.
The main application singleton class.
void start()
Function to start the MainWindow and the Core.
static TunerApplication & getSingleton()
Getter for the singleton instance.
static TunerApplication * getSingletonPtr()
Getter for the singleton instance.
void playStartupSound()
Function to play the startup sound.
AudioPlayerForQt mAudioPlayer
Instance of the Qt audio player.
virtual bool loadStartupFile(const QStringList args)
Load a startup file from the given arguments.
void exit()
Function called upon exitting the application.
std::shared_ptr< Core > mCore
Shared pointer of the Core.
void stop()
Function to stop the MainWindow and the Core.
int mLastExitCode
last exit code to detect if the application crashed
#define LogI(...)
Definition: log.h:50
void stopCore()
Stop the core.
void initCore()
Initialising of the core.
void process()
Main task, processing the events in the queue.
Implementation of the core initializ.
void init()
Function to initialise the application.
CORE : Class managing the core.
Definition: core.h:45
Implementation of the FileManager for Qt.
void timerEvent(QTimerEvent *event)
Called when the internal timer was shot.
void startCore()
Start the core.
Implementation for the log in Qt.
Definition: logforqt.h:31
#define EptAssert(a, b)
Definition: eptexception.h:47
virtual ~TunerApplication()
Destructor of the application.
bool event(QEvent *e)
Handling of general events.
virtual void enableScreensaver()
Function to enable the screen saver.
Definition: platformtools.h:70
virtual void disableScreensaver()
Function to disable the screen saver.
Definition: platformtools.h:63
The main window.
Definition: mainwindow.h:49
void exitCore()
Exit from the core.
static void setApplicationExitState(int errorcode=EXIT_SUCCESS)
This is sets the exit code if the app would be terminated now.
void onApplicationStateChanged(Qt::ApplicationState state)
Depending on the application state the core will be started or stopped.
virtual const std::string & getFullDescription(void) const
Returns a string with the full description of this error.