Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add recording support with Reaper Project generation #1

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions Jamulus.pro
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ win32 {

!exists(/usr/include/jack/jack.h) {
!exists(/usr/local/include/jack/jack.h) {
message(Warning: jack.h was not found at the usual place, maybe the jack dev packet is missing)
message("Warning: jack.h was not found at the usual place, maybe the jack dev packet is missing")
}
}

Expand Down Expand Up @@ -170,7 +170,10 @@ HEADERS += src/audiomixerboard.h \
src/soundbase.h \
src/testbench.h \
src/util.h \
src/analyzerconsole.h
src/analyzerconsole.h \
src/recorder/jamrecorder.h \
src/recorder/creaperproject.h \
src/recorder/cwavestream.h

HEADERS_OPUS = libs/opus/include/opus.h \
libs/opus/include/opus_multistream.h \
Expand Down Expand Up @@ -264,7 +267,10 @@ SOURCES += src/audiomixerboard.cpp \
src/socket.cpp \
src/soundbase.cpp \
src/util.cpp \
src/analyzerconsole.cpp
src/analyzerconsole.cpp \
src/recorder/jamrecorder.cpp \
src/recorder/creaperproject.cpp \
src/recorder/cwavestream.cpp

SOURCES_OPUS = libs/opus/src/opus.c \
libs/opus/src/opus_decoder.c \
Expand Down
8 changes: 8 additions & 0 deletions src/chatdlg.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -102,3 +102,11 @@ void CChatDlg::AddChatText ( QString strChatText )
#endif
);
}

void CChatDlg::keyPressEvent(QKeyEvent *e)
{
if (e->key() != Qt::Key_Escape)
{
QDialog::keyPressEvent(e);
}
}
2 changes: 2 additions & 0 deletions src/chatdlg.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ public slots:
void OnLocalInputTextTextChanged ( const QString& strNewText );
void OnClearPressed();

void keyPressEvent(QKeyEvent *e);

signals:
void NewLocalInputText ( QString strNewText );
};
13 changes: 13 additions & 0 deletions src/clientdlg.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1200,3 +1200,16 @@ rbtReverbSelR->setStyleSheet ( "" );
// also apply GUI design to child GUI controls
MainMixerBoard->SetGUIDesign ( eNewDesign );
}

void CClientDlg::accept()
{
close();
}

void CClientDlg::keyPressEvent(QKeyEvent *e)
{
if (e->key() != Qt::Key_Escape)
{
QDialog::keyPressEvent(e);
}
}
3 changes: 3 additions & 0 deletions src/clientdlg.h
Original file line number Diff line number Diff line change
Expand Up @@ -205,4 +205,7 @@ public slots:
void OnAudioChannelsChanged() { UpdateRevSelection(); }
void OnNumClientsChanged ( int iNewNumClients );
void OnNewClientLevelChanged() { MainMixerBoard->iNewClientFaderLevel = pClient->iNewClientFaderLevel; }

void accept();
void keyPressEvent(QKeyEvent *e);
};
4 changes: 4 additions & 0 deletions src/global.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@ LED bar: lbr
#if !defined ( GLOBAL_H__3B123453_4344_BB2B_23E7A0D31912__INCLUDED_ )
#define GLOBAL_H__3B123453_4344_BB2B_23E7A0D31912__INCLUDED_

#if _WIN32
#define _CRT_SECURE_NO_WARNINGS
#endif

#include <QString>
#include <QEvent>
#include <QDebug>
Expand Down
107 changes: 97 additions & 10 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,17 +33,41 @@
#include "testbench.h"


// Implementation **************************************************************

int main ( int argc, char** argv )
/******************************************************************************\
* Console logging *
\******************************************************************************/
// Try for a portable console --------------------------------------------------

QTextStream* ConsoleWriterFactory::get()
{
#ifdef _WIN32
// no console on windows -> just write in string and dump it
QString strDummySink;
QTextStream tsConsole ( &strDummySink );
if (ptsConsole == nullptr)
{
#if _WIN32
if (!AttachConsole(ATTACH_PARENT_PROCESS))
{
// Not run from console, dump logging to nowhere
static QString conout;
ptsConsole = new QTextStream ( &conout );
}
else
{
freopen("CONOUT$", "w", stdout);
ptsConsole = new QTextStream ( stdout );
}
#else
QTextStream tsConsole ( stdout );
ptsConsole = new QTextStream ( stdout );
#endif
}
return ptsConsole;
}

