Ring Daemon 16.0.0
Loading...
Searching...
No Matches
audiolayer.cpp
Go to the documentation of this file.
1/*
2 * Copyright (C) 2004-2025 Savoir-faire Linux Inc.
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <https://www.gnu.org/licenses/>.
16 */
17
18#include "audiolayer.h"
19#include "logger.h"
20#include "manager.h"
22#include "audio/resampler.h"
23#include "tonecontrol.h"
24#include "client/ring_signal.h"
25
27#include "tracepoint.h"
28#if HAVE_WEBRTC_AP
30#endif
31#if HAVE_SPEEXDSP
33#endif
34
35#include <ctime>
36#include <algorithm>
37
38namespace jami {
39
41 : isCaptureMuted_(pref.getCaptureMuted())
42 , isPlaybackMuted_(pref.getPlaybackMuted())
43 , captureGain_(pref.getVolumemic())
44 , playbackGain_(pref.getVolumespkr())
45 , pref_(pref)
46 , mainRingBuffer_(
47 Manager::instance().getRingBufferPool().getRingBuffer(RingBufferPool::DEFAULT_ID))
48 , audioFormat_(Manager::instance().getRingBufferPool().getInternalAudioFormat())
49 , audioInputFormat_(Manager::instance().getRingBufferPool().getInternalAudioFormat())
50 , urgentRingBuffer_("urgentRingBuffer_id", SIZEBUF, audioFormat_)
51 , resampler_(new Resampler)
52 , lastNotificationTime_()
53{
55
56 JAMI_LOG("[audiolayer] AGC: {:d}, noiseReduce: {:s}, VAD: {:d}, echoCancel: {:s}, audioProcessor: {:s}",
58 pref.getNoiseReduce(),
59 pref.getVadEnabled(),
60 pref.getEchoCanceller(),
61 pref.getAudioProcessor());
62}
63
65
66void
75
76void
78{
79 JAMI_LOG("Hardware input audio format available: {:s}", capture.toString());
80}
81
82void
87
88void
93
94void
99
100void
106
107void
109{
110 playbackStarted_ = started;
111}
112
113void
115{
116 std::lock_guard lock(audioProcessorMutex);
117 if (started) {
118 // create audio processor
119 createAudioProcessor();
120 } else {
121 // destroy audio processor
122 destroyAudioProcessor();
123 }
124 recordStarted_ = started;
125}
126
127// helper function
128static inline bool
129shouldUseAudioProcessorEchoCancel(bool hasNativeAEC, const std::string& echoCancellerPref)
130{
131 return
132 // user doesn't care which and there is not a system AEC
133 (echoCancellerPref == "auto" && !hasNativeAEC)
134 // user specifically wants audioProcessor
135 or (echoCancellerPref == "audioProcessor");
136}
137
138// helper function
139static inline bool
141{
142 return
143 // user doesn't care which and there is no system noise suppression
144 (noiseSuppressionPref == "auto" && !hasNativeNS)
145 // user specifically wants audioProcessor
146 or (noiseSuppressionPref == "audioProcessor");
147}
148
149void
151{
152 JAMI_INFO("[audiolayer] setHasNativeAEC: %d", hasNativeAEC);
153 std::lock_guard lock(audioProcessorMutex);
154 hasNativeAEC_ = hasNativeAEC;
155 // if we have a current audio processor, tell it to enable/disable its own AEC
156 if (audioProcessor) {
157 audioProcessor->enableEchoCancel(
159 }
160}
161
162void
164{
165 JAMI_INFO("[audiolayer] setHasNativeNS: %d", hasNativeNS);
166 std::lock_guard lock(audioProcessorMutex);
167 hasNativeNS_ = hasNativeNS;
168 // if we have a current audio processor, tell it to enable/disable its own noise suppression
169 if (audioProcessor) {
170 audioProcessor->enableNoiseSuppression(
172 }
173}
174
175// must acquire lock beforehand
176void
177AudioLayer::createAudioProcessor()
178{
179 auto nb_channels = std::max(audioFormat_.nb_channels, audioInputFormat_.nb_channels);
180 auto sample_rate = std::max(audioFormat_.sample_rate, audioInputFormat_.sample_rate);
181
182 sample_rate = std::clamp(sample_rate, 16000u, 48000u);
183
184 AudioFormat formatForProcessor {sample_rate, nb_channels};
185
186 unsigned int frame_size;
187 if (pref_.getAudioProcessor() == "speex") {
188 // TODO: maybe force this to be equivalent to 20ms? as expected by Speex
189 frame_size = sample_rate / 50u;
190 } else {
191 frame_size = sample_rate / 100u;
192 }
193
195 JAMI_WARNING("Output {}", audioFormat_.toString());
196 JAMI_WARNING("Starting audio processor with: [{} Hz, {} channels, {} samples/frame]",
197 sample_rate,
198 nb_channels,
199 frame_size);
200
201 if (pref_.getAudioProcessor() == "webrtc") {
202#if HAVE_WEBRTC_AP
203 JAMI_WARN("[audiolayer] using WebRTCAudioProcessor");
204 audioProcessor.reset(new WebRTCAudioProcessor(formatForProcessor, frame_size));
205#else
206 JAMI_ERR("[audiolayer] audioProcessor preference is webrtc, but library not linked! "
207 "using null AudioProcessor instead");
208 audioProcessor.reset();
209#endif
210 } else if (pref_.getAudioProcessor() == "speex") {
211#if HAVE_SPEEXDSP
212 JAMI_WARN("[audiolayer] using SpeexAudioProcessor");
213 audioProcessor.reset(new SpeexAudioProcessor(formatForProcessor, frame_size));
214#else
215 JAMI_ERR("[audiolayer] audioProcessor preference is Speex, but library not linked! "
216 "using null AudioProcessor instead");
217 audioProcessor.reset();
218#endif
219 } else if (pref_.getAudioProcessor() == "null") {
220 JAMI_WARN("[audiolayer] using null AudioProcessor");
221 audioProcessor.reset();
222 } else {
223 JAMI_ERR("[audiolayer] audioProcessor preference not recognized, using null AudioProcessor "
224 "instead");
225 audioProcessor.reset();
226 }
227
228 if (audioProcessor) {
229 audioProcessor->enableNoiseSuppression(
231
232 audioProcessor->enableAutomaticGainControl(pref_.isAGCEnabled());
233
234 audioProcessor->enableEchoCancel(
236
237 audioProcessor->enableVoiceActivityDetection(pref_.getVadEnabled());
238 }
239}
240
241// must acquire lock beforehand
242void
243AudioLayer::destroyAudioProcessor()
244{
245 // delete it
246 audioProcessor.reset();
247}
248
249void
250AudioLayer::putUrgent(std::shared_ptr<AudioFrame> buffer)
251{
252 urgentRingBuffer_.put(std::move(buffer));
253}
254
255// Notify (with a beep) an incoming call when there is already a call in progress
256void
258{
259 if (not playIncomingCallBeep_)
260 return;
261
262 auto now = std::chrono::system_clock::now();
263
264 // Notify maximum once every 5 seconds
265 if (now < lastNotificationTime_ + std::chrono::seconds(5))
266 return;
267
268 lastNotificationTime_ = now;
269
271 size_t nbSample = tone.getSize();
272
273 /* Put the data in the urgent ring buffer */
276}
277
278std::shared_ptr<AudioFrame>
280{
281 if (auto fileToPlay = Manager::instance().getTelephoneFile()) {
282 auto fileformat = fileToPlay->getFormat();
283 bool resample = format != fileformat;
284
285 size_t readableSamples = resample ? rational<size_t>(writableSamples * (size_t)fileformat.sample_rate, format.sample_rate)
286 .real<size_t>()
288
289 return resampler_->resample(fileToPlay->getNext(readableSamples, isRingtoneMuted_), format);
290 }
291 return {};
292}
293
294std::shared_ptr<AudioFrame>
296{
299
302 else
303 playbackQueue_->setFrameSize(writableSamples);
304
305 std::shared_ptr<AudioFrame> playbackBuf {};
306 while (!(playbackBuf = playbackQueue_->dequeue())) {
307 std::shared_ptr<AudioFrame> resampled;
308
311 resampled = resampler_->resample(std::move(urgentSamples), format);
312 } else if (auto toneToPlay = Manager::instance().getTelephoneTone()) {
313 resampled = resampler_->resample(toneToPlay->getNext(), format);
314 } else if (auto buf = bufferPool.getData(RingBufferPool::DEFAULT_ID)) {
315 resampled = resampler_->resample(std::move(buf), format);
316 } else {
317 std::lock_guard lock(audioProcessorMutex);
318 if (audioProcessor) {
319 auto silence = std::make_shared<AudioFrame>(format, writableSamples);
321 audioProcessor->putPlayback(silence);
322 }
323 break;
324 }
325
326 if (resampled) {
327 std::lock_guard lock(audioProcessorMutex);
328 if (audioProcessor) {
329 audioProcessor->putPlayback(resampled);
330 }
331 playbackQueue_->enqueue(std::move(resampled));
332 } else
333 break;
334 }
335
337
338 return playbackBuf;
339}
340
341void
342AudioLayer::putRecorded(std::shared_ptr<AudioFrame>&& frame)
343{
344 std::lock_guard lock(audioProcessorMutex);
345 if (audioProcessor && playbackStarted_ && recordStarted_) {
346 audioProcessor->putRecorded(std::move(frame));
347 while (auto rec = audioProcessor->getProcessed()) {
348 mainRingBuffer_->put(std::move(rec));
349 }
350 } else {
351 mainRingBuffer_->put(std::move(frame));
352 }
353
355}
356
357} // namespace jami
Main sound class.
Buffers extra samples.
std::shared_ptr< RingBuffer > mainRingBuffer_
Buffers for audio processing.
Definition audiolayer.h:254
std::unique_ptr< Resampler > resampler_
Manage sampling rate conversion.
Definition audiolayer.h:288
RingBuffer urgentRingBuffer_
Urgent ring buffer used for ringtones.
Definition audiolayer.h:278
virtual ~AudioLayer()
void playbackChanged(bool started)
void notifyIncomingCall()
Emit an audio notification (beep) on incoming calls.
void setHasNativeNS(bool hasNS)
void hardwareFormatAvailable(AudioFormat playback, size_t bufSize=0)
Callback to be called by derived classes when the audio output is opened.
std::shared_ptr< AudioFrame > getToRing(AudioFormat format, size_t writableSamples)
void flushUrgent()
Flush urgent buffer.
AudioLayer(const AudioPreference &)
void hardwareInputFormatAvailable(AudioFormat capture)
Set the input format on necessary objects.
std::unique_ptr< AudioFrameResizer > playbackQueue_
Definition audiolayer.h:255
void setHasNativeAEC(bool hasEAC)
std::shared_ptr< AudioFrame > getToPlay(AudioFormat format, size_t writableSamples)
size_t nativeFrameSize_
Definition audiolayer.h:273
bool isRingtoneMuted_
True if ringtone should be muted.
Definition audiolayer.h:231
void recordChanged(bool started)
AudioFormat audioFormat_
Sample Rate that should be sent to the sound card.
Definition audiolayer.h:266
void flushMain()
Flush main buffer.
unsigned int getSampleRate() const
Get the sample rate of the audio layer.
Definition audiolayer.h:173
const AudioPreference & pref_
Definition audiolayer.h:249
AudioFormat audioInputFormat_
Sample Rate for input.
Definition audiolayer.h:271
void putRecorded(std::shared_ptr< AudioFrame > &&frame)
void putUrgent(std::shared_ptr< AudioFrame > buffer)
Send a chunk of data to the hardware buffer to start the playback Copy data in the urgent buffer.
bool getVadEnabled() const
bool isAGCEnabled() const
const std::string & getEchoCanceller() const
const std::string & getNoiseReduce() const
const std::string & getAudioProcessor() const
Manager (controller) of daemon.
Definition manager.h:67
static LIBJAMI_TEST_EXPORT Manager & instance()
Definition manager.cpp:676
RingBufferPool & getRingBufferPool()
Return a pointer to the instance of the RingBufferPool.
Definition manager.cpp:3157
AudioFormat hardwareAudioFormatChanged(AudioFormat format)
Callback called when the audio layer initialised with its preferred format.
Definition manager.cpp:2632
Wrapper class for libswresample.
Definition resampler.h:36
size_t discard(size_t toDiscard, const std::string &ringbufferId)
static const char *const DEFAULT_ID
void setFormat(const AudioFormat &format)
Definition ringbuffer.h:72
void createReadOffset(const std::string &ringbufferId)
Add a new readoffset for this ringbuffer.
std::shared_ptr< AudioFrame > get(const std::string &ringbufferId)
Get data in the ring buffer.
void put(std::shared_ptr< AudioFrame > &&data)
Write data in the ring buffer.
#define JAMI_ERR(...)
Definition logger.h:218
#define JAMI_WARN(...)
Definition logger.h:217
#define JAMI_WARNING(formatstr,...)
Definition logger.h:227
#define JAMI_LOG(formatstr,...)
Definition logger.h:225
#define JAMI_INFO(...)
Definition logger.h:215
void fillWithSilence(AVFrame *frame)
void emitSignal(Args... args)
Definition ring_signal.h:64
static constexpr size_t SIZEBUF
Definition ring_types.h:31
static bool shouldUseAudioProcessorEchoCancel(bool hasNativeAEC, const std::string &echoCancellerPref)
static bool shouldUseAudioProcessorNoiseSuppression(bool hasNativeNS, const std::string &noiseSuppressionPref)
Structure to hold sample rate and channel number associated with audio data.
AVSampleFormat sampleFormat
std::string toString() const
#define jami_tracepoint(...)
Definition tracepoint.h:53