// Copyright 2005-2020 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 . #ifndef MUMBLE_MUMBLE_VOICERECORDER_H_ #define MUMBLE_MUMBLE_VOICERECORDER_H_ #include #ifdef Q_OS_WIN # include "win.h" #endif #ifndef Q_MOC_RUN # include # include #endif #include #include #include #include #include #include #ifdef Q_OS_WIN # define ENABLE_SNDFILE_WINDOWS_PROTOTYPES 1 #endif #include 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, 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); } /// Class for recording audio data. /// /// Runs as a seperate 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. NULL in mixdown mode. void addBuffer(const ClientUser *clientUser, boost::shared_array buffer, int samples); /// Returns the elapsed time since the recording started. quint64 getElapsedTime() const; /// Returns a refence 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 buffer_, int samples_, quint64 absoluteStartSample_); /// Hashmap index for the user const int recordInfoIndex; /// The buffer. boost::shared_array 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 > 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 &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 > m_recordBuffer; /// The user which is used to record local audio. boost::scoped_ptr m_recordUser; /// High precision timer for buffer timestamps. boost::scoped_ptr 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 VoiceRecorderPtr; #endif