// Implementation **************************************************************

int main ( int argc, char** argv )
{
QTextStream& tsConsole = *((new ConsoleWriterFactory())->get());

QString strArgument;
double rDbleArgument;

Expand All @@ -56,6 +80,7 @@ int main ( int argc, char** argv )
bool bDisconnectAllClients = false;
bool bShowAnalyzerConsole = false;
bool bCentServPingServerInList = false;
bool bEnableRecording = false;
int iNumServerChannels = DEFAULT_USED_NUM_CHANNELS;
int iCtrlMIDIChannel = INVALID_MIDI_CH;
quint16 iPortNumber = LLCON_DEFAULT_PORT_NUMBER;
Expand All @@ -66,6 +91,8 @@ int main ( int argc, char** argv )
QString strServerName = "";
QString strLoggingFileName = "";
QString strHistoryFileName = "";
QString strRecordingDirName = "";
QString strSessionDirName = "";
QString strCentralServer = "";
QString strServerInfo = "";
QString strWelcomeMessage = "";
Expand Down Expand Up @@ -157,6 +184,18 @@ int main ( int argc, char** argv )
}


// Enable recording at the server --------------------------------------
if ( GetFlagArgument ( argv,
i,
"-r",
"--enablerecording" ) )
{
bEnableRecording = true;
tsConsole << "- enabling recording" << 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
Expand Down Expand Up @@ -292,6 +331,37 @@ int main ( int argc, char** argv )
}


// Recording directory -------------------------------------------------
if ( GetStringArgument ( tsConsole,
argc,
argv,
i,
"-R",
"--recordingdirectory",
strArgument ) )
{
strRecordingDirName = strArgument;
tsConsole << "- recording directory name: " << strRecordingDirName << endl;
continue;
}


// Convert a recording session to a Reaper Project ---------------------
if ( GetStringArgument ( tsConsole,
argc,
argv,
i,
"-T",
"--toreaper",
strArgument ) )
{
bUseGUI = false;
strSessionDirName = strArgument;
tsConsole << "- convert " << strSessionDirName << " to Reaper project (no GUI)" << endl;
continue;
}


