mirror of
https://github.com/LMMS/lmms.git
synced 2026-03-14 03:59:35 -04:00
# Conflicts: # .travis/osx..install.sh # CMakeLists.txt # cmake/apple/install_apple.sh.in # doc/lmms.1 # include/VstSyncController.h # plugins/carlabase/carla.h # plugins/vestige/vestige.cpp # plugins/vst_base/CMakeLists.txt # plugins/vst_base/RemoteVstPlugin.cpp # plugins/vst_base/Win64/CMakeLists.txt # plugins/zynaddsubfx/zynaddsubfx # plugins/zynaddsubfx/zynaddsubfx/src/Misc/QtXmlWrapper.cpp # src/core/Song.cpp # src/core/main.cpp
935 lines
22 KiB
C++
935 lines
22 KiB
C++
/*
|
|
* main.cpp - just main.cpp which is starting up app...
|
|
*
|
|
* Copyright (c) 2004-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
|
* Copyright (c) 2012-2013 Paul Giblock <p/at/pgiblock.net>
|
|
*
|
|
* This file is part of LMMS - https://lmms.io
|
|
*
|
|
* 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.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public
|
|
* License along with this program (see COPYING); if not, write to the
|
|
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
* Boston, MA 02110-1301 USA.
|
|
*
|
|
*/
|
|
|
|
#include "lmmsconfig.h"
|
|
#include "lmmsversion.h"
|
|
#include "versioninfo.h"
|
|
|
|
#include "denormals.h"
|
|
|
|
#include <QDebug>
|
|
#include <QFileInfo>
|
|
#include <QLocale>
|
|
#include <QTimer>
|
|
#include <QTranslator>
|
|
#include <QApplication>
|
|
#include <QMessageBox>
|
|
#include <QPushButton>
|
|
#include <QTextStream>
|
|
|
|
#ifdef LMMS_BUILD_WIN32
|
|
#include <windows.h>
|
|
#endif
|
|
|
|
#ifdef LMMS_HAVE_SCHED_H
|
|
#include "sched.h"
|
|
#endif
|
|
|
|
#ifdef LMMS_HAVE_PROCESS_H
|
|
#include <process.h>
|
|
#endif
|
|
|
|
#ifdef LMMS_HAVE_UNISTD_H
|
|
#include <unistd.h>
|
|
#endif
|
|
|
|
#include <signal.h>
|
|
|
|
#include "MainApplication.h"
|
|
#include "ConfigManager.h"
|
|
#include "NotePlayHandle.h"
|
|
#include "embed.h"
|
|
#include "Engine.h"
|
|
#include "GuiApplication.h"
|
|
#include "ImportFilter.h"
|
|
#include "MainWindow.h"
|
|
#include "OutputSettings.h"
|
|
#include "ProjectRenderer.h"
|
|
#include "RenderManager.h"
|
|
#include "Song.h"
|
|
#include "SetupDialog.h"
|
|
|
|
#ifdef LMMS_DEBUG_FPE
|
|
#include <fenv.h> // For feenableexcept
|
|
#include <execinfo.h> // For backtrace and backtrace_symbols_fd
|
|
#include <unistd.h> // For STDERR_FILENO
|
|
#include <csignal> // To register the signal handler
|
|
#endif
|
|
|
|
|
|
#ifdef LMMS_DEBUG_FPE
|
|
void signalHandler( int signum ) {
|
|
|
|
// Get a back trace
|
|
void *array[10];
|
|
size_t size;
|
|
|
|
// get void*'s for all entries on the stack
|
|
size = backtrace(array, 10);
|
|
|
|
backtrace_symbols_fd(array, size, STDERR_FILENO);
|
|
|
|
// cleanup and close up stuff here
|
|
// terminate program
|
|
|
|
exit(signum);
|
|
}
|
|
#endif
|
|
|
|
static inline QString baseName( const QString & file )
|
|
{
|
|
return QFileInfo( file ).absolutePath() + "/" +
|
|
QFileInfo( file ).completeBaseName();
|
|
}
|
|
|
|
|
|
|
|
|
|
inline void loadTranslation( const QString & tname,
|
|
const QString & dir = ConfigManager::inst()->localeDir() )
|
|
{
|
|
QTranslator * t = new QTranslator( QCoreApplication::instance() );
|
|
QString name = tname + ".qm";
|
|
|
|
t->load( name, dir );
|
|
|
|
QCoreApplication::instance()->installTranslator( t );
|
|
}
|
|
|
|
|
|
|
|
|
|
void printVersion( char *executableName )
|
|
{
|
|
printf( "LMMS %s\n(%s %s, Qt %s, %s)\n\n"
|
|
"Copyright (c) %s\n\n"
|
|
"This program is free software; you can redistribute it and/or\n"
|
|
"modify it under the terms of the GNU General Public\n"
|
|
"License as published by the Free Software Foundation; either\n"
|
|
"version 2 of the License, or (at your option) any later version.\n\n"
|
|
"Try \"%s --help\" for more information.\n\n", LMMS_VERSION,
|
|
PLATFORM, MACHINE, QT_VERSION_STR, COMPILER_VERSION,
|
|
LMMS_PROJECT_COPYRIGHT, executableName );
|
|
}
|
|
|
|
|
|
|
|
|
|
void printHelp()
|
|
{
|
|
printf( "LMMS %s\n"
|
|
"Copyright (c) %s\n\n"
|
|
"Usage: lmms [global options...] [<action> [action parameters...]]\n\n"
|
|
"Actions:\n"
|
|
" <no action> [options...] [<project>] Start LMMS in normal GUI mode\n"
|
|
" dump <in> Dump XML of compressed file <in>\n"
|
|
" render <project> [options...] Render given project file\n"
|
|
" rendertracks <project> [options...] Render each track to a different file\n"
|
|
" upgrade <in> [out] Upgrade file <in> and save as <out>\n"
|
|
" Standard out is used if no output file\n"
|
|
" is specifed\n"
|
|
"\nGlobal options:\n"
|
|
" --allowroot Bypass root user startup check (use with\n"
|
|
" caution).\n"
|
|
" -c, --config <configfile> Get the configuration from <configfile>\n"
|
|
" -h, --help Show this usage information and exit.\n"
|
|
" -v, --version Show version information and exit.\n"
|
|
"\nOptions if no action is given:\n"
|
|
" --geometry <geometry> Specify the size and position of\n"
|
|
" the main window\n"
|
|
" geometry is <xsizexysize+xoffset+yoffsety>.\n"
|
|
" --import <in> [-e] Import MIDI or Hydrogen file <in>.\n"
|
|
" If -e is specified lmms exits after importing the file.\n"
|
|
"\nOptions for \"render\" and \"rendertracks\":\n"
|
|
" -a, --float Use 32bit float bit depth\n"
|
|
" -b, --bitrate <bitrate> Specify output bitrate in KBit/s\n"
|
|
" Default: 160.\n"
|
|
" -f, --format <format> Specify format of render-output where\n"
|
|
" Format is either 'wav', 'flac', 'ogg' or 'mp3'.\n"
|
|
" -i, --interpolation <method> Specify interpolation method\n"
|
|
" Possible values:\n"
|
|
" - linear\n"
|
|
" - sincfastest (default)\n"
|
|
" - sincmedium\n"
|
|
" - sincbest\n"
|
|
" -l, --loop Render as a loop\n"
|
|
" -m, --mode Stereo mode used for MP3 export\n"
|
|
" Possible values: s, j, m\n"
|
|
" s: Stereo\n"
|
|
" j: Joint Stereo\n"
|
|
" m: Mono\n"
|
|
" Default: j\n"
|
|
" -o, --output <path> Render into <path>\n"
|
|
" For \"render\", provide a file path\n"
|
|
" For \"rendertracks\", provide a directory path\n"
|
|
" If not specified, render will overwrite the input file\n"
|
|
" For \"rendertracks\", this might be required\n"
|
|
" -p, --profile <out> Dump profiling information to file <out>\n"
|
|
" -s, --samplerate <samplerate> Specify output samplerate in Hz\n"
|
|
" Range: 44100 (default) to 192000\n"
|
|
" -x, --oversampling <value> Specify oversampling\n"
|
|
" Possible values: 1, 2, 4, 8\n"
|
|
" Default: 2\n\n",
|
|
LMMS_VERSION, LMMS_PROJECT_COPYRIGHT );
|
|
}
|
|
|
|
|
|
|
|
|
|
void fileCheck( QString &file )
|
|
{
|
|
QFileInfo fileToCheck( file );
|
|
|
|
if( !fileToCheck.size() )
|
|
{
|
|
printf( "The file %s does not have any content.\n",
|
|
file.toUtf8().constData() );
|
|
exit( EXIT_FAILURE );
|
|
}
|
|
else if( fileToCheck.isDir() )
|
|
{
|
|
printf( "%s is a directory.\n",
|
|
file.toUtf8().constData() );
|
|
exit( EXIT_FAILURE );
|
|
}
|
|
}
|
|
|
|
int usageError(const QString& message)
|
|
{
|
|
qCritical().noquote() << QString("\n%1.\n\nTry \"%2 --help\" for more information.\n\n")
|
|
.arg( message ).arg( qApp->arguments()[0] );
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
int noInputFileError()
|
|
{
|
|
return usageError( "No input file specified" );
|
|
}
|
|
|
|
|
|
int main( int argc, char * * argv )
|
|
{
|
|
#ifdef LMMS_DEBUG_FPE
|
|
// Enable exceptions for certain floating point results
|
|
feenableexcept( FE_INVALID |
|
|
FE_DIVBYZERO |
|
|
FE_OVERFLOW |
|
|
FE_UNDERFLOW);
|
|
|
|
// Install the trap handler
|
|
// register signal SIGFPE and signal handler
|
|
signal(SIGFPE, signalHandler);
|
|
#endif
|
|
|
|
// initialize memory managers
|
|
NotePlayHandleManager::init();
|
|
|
|
// intialize RNG
|
|
srand( getpid() + time( 0 ) );
|
|
|
|
disable_denormals();
|
|
|
|
bool coreOnly = false;
|
|
bool fullscreen = true;
|
|
bool exitAfterImport = false;
|
|
bool allowRoot = false;
|
|
bool renderLoop = false;
|
|
bool renderTracks = false;
|
|
QString fileToLoad, fileToImport, renderOut, profilerOutputFile, configFile;
|
|
|
|
// first of two command-line parsing stages
|
|
for( int i = 1; i < argc; ++i )
|
|
{
|
|
QString arg = argv[i];
|
|
|
|
if( arg == "--help" || arg == "-h" ||
|
|
arg == "--version" || arg == "-v" ||
|
|
arg == "render" || arg == "--render" || arg == "-r" )
|
|
{
|
|
coreOnly = true;
|
|
}
|
|
else if( arg == "rendertracks" || arg == "--rendertracks" )
|
|
{
|
|
coreOnly = true;
|
|
renderTracks = true;
|
|
}
|
|
else if( arg == "--allowroot" )
|
|
{
|
|
allowRoot = true;
|
|
}
|
|
else if( arg == "--geometry" || arg == "-geometry")
|
|
{
|
|
if( arg == "--geometry" )
|
|
{
|
|
// Delete the first "-" so Qt recognize the option
|
|
strcpy(argv[i], "-geometry");
|
|
}
|
|
// option -geometry is filtered by Qt later,
|
|
// so we need to check its presence now to
|
|
// determine, if the application should run in
|
|
// fullscreen mode (default, no -geometry given).
|
|
fullscreen = false;
|
|
}
|
|
}
|
|
|
|
#if !defined(LMMS_BUILD_WIN32) && !defined(LMMS_BUILD_HAIKU)
|
|
if ( ( getuid() == 0 || geteuid() == 0 ) && !allowRoot )
|
|
{
|
|
printf( "LMMS cannot be run as root.\nUse \"--allowroot\" to override.\n\n" );
|
|
return EXIT_FAILURE;
|
|
}
|
|
#endif
|
|
|
|
QCoreApplication * app = coreOnly ?
|
|
new QCoreApplication( argc, argv ) :
|
|
new MainApplication( argc, argv );
|
|
|
|
Mixer::qualitySettings qs( Mixer::qualitySettings::Mode_HighQuality );
|
|
OutputSettings os( 44100, OutputSettings::BitRateSettings(160, false), OutputSettings::Depth_16Bit, OutputSettings::StereoMode_JointStereo );
|
|
ProjectRenderer::ExportFileFormats eff = ProjectRenderer::WaveFile;
|
|
|
|
// second of two command-line parsing stages
|
|
for( int i = 1; i < argc; ++i )
|
|
{
|
|
QString arg = argv[i];
|
|
|
|
if( arg == "--version" || arg == "-v" )
|
|
{
|
|
printVersion( argv[0] );
|
|
return EXIT_SUCCESS;
|
|
}
|
|
else if( arg == "--help" || arg == "-h" )
|
|
{
|
|
printHelp();
|
|
return EXIT_SUCCESS;
|
|
}
|
|
else if( arg == "upgrade" || arg == "--upgrade" || arg == "-u")
|
|
{
|
|
++i;
|
|
|
|
if( i == argc )
|
|
{
|
|
return noInputFileError();
|
|
}
|
|
|
|
|
|
DataFile dataFile( QString::fromLocal8Bit( argv[i] ) );
|
|
|
|
if( argc > i+1 ) // output file specified
|
|
{
|
|
dataFile.writeFile( QString::fromLocal8Bit( argv[i+1] ) );
|
|
}
|
|
else // no output file specified; use stdout
|
|
{
|
|
QTextStream ts( stdout );
|
|
dataFile.write( ts );
|
|
fflush( stdout );
|
|
}
|
|
|
|
return EXIT_SUCCESS;
|
|
}
|
|
else if( arg == "--allowroot" )
|
|
{
|
|
// Ignore, processed earlier
|
|
#ifdef LMMS_BUILD_WIN32
|
|
if( allowRoot )
|
|
{
|
|
printf( "\nOption \"--allowroot\" will be ignored on this platform.\n\n" );
|
|
}
|
|
#endif
|
|
|
|
}
|
|
else if( arg == "dump" || arg == "--dump" || arg == "-d" )
|
|
{
|
|
++i;
|
|
|
|
if( i == argc )
|
|
{
|
|
return noInputFileError();
|
|
}
|
|
|
|
|
|
QFile f( QString::fromLocal8Bit( argv[i] ) );
|
|
f.open( QIODevice::ReadOnly );
|
|
QString d = qUncompress( f.readAll() );
|
|
printf( "%s\n", d.toUtf8().constData() );
|
|
|
|
return EXIT_SUCCESS;
|
|
}
|
|
else if( arg == "render" || arg == "--render" || arg == "-r" ||
|
|
arg == "rendertracks" || arg == "--rendertracks" )
|
|
{
|
|
++i;
|
|
|
|
if( i == argc )
|
|
{
|
|
return noInputFileError();
|
|
}
|
|
|
|
|
|
fileToLoad = QString::fromLocal8Bit( argv[i] );
|
|
renderOut = fileToLoad;
|
|
}
|
|
else if( arg == "--loop" || arg == "-l" )
|
|
{
|
|
renderLoop = true;
|
|
}
|
|
else if( arg == "--output" || arg == "-o" )
|
|
{
|
|
++i;
|
|
|
|
if( i == argc )
|
|
{
|
|
return usageError( "No output file specified" );
|
|
}
|
|
|
|
|
|
renderOut = QString::fromLocal8Bit( argv[i] );
|
|
}
|
|
else if( arg == "--format" || arg == "-f" )
|
|
{
|
|
++i;
|
|
|
|
if( i == argc )
|
|
{
|
|
return usageError( "No output format specified" );
|
|
}
|
|
|
|
|
|
const QString ext = QString( argv[i] );
|
|
|
|
if( ext == "wav" )
|
|
{
|
|
eff = ProjectRenderer::WaveFile;
|
|
}
|
|
#ifdef LMMS_HAVE_OGGVORBIS
|
|
else if( ext == "ogg" )
|
|
{
|
|
eff = ProjectRenderer::OggFile;
|
|
}
|
|
#endif
|
|
#ifdef LMMS_HAVE_MP3LAME
|
|
else if( ext == "mp3" )
|
|
{
|
|
eff = ProjectRenderer::MP3File;
|
|
}
|
|
#endif
|
|
else if (ext == "flac")
|
|
{
|
|
eff = ProjectRenderer::FlacFile;
|
|
}
|
|
else
|
|
{
|
|
return usageError( QString( "Invalid output format %1" ).arg( argv[i] ) );
|
|
}
|
|
}
|
|
else if( arg == "--samplerate" || arg == "-s" )
|
|
{
|
|
++i;
|
|
|
|
if( i == argc )
|
|
{
|
|
return usageError( "No samplerate specified" );
|
|
}
|
|
|
|
|
|
sample_rate_t sr = QString( argv[i] ).toUInt();
|
|
if( sr >= 44100 && sr <= 192000 )
|
|
{
|
|
os.setSampleRate(sr);
|
|
}
|
|
else
|
|
{
|
|
return usageError( QString( "Invalid samplerate %1" ).arg( argv[i] ) );
|
|
}
|
|
}
|
|
else if( arg == "--bitrate" || arg == "-b" )
|
|
{
|
|
++i;
|
|
|
|
if( i == argc )
|
|
{
|
|
return usageError( "No bitrate specified" );
|
|
}
|
|
|
|
|
|
int br = QString( argv[i] ).toUInt();
|
|
|
|
if( br >= 64 && br <= 384 )
|
|
{
|
|
OutputSettings::BitRateSettings bitRateSettings = os.getBitRateSettings();
|
|
bitRateSettings.setBitRate(br);
|
|
os.setBitRateSettings(bitRateSettings);
|
|
}
|
|
else
|
|
{
|
|
return usageError( QString( "Invalid bitrate %1" ).arg( argv[i] ) );
|
|
}
|
|
}
|
|
else if( arg == "--mode" || arg == "-m" )
|
|
{
|
|
++i;
|
|
|
|
if( i == argc )
|
|
{
|
|
return usageError( "No stereo mode specified" );
|
|
}
|
|
|
|
QString const mode( argv[i] );
|
|
|
|
if( mode == "s" )
|
|
{
|
|
os.setStereoMode(OutputSettings::StereoMode_Stereo);
|
|
}
|
|
else if( mode == "j" )
|
|
{
|
|
os.setStereoMode(OutputSettings::StereoMode_JointStereo);
|
|
}
|
|
else if( mode == "m" )
|
|
{
|
|
os.setStereoMode(OutputSettings::StereoMode_Mono);
|
|
}
|
|
else
|
|
{
|
|
return usageError( QString( "Invalid stereo mode %1" ).arg( argv[i] ) );
|
|
}
|
|
}
|
|
else if( arg =="--float" || arg == "-a" )
|
|
{
|
|
os.setBitDepth(OutputSettings::Depth_32Bit);
|
|
}
|
|
else if( arg == "--interpolation" || arg == "-i" )
|
|
{
|
|
++i;
|
|
|
|
if( i == argc )
|
|
{
|
|
return usageError( "No interpolation method specified" );
|
|
}
|
|
|
|
|
|
const QString ip = QString( argv[i] );
|
|
|
|
if( ip == "linear" )
|
|
{
|
|
qs.interpolation = Mixer::qualitySettings::Interpolation_Linear;
|
|
}
|
|
else if( ip == "sincfastest" )
|
|
{
|
|
qs.interpolation = Mixer::qualitySettings::Interpolation_SincFastest;
|
|
}
|
|
else if( ip == "sincmedium" )
|
|
{
|
|
qs.interpolation = Mixer::qualitySettings::Interpolation_SincMedium;
|
|
}
|
|
else if( ip == "sincbest" )
|
|
{
|
|
qs.interpolation = Mixer::qualitySettings::Interpolation_SincBest;
|
|
}
|
|
else
|
|
{
|
|
return usageError( QString( "Invalid interpolation method %1" ).arg( argv[i] ) );
|
|
}
|
|
}
|
|
else if( arg == "--oversampling" || arg == "-x" )
|
|
{
|
|
++i;
|
|
|
|
if( i == argc )
|
|
{
|
|
return usageError( "No oversampling specified" );
|
|
}
|
|
|
|
|
|
int o = QString( argv[i] ).toUInt();
|
|
|
|
switch( o )
|
|
{
|
|
case 1:
|
|
qs.oversampling = Mixer::qualitySettings::Oversampling_None;
|
|
break;
|
|
case 2:
|
|
qs.oversampling = Mixer::qualitySettings::Oversampling_2x;
|
|
break;
|
|
case 4:
|
|
qs.oversampling = Mixer::qualitySettings::Oversampling_4x;
|
|
break;
|
|
case 8:
|
|
qs.oversampling = Mixer::qualitySettings::Oversampling_8x;
|
|
break;
|
|
default:
|
|
return usageError( QString( "Invalid oversampling %1" ).arg( argv[i] ) );
|
|
}
|
|
}
|
|
else if( arg == "--import" )
|
|
{
|
|
++i;
|
|
|
|
if( i == argc )
|
|
{
|
|
return usageError( "No file specified for importing" );
|
|
}
|
|
|
|
fileToImport = QString::fromLocal8Bit( argv[i] );
|
|
|
|
// exit after import? (only for debugging)
|
|
if( QString( argv[i + 1] ) == "-e" )
|
|
{
|
|
exitAfterImport = true;
|
|
++i;
|
|
}
|
|
}
|
|
else if( arg == "--profile" || arg == "-p" )
|
|
{
|
|
++i;
|
|
|
|
if( i == argc )
|
|
{
|
|
return usageError( "No profile specified" );
|
|
}
|
|
|
|
|
|
profilerOutputFile = QString::fromLocal8Bit( argv[i] );
|
|
}
|
|
else if( arg == "--config" || arg == "-c" )
|
|
{
|
|
++i;
|
|
|
|
if( i == argc )
|
|
{
|
|
return usageError( "No configuration file specified" );
|
|
}
|
|
|
|
configFile = QString::fromLocal8Bit( argv[i] );
|
|
}
|
|
else
|
|
{
|
|
if( argv[i][0] == '-' )
|
|
{
|
|
return usageError( QString( "Invalid option %1" ).arg( argv[i] ) );
|
|
}
|
|
fileToLoad = QString::fromLocal8Bit( argv[i] );
|
|
}
|
|
}
|
|
|
|
// Test file argument before continuing
|
|
if( !fileToLoad.isEmpty() )
|
|
{
|
|
fileCheck( fileToLoad );
|
|
}
|
|
else if( !fileToImport.isEmpty() )
|
|
{
|
|
fileCheck( fileToImport );
|
|
}
|
|
|
|
ConfigManager::inst()->loadConfigFile(configFile);
|
|
|
|
// set language
|
|
QString pos = ConfigManager::inst()->value( "app", "language" );
|
|
if( pos.isEmpty() )
|
|
{
|
|
pos = QLocale::system().name().left( 2 );
|
|
}
|
|
|
|
#ifdef LMMS_BUILD_WIN32
|
|
#undef QT_TRANSLATIONS_DIR
|
|
#define QT_TRANSLATIONS_DIR ConfigManager::inst()->localeDir()
|
|
#endif
|
|
|
|
#ifdef QT_TRANSLATIONS_DIR
|
|
// load translation for Qt-widgets/-dialogs
|
|
loadTranslation( QString( "qt_" ) + pos,
|
|
QString( QT_TRANSLATIONS_DIR ) );
|
|
#endif
|
|
// load actual translation for LMMS
|
|
loadTranslation( pos );
|
|
|
|
|
|
// try to set realtime priority
|
|
#ifdef LMMS_BUILD_LINUX
|
|
#ifdef LMMS_HAVE_SCHED_H
|
|
#ifndef __OpenBSD__
|
|
struct sched_param sparam;
|
|
sparam.sched_priority = ( sched_get_priority_max( SCHED_FIFO ) +
|
|
sched_get_priority_min( SCHED_FIFO ) ) / 2;
|
|
if( sched_setscheduler( 0, SCHED_FIFO, &sparam ) == -1 )
|
|
{
|
|
printf( "Notice: could not set realtime priority.\n" );
|
|
}
|
|
#endif
|
|
#endif
|
|
#endif
|
|
|
|
#ifdef LMMS_BUILD_WIN32
|
|
if( !SetPriorityClass( GetCurrentProcess(), HIGH_PRIORITY_CLASS ) )
|
|
{
|
|
printf( "Notice: could not set high priority.\n" );
|
|
}
|
|
#endif
|
|
|
|
#if _POSIX_C_SOURCE >= 1 || _XOPEN_SOURCE || _POSIX_SOURCE
|
|
struct sigaction sa;
|
|
sa.sa_handler = SIG_IGN;
|
|
sa.sa_flags = SA_SIGINFO;
|
|
if ( sigemptyset( &sa.sa_mask ) )
|
|
{
|
|
fprintf( stderr, "Signal initialization failed.\n" );
|
|
}
|
|
if ( sigaction( SIGPIPE, &sa, NULL ) )
|
|
{
|
|
fprintf( stderr, "Signal initialization failed.\n" );
|
|
}
|
|
#endif
|
|
|
|
bool destroyEngine = false;
|
|
|
|
// if we have an output file for rendering, just render the song
|
|
// without starting the GUI
|
|
if( !renderOut.isEmpty() )
|
|
{
|
|
Engine::init( true );
|
|
destroyEngine = true;
|
|
|
|
printf( "Loading project...\n" );
|
|
Engine::getSong()->loadProject( fileToLoad );
|
|
if( Engine::getSong()->isEmpty() )
|
|
{
|
|
printf("The project %s is empty, aborting!\n", fileToLoad.toUtf8().constData() );
|
|
exit( EXIT_FAILURE );
|
|
}
|
|
printf( "Done\n" );
|
|
|
|
Engine::getSong()->setExportLoop( renderLoop );
|
|
|
|
// when rendering multiple tracks, renderOut is a directory
|
|
// otherwise, it is a file, so we need to append the file extension
|
|
if ( !renderTracks )
|
|
{
|
|
renderOut = baseName( renderOut ) +
|
|
ProjectRenderer::getFileExtensionFromFormat(eff);
|
|
}
|
|
|
|
// create renderer
|
|
RenderManager * r = new RenderManager( qs, os, eff, renderOut );
|
|
QCoreApplication::instance()->connect( r,
|
|
SIGNAL( finished() ), SLOT( quit() ) );
|
|
|
|
// timer for progress-updates
|
|
QTimer * t = new QTimer( r );
|
|
r->connect( t, SIGNAL( timeout() ),
|
|
SLOT( updateConsoleProgress() ) );
|
|
t->start( 200 );
|
|
|
|
if( profilerOutputFile.isEmpty() == false )
|
|
{
|
|
Engine::mixer()->profiler().setOutputFile( profilerOutputFile );
|
|
}
|
|
|
|
// start now!
|
|
if ( renderTracks )
|
|
{
|
|
r->renderTracks();
|
|
}
|
|
else
|
|
{
|
|
r->renderProject();
|
|
}
|
|
}
|
|
else // otherwise, start the GUI
|
|
{
|
|
new GuiApplication();
|
|
|
|
// re-intialize RNG - shared libraries might have srand() or
|
|
// srandom() calls in their init procedure
|
|
srand( getpid() + time( 0 ) );
|
|
|
|
// recover a file?
|
|
QString recoveryFile = ConfigManager::inst()->recoveryFile();
|
|
|
|
bool recoveryFilePresent = QFileInfo( recoveryFile ).exists() &&
|
|
QFileInfo( recoveryFile ).isFile();
|
|
bool autoSaveEnabled =
|
|
ConfigManager::inst()->value( "ui", "enableautosave" ).toInt();
|
|
if( recoveryFilePresent )
|
|
{
|
|
QMessageBox mb;
|
|
mb.setWindowTitle( MainWindow::tr( "Project recovery" ) );
|
|
mb.setText( QString(
|
|
"<html>"
|
|
"<p style=\"margin-left:6\">%1</p>"
|
|
"<table cellpadding=\"3\">"
|
|
" <tr>"
|
|
" <td><b>%2</b></td>"
|
|
" <td>%3</td>"
|
|
" </tr>"
|
|
" <tr>"
|
|
" <td><b>%4</b></td>"
|
|
" <td>%5</td>"
|
|
" </tr>"
|
|
"</table>"
|
|
"</html>" ).arg(
|
|
MainWindow::tr( "There is a recovery file present. "
|
|
"It looks like the last session did not end "
|
|
"properly or another instance of LMMS is "
|
|
"already running. Do you want to recover the "
|
|
"project of this session?" ),
|
|
MainWindow::tr( "Recover" ),
|
|
MainWindow::tr( "Recover the file. Please don't run "
|
|
"multiple instances of LMMS when you do this." ),
|
|
MainWindow::tr( "Discard" ),
|
|
MainWindow::tr( "Launch a default session and delete "
|
|
"the restored files. This is not reversible." )
|
|
) );
|
|
|
|
mb.setIcon( QMessageBox::Warning );
|
|
mb.setWindowIcon( embed::getIconPixmap( "icon" ) );
|
|
mb.setWindowFlags( Qt::WindowCloseButtonHint );
|
|
|
|
QPushButton * recover;
|
|
QPushButton * discard;
|
|
QPushButton * exit;
|
|
|
|
// setting all buttons to the same roles allows us
|
|
// to have a custom layout
|
|
discard = mb.addButton( MainWindow::tr( "Discard" ),
|
|
QMessageBox::AcceptRole );
|
|
recover = mb.addButton( MainWindow::tr( "Recover" ),
|
|
QMessageBox::AcceptRole );
|
|
|
|
// have a hidden exit button
|
|
exit = mb.addButton( "", QMessageBox::RejectRole);
|
|
exit->setVisible(false);
|
|
|
|
// set icons
|
|
recover->setIcon( embed::getIconPixmap( "recover" ) );
|
|
discard->setIcon( embed::getIconPixmap( "discard" ) );
|
|
|
|
mb.setDefaultButton( recover );
|
|
mb.setEscapeButton( exit );
|
|
|
|
mb.exec();
|
|
if( mb.clickedButton() == discard )
|
|
{
|
|
gui->mainWindow()->sessionCleanup();
|
|
}
|
|
else if( mb.clickedButton() == recover ) // Recover
|
|
{
|
|
fileToLoad = recoveryFile;
|
|
gui->mainWindow()->setSession( MainWindow::SessionState::Recover );
|
|
}
|
|
else // Exit
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
// first show the Main Window and then try to load given file
|
|
|
|
// [Settel] workaround: showMaximized() doesn't work with
|
|
// FVWM2 unless the window is already visible -> show() first
|
|
gui->mainWindow()->show();
|
|
if( fullscreen )
|
|
{
|
|
gui->mainWindow()->showMaximized();
|
|
}
|
|
|
|
// Handle macOS-style FileOpen QEvents
|
|
QString queuedFile = static_cast<MainApplication *>( app )->queuedFile();
|
|
if ( !queuedFile.isEmpty() ) {
|
|
fileToLoad = queuedFile;
|
|
}
|
|
|
|
if( !fileToLoad.isEmpty() )
|
|
{
|
|
if( fileToLoad == recoveryFile )
|
|
{
|
|
Engine::getSong()->createNewProjectFromTemplate( fileToLoad );
|
|
}
|
|
else
|
|
{
|
|
Engine::getSong()->loadProject( fileToLoad );
|
|
}
|
|
}
|
|
else if( !fileToImport.isEmpty() )
|
|
{
|
|
ImportFilter::import( fileToImport, Engine::getSong() );
|
|
if( exitAfterImport )
|
|
{
|
|
return EXIT_SUCCESS;
|
|
}
|
|
}
|
|
// If enabled, open last project if there is one. Else, create
|
|
// a new one.
|
|
else if( ConfigManager::inst()->
|
|
value( "app", "openlastproject" ).toInt() &&
|
|
!ConfigManager::inst()->
|
|
recentlyOpenedProjects().isEmpty() &&
|
|
!recoveryFilePresent )
|
|
{
|
|
QString f = ConfigManager::inst()->
|
|
recentlyOpenedProjects().first();
|
|
QFileInfo recentFile( f );
|
|
|
|
if ( recentFile.exists() &&
|
|
recentFile.suffix().toLower() != "mpt" )
|
|
{
|
|
Engine::getSong()->loadProject( f );
|
|
}
|
|
else
|
|
{
|
|
Engine::getSong()->createNewProject();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Engine::getSong()->createNewProject();
|
|
}
|
|
|
|
// Finally we start the auto save timer and also trigger the
|
|
// autosave one time as recover.mmp is a signal to possible other
|
|
// instances of LMMS.
|
|
if( autoSaveEnabled )
|
|
{
|
|
gui->mainWindow()->autoSaveTimerReset();
|
|
}
|
|
}
|
|
|
|
const int ret = app->exec();
|
|
delete app;
|
|
|
|
if( destroyEngine )
|
|
{
|
|
Engine::destroy();
|
|
}
|
|
|
|
// ProjectRenderer::updateConsoleProgress() doesn't return line after render
|
|
if( coreOnly )
|
|
{
|
|
printf( "\n" );
|
|
}
|
|
|
|
return ret;
|
|
}
|