jamulus/src/recorder/jamrecorder.h

157 lines
4.1 KiB
C
Raw Normal View History

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
#ifndef JAMRECORDER_H
#define JAMRECORDER_H
#include <QDir>
#include <QFile>
#include <QDateTime>
#include "../util.h"
#include "../channel.h"
#include "creaperproject.h"
#include "cwavestream.h"
namespace recorder {
class CJamClientConnection : public QObject
{
Q_OBJECT
public:
CJamClientConnection(const int _numAudioChannels, const qint64 _startFrame, const qint64 _length, const QString _name, const QString _fileName) :
numAudioChannels (_numAudioChannels),
startFrame (_startFrame),
length (_length),
name (_name),
fileName (_fileName)
{
}
int Format() { return numAudioChannels; }
qint64 StartFrame() { return startFrame; }
qint64 Length() { return length; }
QString Name() { return name; }
QString FileName() { return fileName; }
private:
const int numAudioChannels;
const qint64 startFrame;
const qint64 length;
const QString name;
const QString fileName;
};
class CJamClient : public QObject
{
Q_OBJECT
public:
CJamClient(const qint64 frame, const int numChannels, const QString name, const CHostAddress address, const QDir recordBaseDir);
void Frame(const QString name, const CVector<int16_t>& pcm);
void Disconnect();
qint64 StartFrame() { return startFrame; }
qint64 FrameCount() { return frameCount; }
uint16_t NumAudioChannels() { return numChannels; }
QString ClientName() { return name.leftJustified(4, '_', false).replace(QRegExp("[-.:/\\ ]"), "_")
.append("-")
.append(address.toString(CHostAddress::EStringMode::SM_IP_NO_LAST_BYTE_PORT).replace(QRegExp("[-.:/\\ ]"), "_"))
;
}
CHostAddress ClientAddress() { return address; }
QString FileName() { return filename; }
private:
const qint64 startFrame;
const uint16_t numChannels;
QString name;
const CHostAddress address;
QString filename;
QFile* wavFile;
QDataStream* out;
qint64 frameCount = 0;
};
class CJamSession : public QObject
{
Q_OBJECT
public:
CJamSession(QDir recordBaseDir);
void Frame(const int iChID, const QString name, const CHostAddress address, const int numAudioChannels, const CVector<int16_t> data);
void End();
QVector<CJamClient*> Clients() { return vecptrJamClients; }
QMap<QString, QList<STrackItem>> Tracks();
QString Name() { return sessionDir.dirName(); }
const QDir SessionDir() { return sessionDir; }
void DisconnectClient(int iChID);
static QMap<QString, QList<STrackItem>> TracksFromSessionDir(const QString& name);
private:
CJamSession();
const QDir sessionDir;
qint64 currentFrame;
QVector<CJamClient*> vecptrJamClients;
QList<CJamClientConnection*> jamClientConnections;
};
class CJamRecorder : public QThread
{
Q_OBJECT
public:
CJamRecorder(const CServer* server, const QString recordingDirName);
static void SessionDirToReaper(QString& strSessionDirName);
public slots:
/**
* @brief Raised when first client joins the server, triggering a new recording.
*/
void OnStart();
/**
* @brief Raised when last client leaves the server, ending the recording.
*/
void OnEnd();
/**
* @brief Raised when an existing client leaves the server.
* @param iChID channel number of client
*/
void OnDisconnected(int iChID);
/**
* @brief Raised when a frame of data fis available to process
*/
void OnFrame(const int iChID, const QString name, const CHostAddress address, const int numAudioChannels, const CVector<int16_t> data);
private:
QDir recordBaseDir;
bool isRecording;
CJamSession* currentSession;
QTextStream& tsConsole = *((new ConsoleWriterFactory())->get());
};
}
Q_DECLARE_METATYPE(int16_t)
Q_DECLARE_METATYPE(CVector<int16_t>)
#endif // JAMRECORDER_H