// Central server ------------------------------------------------------
if ( GetStringArgument ( tsConsole,
argc,
Expand Down Expand Up @@ -402,7 +472,15 @@ int main ( int argc, char** argv )

// Application/GUI setup ---------------------------------------------------
// Application object
QApplication app ( argc, argv, bUseGUI );
if (!bUseGUI && !strHistoryFileName.isEmpty())
{
tsConsole << "Qt5 requires a windowing system to paint a JPEG image; disabling history graph" << endl;
strHistoryFileName = "";
}
QCoreApplication* _app = bUseGUI
? new QApplication ( argc, argv )
: new QCoreApplication ( argc, argv );
#define app (*_app)

#ifdef _WIN32
// set application priority class -> high priority
Expand All @@ -425,7 +503,11 @@ int main ( int argc, char** argv )

try
{
if ( bIsClient )
if ( !strSessionDirName.isEmpty() )
{
CJamRecorder::SessionDirToReaper(strSessionDirName);
}
else if ( bIsClient )
{
// Client:
// actual client object
Expand Down Expand Up @@ -473,10 +555,11 @@ int main ( int argc, char** argv )
strCentralServer,
strServerInfo,
strWelcomeMessage,
strRecordingDirName,
bEnableRecording,
bCentServPingServerInList,
bDisconnectAllClients,
eLicenceType );

if ( bUseGUI )
{
// special case for the GUI mode: as the default we want to use
Expand Down Expand Up @@ -571,7 +654,11 @@ QString UsageArguments ( char **argv )
" [server1 country as QLocale ID]; ...\n"
" [server2 address]; ... (server only)\n"
" -p, --port local port number (server only)\n"
" -r --enablerecording create recordings of jam sessions (server only)\n"
" -R, --recordingdirectory\n"
" directory to contain recorded jams (server only)\n"
" -s, --server start server\n"
" -T, --toreaper create Reaper project from session in named directory\n"
" -u, --numchannels maximum number of channels (server only)\n"
" -w, --welcomemessage welcome message on connect (server only)\n"
" -y, --history enable connection history and set file\n"
Expand Down
107 changes: 107 additions & 0 deletions src/recorder/creaperproject.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
#include "creaperproject.h"

/**
* @brief operator << Write details of the STrackItem to the QTextStream
* @param os the QTextStream
* @param trackItem the STrackItem
* @return the QTextStream
*
* Note: unused?
*/
QTextStream& operator<<(QTextStream& os, const recorder::STrackItem& trackItem)
{
os << "_track( "
<< "numAudioChannels(" << trackItem.numAudioChannels << ")"
<< ", startFrame(" << trackItem.startFrame << ")"
<< ", frameCount(" << trackItem.frameCount << ")"
<< ", fileName(" << trackItem.fileName << ")"
<< " );";
return os;
}

/******************************************************************************\
* recorder methods *
\******************************************************************************/
using namespace recorder;

// Reaper Project writer -------------------------------------------------------

/**
* @brief CReaperItem::CReaperItem Construct a Reaper RPP "<ITEM>" for a given RIFF WAVE file
* @param name the item name
* @param trackItem the details of where the item is in the track, along with the RIFF WAVE filename
* @param iid the sequential item id
*/
CReaperItem::CReaperItem(const QString& name, const STrackItem& trackItem, const qint32& iid)
{
QString wavName = trackItem.fileName; // assume RPP in same location...

QTextStream sOut(&out);

sOut << " <ITEM " << endl;
sOut << " FADEIN 0 0 0 0 0 0" << endl;
sOut << " FADEOUT 0 0 0 0 0 0" << endl;
sOut << " POSITION " << secondsAt48K(trackItem.startFrame) << endl;
sOut << " LENGTH " << secondsAt48K(trackItem.frameCount) << endl;
sOut << " IGUID " << iguid.toString() << endl;
sOut << " IID " << iid << endl;
sOut << " NAME " << name << endl;
sOut << " GUID " << guid.toString() << endl;

sOut << " <SOURCE WAVE" << endl;
sOut << " FILE " << '"' << wavName << '"' << endl;
sOut << " >" << endl;

sOut << " >";

sOut.flush();
}

/**
* @brief CReaperTrack::CReaperTrack Construct a Reaper RPP "<TRACK>" for a given list of track items
* @param name the track name
* @param iid the sequential track id
* @param items the list of items in the track
*/
CReaperTrack::CReaperTrack(QString name, qint32& iid, QList<STrackItem> items)
{
QTextStream sOut(&out);

sOut << " <TRACK " << trackId.toString() << endl;
sOut << " NAME " << name << endl;
sOut << " TRACKID " << trackId.toString() << endl;

int ino = 1;
foreach (auto item, items) {
sOut << CReaperItem(name + " (" + QString::number(ino) + ")", item, iid).toString() << endl;
ino++;
iid++;
}
sOut << " >";

sOut.flush();
}

/**
* @brief CReaperProject::CReaperProject Construct a Reaper RPP "<REAPER_PROJECT>" for a given list of tracks
* @param tracks the list of tracks
*/
CReaperProject::CReaperProject(QMap<QString, QList<STrackItem>> tracks)
{
QTextStream sOut(&out);

sOut << "<REAPER_PROJECT 0.1 \"5.0\" 1551567848" << endl;
sOut << " RECORD_PATH \"\" \"\"" << endl;
sOut << " SAMPLERATE 48000 0 0" << endl;
sOut << " TEMPO 120 4 4" << endl;

qint32 iid = 0;
foreach(auto trackName, tracks.keys())
{
sOut << CReaperTrack(trackName, iid, tracks[trackName]).toString() << endl;
}

sOut << ">";

sOut.flush();
}
Loading