mumble-voip_mumble/src/mumble/VoiceRecorder.h

228 lines
6.5 KiB
C++

// Copyright 2010-2023 The Mumble Developers. All rights reserved.
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file at the root of the
// Mumble source tree or at <https://www.mumble.info/LICENSE>.
#ifndef MUMBLE_MUMBLE_VOICERECORDER_H_
#define MUMBLE_MUMBLE_VOICERECORDER_H_
#include <QtCore/QtGlobal>
#ifdef Q_OS_WIN
# include "win.h"
#endif
#ifndef Q_MOC_RUN
# include <boost/scoped_ptr.hpp>
# include <boost/shared_array.hpp>
#endif
#include <QtCore/QDateTime>
#include <QtCore/QHash>
#include <QtCore/QMutex>
#include <QtCore/QObject>
#include <QtCore/QThread>
#include <QtCore/QWaitCondition>
#ifdef Q_OS_WIN
# define ENABLE_SNDFILE_WINDOWS_PROTOTYPES 1
#endif
#include <sndfile.h>
class ClientUser;
class RecordUser;
class Timer;
/// Utilities and enums for voice recorder format handling
namespace VoiceRecorderFormat {
/// List of all formats currently supported by the recorder.
enum Format {
/// WAVE Format
WAV = 0,
// When switching between a non vorbis capable lib and a vorbis capable one
// this can mess up the selection stored in the config
#ifndef NO_VORBIS_RECORDING
/// Ogg Vorbis Format
VORBIS,
#endif
/// AU Format
AU,
/// FLAC Format
FLAC,
#ifdef USE_SNDFILE_OPUS
// OPUS Format
OPUS,
#endif
#ifdef USE_SNDFILE_MP3
MP3,
#endif
kEnd
};
/// Returns a human readable description of the format id.
QString getFormatDescription(VoiceRecorderFormat::Format fm);
/// Returns the default extension for the given format.
QString getFormatDefaultExtension(VoiceRecorderFormat::Format fm);
} // namespace VoiceRecorderFormat
/// Class for recording audio data.
///
/// Runs as a separate thread accepting audio data through the addBuffer method
/// which is then encoded using one of the formats of VoiceRecordingFormat::Format
/// and written to disk.
///
class VoiceRecorder : public QThread {
Q_OBJECT
public:
/// Possible error conditions inside the recorder
enum Error { Unspecified, CreateDirectoryFailed, CreateFileFailed, InvalidSampleRate };
/// Structure for holding configuration of VoiceRecorder object
struct Config {
/// The current sample rate of the recorder.
int sampleRate;
/// The path to store recordings.
QString fileName;
/// True if multi channel recording is disabled.
bool mixDownMode;
/// The current recording format.
VoiceRecorderFormat::Format recordingFormat;
};
/// Creates a new VoiceRecorder instance.
VoiceRecorder(QObject *p, const Config &config);
~VoiceRecorder() Q_DECL_OVERRIDE;
/// The main event loop of the thread, which writes all buffers to files.
void run() Q_DECL_OVERRIDE;
/// Stops the main loop.
/// @param force If true buffers are discarded. Otherwise the thread will not stop before writing everything.
void stop(bool force = false);
/// Remembers the current time for a set of coming addBuffer calls
void prepareBufferAdds();
/// Adds an audio buffer which contains |samples| audio samples to the recorder.
/// The audio data will be assumed to be recorded at the time
/// prepareBufferAdds was last called.
/// @param clientUser User for which to add the audio data. nullptr in mixdown mode.
void addBuffer(const ClientUser *clientUser, boost::shared_array< float > buffer, int samples);
/// Returns the elapsed time since the recording started.
quint64 getElapsedTime() const;
/// Returns a reference to the record user which is used to record local audio.
RecordUser &getRecordUser() const;
/// Returns true if the recorder is recording mixed down data instead of multichannel
bool isInMixDownMode() const;
signals:
/// Emitted if an error is encountered
void error(int err, QString strerr);
/// Emitted when recording is started
void recording_started();
/// Emitted when recording is stopped
void recording_stopped();
private:
/// Stores information about a recording buffer.
struct RecordBuffer {
/// Constructs a new RecordBuffer object.
RecordBuffer(int recordInfoIndex_, boost::shared_array< float > buffer_, int samples_,
quint64 absoluteStartSample_);
/// Hashmap index for the user
const int recordInfoIndex;
/// The buffer.
boost::shared_array< float > buffer;
/// The number of samples in the buffer.
int samples;
/// Absolute sample number at the start of this buffer
quint64 absoluteStartSample;
};
/// Stores the recording state for one user.
struct RecordInfo {
RecordInfo(const QString &userName_);
~RecordInfo();
/// Name of the user being recorded
const QString userName;
/// libsndfile's handle.
SNDFILE *soundFile;
/// The last absolute sample we wrote for this users
quint64 lastWrittenAbsoluteSample;
};
typedef QHash< int, boost::shared_ptr< RecordInfo > > RecordInfoMap;
/// Removes invalid characters in a path component.
QString sanitizeFilenameOrPathComponent(const QString &str) const;
/// Expands the template variables in |path| for the given |userName|.
QString expandTemplateVariables(const QString &path, const QString &userName) const;
/// Returns the RecordInfo hashmap index for the given user
int indexForUser(const ClientUser *clientUser) const;
/// Create a sndfile SF_INFO structure describing the currently configured recording format
SF_INFO createSoundFileInfo() const;
/// Opens the file for the given recording information
/// Helper function for run method. Will abort recording on failure.
bool ensureFileIsOpenedFor(SF_INFO &soundFileInfo, boost::shared_ptr< RecordInfo > &ri);
/// Hash which maps the |uiSession| of all users for which we have to keep a recording state to the corresponding
/// RecordInfo object.
RecordInfoMap m_recordInfo;
/// List containing all unprocessed RecordBuffer objects.
QList< boost::shared_ptr< RecordBuffer > > m_recordBuffer;
/// The user which is used to record local audio.
boost::scoped_ptr< RecordUser > m_recordUser;
/// High precision timer for buffer timestamps.
boost::scoped_ptr< Timer > m_timestamp;
/// Protects the buffer list |qlRecordBuffer|.
QMutex m_bufferLock;
/// Wait condition and mutex to block until there is new data.
QMutex m_sleepLock;
QWaitCondition m_sleepCondition;
/// Configuration for this instance
const Config m_config;
/// True if the main loop is active.
bool m_recording;
/// Tells the recorder to not finish writing its buffers before returning
bool m_abort;
/// The timestamp where the recording started.
const QDateTime m_recordingStartTime;
/// Absolute sample position to assume for buffer adds
quint64 m_absoluteSampleEstimation;
};
typedef boost::shared_ptr< VoiceRecorder > VoiceRecorderPtr;
#endif