1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
|
// Copyright 2005-2017 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_
#ifndef Q_MOC_RUN
# include <boost/scoped_ptr.hpp>
# include <boost/shared_array.hpp>
#endif
#include <sndfile.h>
#include <QtCore/QDateTime>
#include <QtCore/QHash>
#include <QtCore/QMutex>
#include <QtCore/QObject>
#include <QtCore/QThread>
#include <QtCore/QWaitCondition>
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<float> 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<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
|