jamulus/src/main.cpp

873 lines
30 KiB
C++
Raw Normal View History

/******************************************************************************\
2020-01-01 15:41:43 +01:00
* Copyright (c) 2004-2020
*
* Author(s):
* Volker Fischer
*
******************************************************************************
*
* 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; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*
\******************************************************************************/
#include <QCoreApplication>
#include <QDir>
#include <QTextStream>
2020-04-23 17:00:58 +02:00
#include <QTranslator>
2020-05-15 00:12:00 +02:00
#include <QLibraryInfo>
#include "global.h"
#ifndef HEADLESS
# include <QApplication>
# include <QMessageBox>
# include "clientdlg.h"
# include "serverdlg.h"
#endif
#include "settings.h"
#include "testbench.h"
#include "util.h"
#ifdef ANDROID
2020-05-11 17:10:38 +02:00
# include <QtAndroidExtras/QtAndroid>
#endif
#if defined ( __APPLE__ ) || defined ( __MACOSX )
# include "mac/activity.h"
#endif
2020-05-11 17:10:38 +02:00
Add recording support with Reaper Project generation Includes the following changes * Initial .gitignore Administrative * Fix up warning message * Not all Windows file systems are case insensitive Bugfixes * (Qt5) Use QCoreApplication for headless Possible solution to get the application to run as a headless server but it loses the nice history graph, so not ideal. * Avoid ESC closing chat Because ESC shouldn't close the chat window. Or the main app window. * Add console logging support for Windows Whilst looking for the headless support, I found this idea for Windows logging. New improved version. This makes far fewer changes. ---- * Add recording support with Reaper Project generation The main feature! * New -r option to enable recording of PCM files and conversion to Reaper RPP with WAV files * New -R option to set the directory in which to create recording sessions You need to specify the -R option, there's no default... so I guess -r and -R could be combined. * New -T option to convert a session directory with PCM files into a Reaper RPP with WAV files You can use -T on "failed" sessions, if the -r option captures the PCMs but the RPP converter doesn't run for some reaon. (It was useful during development, maybe less so once things seem stable.) The recorder is implemented as a new thread with queuing from the main "real time" server thread. When a new client connects or if its audio format changes (e.g. mono to stereo), a new RIFF WAVE file is started. Each frame of decompressed audio for each client written out as LPCM to the file. When the client disconnects, the RIFF WAVE headers are updated to reflect the file length. Once all clients disconnect, the session is considered ended and a Reaper RPP file is written.
2019-04-03 19:12:45 +02:00
// Implementation **************************************************************
int main ( int argc, char** argv )
{
QTextStream& tsConsole = *( ( new ConsoleWriterFactory() )->get() );
QString strArgument;
double rDbleArgument;
// initialize all flags and string which might be changed by command line
// arguments
#if defined( SERVER_BUNDLE ) && ( defined( __APPLE__ ) || defined( __MACOSX ) )
// if we are on MacOS and we are building a server bundle, starts Jamulus in server mode
bool bIsClient = false;
#else
bool bIsClient = true;
#endif
bool bUseGUI = true;
bool bStartMinimized = false;
bool bShowComplRegConnList = false;
bool bDisconnectAllClientsOnQuit = false;
bool bUseDoubleSystemFrameSize = true; // default is 128 samples frame size
bool bShowAnalyzerConsole = false;
bool bCentServPingServerInList = false;
bool bNoAutoJackConnect = false;
bool bUseTranslation = true;
bool bCustomPortNumberGiven = false;
int iNumServerChannels = DEFAULT_USED_NUM_CHANNELS;
int iMaxDaysHistory = DEFAULT_DAYS_HISTORY;
int iCtrlMIDIChannel = INVALID_MIDI_CH;
2020-05-15 21:00:41 +02:00
quint16 iPortNumber = DEFAULT_PORT_NUMBER;
ELicenceType eLicenceType = LT_NO_LICENCE;
QString strConnOnStartupAddress = "";
QString strIniFileName = "";
QString strHTMLStatusFileName = "";
QString strServerName = "";
QString strLoggingFileName = "";
QString strHistoryFileName = "";
QString strRecordingDirName = "";
QString strCentralServer = "";
QString strServerInfo = "";
QString strWelcomeMessage = "";
QString strClientName = APP_NAME;
// QT docu: argv()[0] is the program name, argv()[1] is the first
// argument and argv()[argc()-1] is the last argument.
// Start with first argument, therefore "i = 1"
for ( int i = 1; i < argc; i++ )
{
// Server mode flag ----------------------------------------------------
if ( GetFlagArgument ( argv,
i,
"-s",
"--server" ) )
{
bIsClient = false;
tsConsole << "- server mode chosen" << endl;
continue;
}
// Use GUI flag --------------------------------------------------------
if ( GetFlagArgument ( argv,
i,
"-n",
"--nogui" ) )
{
bUseGUI = false;
tsConsole << "- no GUI mode chosen" << endl;
continue;
}
// Use licence flag ----------------------------------------------------
if ( GetFlagArgument ( argv,
i,
"-L",
"--licence" ) )
{
// right now only the creative commons licence is supported
eLicenceType = LT_CREATIVECOMMONS;
tsConsole << "- licence required" << endl;
continue;
}
// Use 64 samples frame size mode ----------------------------------------------------
if ( GetFlagArgument ( argv,
i,
"-F",
"--fastupdate" ) )
{
bUseDoubleSystemFrameSize = false; // 64 samples frame size
tsConsole << "- using " << SYSTEM_FRAME_SIZE_SAMPLES << " samples frame size mode" << endl;
continue;
}
// Maximum number of channels ------------------------------------------
if ( GetNumericArgument ( tsConsole,
argc,
argv,
i,
"-u",
"--numchannels",
1,
MAX_NUM_CHANNELS,
rDbleArgument ) )
{
iNumServerChannels = static_cast<int> ( rDbleArgument );
tsConsole << "- maximum number of channels: "
<< iNumServerChannels << endl;
continue;
}
2020-03-22 20:24:30 +01:00
// Maximum days in history display -------------------------------------
if ( GetNumericArgument ( tsConsole,
argc,
argv,
i,
"-D",
"--histdays",
1,
366,
rDbleArgument ) )
{
iMaxDaysHistory = static_cast<int> ( rDbleArgument );
tsConsole << "- maximum days in history display: "
<< iMaxDaysHistory << endl;
continue;
}
// Start minimized -----------------------------------------------------
if ( GetFlagArgument ( argv,
i,
"-z",
"--startminimized" ) )
{
bStartMinimized = true;
tsConsole << "- start minimized enabled" << endl;
continue;
}
// Ping servers in list for central server -----------------------------
if ( GetFlagArgument ( argv,
i,
"-g",
"--pingservers" ) )
{
bCentServPingServerInList = true;
tsConsole << "- ping servers in slave server list" << endl;
continue;
}
// Disconnect all clients on quit --------------------------------------
if ( GetFlagArgument ( argv,
i,
"-d",
"--discononquit" ) )
{
bDisconnectAllClientsOnQuit = true;
tsConsole << "- disconnect all clients on quit" << endl;
continue;
}
// Disabling auto Jack connections -------------------------------------
if ( GetFlagArgument ( argv,
i,
"-j",
"--nojackconnect" ) )
{
bNoAutoJackConnect = true;
tsConsole << "- disable auto Jack connections" << endl;
continue;
}
// Disable translations ------------------------------------------------
if ( GetFlagArgument ( argv,
i,
"-t",
"--notranslation" ) )
{
bUseTranslation = false;
tsConsole << "- translations disabled" << endl;
continue;
}
// Show all registered servers in the server list ----------------------
// Undocumented debugging command line argument: Show all registered
// servers in the server list regardless if a ping to the server is
// possible or not.
if ( GetFlagArgument ( argv,
i,
"--showallservers", // no short form
"--showallservers" ) )
{
bShowComplRegConnList = true;
tsConsole << "- show all registered servers in server list" << endl;
continue;
}
// Show analyzer console -----------------------------------------------
// Undocumented debugging command line argument: Show the analyzer
// console to debug network buffer properties.
if ( GetFlagArgument ( argv,
i,
"--showanalyzerconsole", // no short form
"--showanalyzerconsole" ) )
{
bShowAnalyzerConsole = true;
tsConsole << "- show analyzer console" << endl;
continue;
}
// Controller MIDI channel ---------------------------------------------
if ( GetNumericArgument ( tsConsole,
argc,
argv,
i,
"--ctrlmidich", // no short form
"--ctrlmidich",
0,
15,
rDbleArgument ) )
{
iCtrlMIDIChannel = static_cast<int> ( rDbleArgument );
tsConsole << "- selected controller MIDI channel: " << iCtrlMIDIChannel << endl;
continue;
}
// Use logging ---------------------------------------------------------
if ( GetStringArgument ( tsConsole,
argc,
argv,
i,
"-l",
"--log",
strArgument ) )
{
strLoggingFileName = strArgument;
tsConsole << "- logging file name: " << strLoggingFileName << endl;
continue;
}
// Port number ---------------------------------------------------------
if ( GetNumericArgument ( tsConsole,
argc,
argv,
i,
"-p",
"--port",
0,
65535,
rDbleArgument ) )
{
iPortNumber = static_cast<quint16> ( rDbleArgument );
bCustomPortNumberGiven = true;
tsConsole << "- selected port number: " << iPortNumber << endl;
continue;
}
// HTML status file ----------------------------------------------------
if ( GetStringArgument ( tsConsole,
argc,
argv,
i,
"-m",
"--htmlstatus",
strArgument ) )
{
strHTMLStatusFileName = strArgument;
tsConsole << "- HTML status file name: " << strHTMLStatusFileName << endl;
continue;
}
if ( GetStringArgument ( tsConsole,
argc,
argv,
i,
"-a",
"--servername",
strArgument ) )
{
strServerName = strArgument;
tsConsole << "- server name for HTML status file: " << strServerName << endl;
continue;
}
2020-04-30 22:03:01 +02:00
// Client Name ---------------------------------------------------------
if ( GetStringArgument ( tsConsole,
argc,
argv,
i,
"--clientname",
"--clientname",
strArgument ) )
{
strClientName = QString ( APP_NAME ) + " " + strArgument;
tsConsole << "- client name: " << strClientName << endl;
continue;
}
// Server history file name --------------------------------------------
if ( GetStringArgument ( tsConsole,
argc,
argv,
i,
"-y",
"--history",
strArgument ) )
{
strHistoryFileName = strArgument;
tsConsole << "- history file name: " << strHistoryFileName << endl;
continue;
}
Add recording support with Reaper Project generation Includes the following changes * Initial .gitignore Administrative * Fix up warning message * Not all Windows file systems are case insensitive Bugfixes * (Qt5) Use QCoreApplication for headless Possible solution to get the application to run as a headless server but it loses the nice history graph, so not ideal. * Avoid ESC closing chat Because ESC shouldn't close the chat window. Or the main app window. * Add console logging support for Windows Whilst looking for the headless support, I found this idea for Windows logging. New improved version. This makes far fewer changes. ---- * Add recording support with Reaper Project generation The main feature! * New -r option to enable recording of PCM files and conversion to Reaper RPP with WAV files * New -R option to set the directory in which to create recording sessions You need to specify the -R option, there's no default... so I guess -r and -R could be combined. * New -T option to convert a session directory with PCM files into a Reaper RPP with WAV files You can use -T on "failed" sessions, if the -r option captures the PCMs but the RPP converter doesn't run for some reaon. (It was useful during development, maybe less so once things seem stable.) The recorder is implemented as a new thread with queuing from the main "real time" server thread. When a new client connects or if its audio format changes (e.g. mono to stereo), a new RIFF WAVE file is started. Each frame of decompressed audio for each client written out as LPCM to the file. When the client disconnects, the RIFF WAVE headers are updated to reflect the file length. Once all clients disconnect, the session is considered ended and a Reaper RPP file is written.
2019-04-03 19:12:45 +02:00
// Recording directory -------------------------------------------------
if ( GetStringArgument ( tsConsole,
argc,
argv,
i,
"-R",
"--recording",
Add recording support with Reaper Project generation Includes the following changes * Initial .gitignore Administrative * Fix up warning message * Not all Windows file systems are case insensitive Bugfixes * (Qt5) Use QCoreApplication for headless Possible solution to get the application to run as a headless server but it loses the nice history graph, so not ideal. * Avoid ESC closing chat Because ESC shouldn't close the chat window. Or the main app window. * Add console logging support for Windows Whilst looking for the headless support, I found this idea for Windows logging. New improved version. This makes far fewer changes. ---- * Add recording support with Reaper Project generation The main feature! * New -r option to enable recording of PCM files and conversion to Reaper RPP with WAV files * New -R option to set the directory in which to create recording sessions You need to specify the -R option, there's no default... so I guess -r and -R could be combined. * New -T option to convert a session directory with PCM files into a Reaper RPP with WAV files You can use -T on "failed" sessions, if the -r option captures the PCMs but the RPP converter doesn't run for some reaon. (It was useful during development, maybe less so once things seem stable.) The recorder is implemented as a new thread with queuing from the main "real time" server thread. When a new client connects or if its audio format changes (e.g. mono to stereo), a new RIFF WAVE file is started. Each frame of decompressed audio for each client written out as LPCM to the file. When the client disconnects, the RIFF WAVE headers are updated to reflect the file length. Once all clients disconnect, the session is considered ended and a Reaper RPP file is written.
2019-04-03 19:12:45 +02:00
strArgument ) )
{
strRecordingDirName = strArgument;
tsConsole << "- recording directory name: " << strRecordingDirName << endl;
continue;
}
// Central server ------------------------------------------------------
if ( GetStringArgument ( tsConsole,
argc,
argv,
i,
"-e",
"--centralserver",
strArgument ) )
{
strCentralServer = strArgument;
tsConsole << "- central server: " << strCentralServer << endl;
continue;
}
// Server info ---------------------------------------------------------
if ( GetStringArgument ( tsConsole,
argc,
argv,
i,
"-o",
"--serverinfo",
strArgument ) )
{
strServerInfo = strArgument;
tsConsole << "- server info: " << strServerInfo << endl;
continue;
}
// Server welcome message ----------------------------------------------
if ( GetStringArgument ( tsConsole,
argc,
argv,
i,
"-w",
"--welcomemessage",
strArgument ) )
{
strWelcomeMessage = strArgument;
tsConsole << "- welcome message: " << strWelcomeMessage << endl;
continue;
}
// Initialization file -------------------------------------------------
if ( GetStringArgument ( tsConsole,
argc,
argv,
i,
"-i",
"--inifile",
strArgument ) )
{
strIniFileName = strArgument;
tsConsole << "- initialization file name: " << strIniFileName << endl;
continue;
}
// Connect on startup --------------------------------------------------
if ( GetStringArgument ( tsConsole,
argc,
argv,
i,
"-c",
"--connect",
strArgument ) )
{
strConnOnStartupAddress = strArgument;
tsConsole << "- connect on startup to address: " << strConnOnStartupAddress << endl;
continue;
}
// Version number ------------------------------------------------------
if ( ( !strcmp ( argv[i], "--version" ) ) ||
( !strcmp ( argv[i], "-v" ) ) )
{
tsConsole << GetVersionAndNameStr ( false ) << endl;
exit ( 1 );
}
// Help (usage) flag ---------------------------------------------------
if ( ( !strcmp ( argv[i], "--help" ) ) ||
( !strcmp ( argv[i], "-h" ) ) ||
( !strcmp ( argv[i], "-?" ) ) )
{
const QString strHelp = UsageArguments ( argv );
tsConsole << strHelp << endl;
exit ( 1 );
}
// Unknown option ------------------------------------------------------
tsConsole << argv[0] << ": ";
tsConsole << "Unknown option '" <<
argv[i] << "' -- use '--help' for help" << endl;
// clicking on the Mac application bundle, the actual application
// is called with weird command line args -> do not exit on these
#if !( defined ( __APPLE__ ) || defined ( __MACOSX ) )
exit ( 1 );
#endif
}
#ifdef HEADLESS
2020-06-17 19:34:51 +02:00
if ( bUseGUI )
{
bUseGUI = false;
tsConsole << "No GUI support compiled. Running in headless mode." << endl;
}
2020-06-18 19:22:21 +02:00
Q_UNUSED ( bStartMinimized ) // avoid compiler warnings
Q_UNUSED ( bShowComplRegConnList ) // avoid compiler warnings
Q_UNUSED ( bShowAnalyzerConsole ) // avoid compiler warnings
#endif
// Dependencies ------------------------------------------------------------
// per definition: if we are in "GUI" server mode and no central server
// address is given, we use the default central server address
if ( !bIsClient && bUseGUI && strCentralServer.isEmpty() )
{
strCentralServer = DEFAULT_SERVER_ADDRESS;
}
// adjust default port number for client: use different default port than the server since
// if the client is started before the server, the server would get a socket bind error
if ( bIsClient && !bCustomPortNumberGiven )
{
iPortNumber += 10; // increment by 10
}
// display a warning if in server no GUI mode and a history file is requested
if ( !bIsClient && !bUseGUI && !strHistoryFileName.isEmpty() )
Add recording support with Reaper Project generation Includes the following changes * Initial .gitignore Administrative * Fix up warning message * Not all Windows file systems are case insensitive Bugfixes * (Qt5) Use QCoreApplication for headless Possible solution to get the application to run as a headless server but it loses the nice history graph, so not ideal. * Avoid ESC closing chat Because ESC shouldn't close the chat window. Or the main app window. * Add console logging support for Windows Whilst looking for the headless support, I found this idea for Windows logging. New improved version. This makes far fewer changes. ---- * Add recording support with Reaper Project generation The main feature! * New -r option to enable recording of PCM files and conversion to Reaper RPP with WAV files * New -R option to set the directory in which to create recording sessions You need to specify the -R option, there's no default... so I guess -r and -R could be combined. * New -T option to convert a session directory with PCM files into a Reaper RPP with WAV files You can use -T on "failed" sessions, if the -r option captures the PCMs but the RPP converter doesn't run for some reaon. (It was useful during development, maybe less so once things seem stable.) The recorder is implemented as a new thread with queuing from the main "real time" server thread. When a new client connects or if its audio format changes (e.g. mono to stereo), a new RIFF WAVE file is started. Each frame of decompressed audio for each client written out as LPCM to the file. When the client disconnects, the RIFF WAVE headers are updated to reflect the file length. Once all clients disconnect, the session is considered ended and a Reaper RPP file is written.
2019-04-03 19:12:45 +02:00
{
2019-05-19 09:30:49 +02:00
tsConsole << "Qt5 requires a windowing system to paint a JPEG image; image will use SVG" << endl;
Add recording support with Reaper Project generation Includes the following changes * Initial .gitignore Administrative * Fix up warning message * Not all Windows file systems are case insensitive Bugfixes * (Qt5) Use QCoreApplication for headless Possible solution to get the application to run as a headless server but it loses the nice history graph, so not ideal. * Avoid ESC closing chat Because ESC shouldn't close the chat window. Or the main app window. * Add console logging support for Windows Whilst looking for the headless support, I found this idea for Windows logging. New improved version. This makes far fewer changes. ---- * Add recording support with Reaper Project generation The main feature! * New -r option to enable recording of PCM files and conversion to Reaper RPP with WAV files * New -R option to set the directory in which to create recording sessions You need to specify the -R option, there's no default... so I guess -r and -R could be combined. * New -T option to convert a session directory with PCM files into a Reaper RPP with WAV files You can use -T on "failed" sessions, if the -r option captures the PCMs but the RPP converter doesn't run for some reaon. (It was useful during development, maybe less so once things seem stable.) The recorder is implemented as a new thread with queuing from the main "real time" server thread. When a new client connects or if its audio format changes (e.g. mono to stereo), a new RIFF WAVE file is started. Each frame of decompressed audio for each client written out as LPCM to the file. When the client disconnects, the RIFF WAVE headers are updated to reflect the file length. Once all clients disconnect, the session is considered ended and a Reaper RPP file is written.
2019-04-03 19:12:45 +02:00
}
// Application/GUI setup ---------------------------------------------------
// Application object
#ifdef HEADLESS
QCoreApplication* pApp = new QCoreApplication ( argc, argv );
#else
QCoreApplication* pApp = bUseGUI
2020-06-17 19:34:51 +02:00
? new QApplication ( argc, argv )
: new QCoreApplication ( argc, argv );
#endif
2020-05-11 17:10:38 +02:00
#ifdef ANDROID
2020-05-11 17:10:38 +02:00
// special Android coded needed for record audio permission handling
auto result = QtAndroid::checkPermission ( QString ( "android.permission.RECORD_AUDIO" ) );
if ( result == QtAndroid::PermissionResult::Denied )
{
QtAndroid::PermissionResultMap resultHash = QtAndroid::requestPermissionsSync ( QStringList ( { "android.permission.RECORD_AUDIO" } ) );
if ( resultHash["android.permission.RECORD_AUDIO"] == QtAndroid::PermissionResult::Denied )
{
return 0;
}
2020-05-11 17:10:38 +02:00
}
#endif
#ifdef _WIN32
// set application priority class -> high priority
SetPriorityClass ( GetCurrentProcess(), HIGH_PRIORITY_CLASS );
// For accessible support we need to add a plugin to qt. The plugin has to
2013-03-24 11:49:25 +01:00
// be located in the install directory of the software by the installer.
// Here, we set the path to our application path.
QDir ApplDir ( QApplication::applicationDirPath() );
pApp->addLibraryPath ( QString ( ApplDir.absolutePath() ) );
#endif
2020-06-11 08:35:10 +02:00
#if defined ( __APPLE__ ) || defined ( __MACOSX )
// On OSX we need to declare an activity to ensure the process doesn't get
// throttled by OS level Nap, Sleep, and Thread Priority systems.
CActivity activity;
activity.BeginActivity();
#endif
// init resources
Q_INIT_RESOURCE(resources);
// load translations
2020-05-15 15:55:40 +02:00
QTranslator myappTranslator, myqtTranslator;
if ( bUseGUI && bUseTranslation )
{
if ( myappTranslator.load ( QLocale(), "translation", "_", ":/translations" ) )
{
pApp->installTranslator ( &myappTranslator );
}
2020-05-15 00:12:00 +02:00
2020-05-15 15:55:40 +02:00
// allows the Qt messages to be translated in the application
if ( myqtTranslator.load ( QLocale(), "qt", "_", QLibraryInfo::location ( QLibraryInfo::TranslationsPath ) ) )
2020-05-15 00:12:00 +02:00
{
pApp->installTranslator ( &myqtTranslator );
}
}
2020-04-23 17:00:58 +02:00
// TEST -> activate the following line to activate the test bench,
2020-05-15 21:00:41 +02:00
//CTestbench Testbench ( "127.0.0.1", DEFAULT_PORT_NUMBER );
try
{
if ( bIsClient )
{
// Client:
// actual client object
CClient Client ( iPortNumber,
strConnOnStartupAddress,
iCtrlMIDIChannel,
bNoAutoJackConnect,
strClientName );
// load settings from init-file
CClientSettings Settings ( &Client, strIniFileName );
Settings.Load();
#ifndef HEADLESS
if ( bUseGUI )
{
// GUI object
CClientDlg ClientDlg ( &Client,
&Settings,
strConnOnStartupAddress,
iCtrlMIDIChannel,
bShowComplRegConnList,
bShowAnalyzerConsole,
2019-04-12 18:04:28 +02:00
nullptr,
Qt::Window );
// show dialog
ClientDlg.show();
pApp->exec();
}
else
#endif
{
// only start application without using the GUI
tsConsole << GetVersionAndNameStr ( false ) << endl;
pApp->exec();
}
}
else
{
// Server:
// actual server object
CServer Server ( iNumServerChannels,
iMaxDaysHistory,
strLoggingFileName,
iPortNumber,
strHTMLStatusFileName,
strHistoryFileName,
strServerName,
strCentralServer,
strServerInfo,
strWelcomeMessage,
Add recording support with Reaper Project generation Includes the following changes * Initial .gitignore Administrative * Fix up warning message * Not all Windows file systems are case insensitive Bugfixes * (Qt5) Use QCoreApplication for headless Possible solution to get the application to run as a headless server but it loses the nice history graph, so not ideal. * Avoid ESC closing chat Because ESC shouldn't close the chat window. Or the main app window. * Add console logging support for Windows Whilst looking for the headless support, I found this idea for Windows logging. New improved version. This makes far fewer changes. ---- * Add recording support with Reaper Project generation The main feature! * New -r option to enable recording of PCM files and conversion to Reaper RPP with WAV files * New -R option to set the directory in which to create recording sessions You need to specify the -R option, there's no default... so I guess -r and -R could be combined. * New -T option to convert a session directory with PCM files into a Reaper RPP with WAV files You can use -T on "failed" sessions, if the -r option captures the PCMs but the RPP converter doesn't run for some reaon. (It was useful during development, maybe less so once things seem stable.) The recorder is implemented as a new thread with queuing from the main "real time" server thread. When a new client connects or if its audio format changes (e.g. mono to stereo), a new RIFF WAVE file is started. Each frame of decompressed audio for each client written out as LPCM to the file. When the client disconnects, the RIFF WAVE headers are updated to reflect the file length. Once all clients disconnect, the session is considered ended and a Reaper RPP file is written.
2019-04-03 19:12:45 +02:00
strRecordingDirName,
bCentServPingServerInList,
bDisconnectAllClientsOnQuit,
bUseDoubleSystemFrameSize,
eLicenceType );
2020-06-17 19:34:51 +02:00
#ifndef HEADLESS
if ( bUseGUI )
{
// load settings from init-file
CServerSettings Settings ( &Server, strIniFileName );
Settings.Load();
// update server list AFTER restoring the settings from the
// settings file
Server.UpdateServerList();
// GUI object for the server
2013-03-24 16:42:23 +01:00
CServerDlg ServerDlg ( &Server,
&Settings,
bStartMinimized,
2019-04-12 18:04:28 +02:00
nullptr,
2013-03-24 16:42:23 +01:00
Qt::Window );
2011-05-25 22:00:54 +02:00
// show dialog (if not the minimized flag is set)
if ( !bStartMinimized )
{
ServerDlg.show();
}
pApp->exec();
}
else
#endif
{
// only start application without using the GUI
tsConsole << GetVersionAndNameStr ( false ) << endl;
// update serverlist
Server.UpdateServerList();
pApp->exec();
}
}
}
catch ( const CGenErr& generr )
{
// show generic error
#ifndef HEADLESS
if ( bUseGUI )
{
2019-04-12 18:04:28 +02:00
QMessageBox::critical ( nullptr,
APP_NAME,
generr.GetErrorText(),
"Quit",
2019-04-12 18:04:28 +02:00
nullptr );
}
else
#endif
{
tsConsole << generr.GetErrorText() << endl;
}
}
2020-07-04 22:25:08 +02:00
#if defined ( __APPLE__ ) || defined ( __MACOSX )
activity.EndActivity();
#endif
return 0;
}
/******************************************************************************\
* Command Line Argument Parsing *
\******************************************************************************/
QString UsageArguments ( char **argv )
{
return
"Usage: " + QString ( argv[0] ) + " [option] [optional argument]\n"
"\nRecognized options:\n"
" -h, -?, --help display this help text and exit\n"
" -i, --inifile initialization file name\n"
" -n, --nogui disable GUI\n"
" -p, --port set your local port number\n"
" -t, --notranslation disable translation (use englisch language)\n"
" -v, --version output version information and exit\n"
"\nServer only:\n"
" -a, --servername server name, required for HTML status\n"
" -d, --discononquit disconnect all clients on quit\n"
" -D, --histdays number of days of history to display\n"
" -e, --centralserver address of the central server\n"
" -F, --fastupdate use 64 samples frame size mode\n"
" -g, --pingservers ping servers in list to keep NAT port open\n"
" (central server only)\n"
" -l, --log enable logging, set file name\n"
" -L, --licence a licence must be accepted on a new\n"
" connection\n"
" -m, --htmlstatus enable HTML status file, set file name\n"
" -o, --serverinfo infos of the server(s) in the format:\n"
" [name];[city];[country as QLocale ID]; ...\n"
" [server1 address];[server1 name]; ...\n"
" [server1 city]; ...\n"
" [server1 country as QLocale ID]; ...\n"
" [server2 address]; ...\n"
" -R, --recording enables recording and sets directory to contain\n"
" recorded jams\n"
" -s, --server start server\n"
" -u, --numchannels maximum number of channels\n"
" -w, --welcomemessage welcome message on connect\n"
" -y, --history enable connection history and set file name\n"
" -z, --startminimized start minimizied\n"
"\nClient only:\n"
" -c, --connect connect to given server address on startup\n"
" -j, --nojackconnect disable auto Jack connections\n"
" --ctrlmidich MIDI controller channel to listen\n"
2020-04-30 22:03:01 +02:00
" --clientname client name (window title and jack client name)\n"
"\nExample: " + QString ( argv[0] ) + " -s --inifile myinifile.ini\n";
}
bool GetFlagArgument ( char** argv,
int& i,
QString strShortOpt,
QString strLongOpt )
{
if ( ( !strShortOpt.compare ( argv[i] ) ) ||
( !strLongOpt.compare ( argv[i] ) ) )
{
return true;
}
else
{
return false;
}
}
bool GetStringArgument ( QTextStream& tsConsole,
int argc,
char** argv,
int& i,
QString strShortOpt,
QString strLongOpt,
QString& strArg )
{
if ( ( !strShortOpt.compare ( argv[i] ) ) ||
( !strLongOpt.compare ( argv[i] ) ) )
{
if ( ++i >= argc )
{
tsConsole << argv[0] << ": ";
tsConsole << "'" << strLongOpt << "' needs a string argument" << endl;
exit ( 1 );
}
strArg = argv[i];
return true;
}
else
{
return false;
}
}
bool GetNumericArgument ( QTextStream& tsConsole,
int argc,
char** argv,
int& i,
QString strShortOpt,
QString strLongOpt,
double rRangeStart,
double rRangeStop,
double& rValue )
{
if ( ( !strShortOpt.compare ( argv[i] ) ) ||
( !strLongOpt.compare ( argv[i] ) ) )
{
if ( ++i >= argc )
{
tsConsole << argv[0] << ": ";
tsConsole << "'" <<
strLongOpt << "' needs a numeric argument between " <<
rRangeStart << " and " << rRangeStop << endl;
exit ( 1 );
}
char *p;
rValue = strtod ( argv[i], &p );
if ( *p ||
( rValue < rRangeStart ) ||
( rValue > rRangeStop ) )
{
tsConsole << argv[0] << ": ";
tsConsole << "'" <<
strLongOpt << "' needs a numeric argument between " <<
rRangeStart << " and " << rRangeStop << endl;
exit ( 1 );
}
return true;
}
else
{
return false;
}
}