Ring Daemon
Loading...
Searching...
No Matches
audiolayer.cpp
Go to the documentation of this file.
1/*
2 * Copyright (C) 2004-2026 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 "audio/sound/tone.h"
20#include "logger.h"
21#include "manager.h"
23#include "audio/resampler.h"
24#include "client/jami_signal.h"
25
26#include "tracepoint.h"
27#if HAVE_WEBRTC_AP
29#endif
30#if HAVE_SPEEXDSP
32#endif
33
34#include <ctime>
35#include <algorithm>
36
37namespace jami {
38
40 : isCaptureMuted_(pref.getCaptureMuted())
41 , isPlaybackMuted_(pref.getPlaybackMuted())
42 , captureGain_(pref.getVolumemic())
43 , playbackGain_(pref.getVolumespkr())
44 , pref_(pref)
45 , mainRingBuffer_(Manager::instance().getRingBufferPool().getRingBuffer(RingBufferPool::DEFAULT_ID))
46 , audioFormat_(Manager::instance().getRingBufferPool().getInternalAudioFormat())
47 , audioInputFormat_(Manager::instance().getRingBufferPool().getInternalAudioFormat())
48 , urgentRingBuffer_("urgentRingBuffer_id", audioFormat_)
49 , resampler_(new Resampler)
50 , lastNotificationTime_()
51{
53
54 JAMI_LOG("[audiolayer] AGC: {:d}, noiseReduce: {:s}, VAD: {:d}, echoCancel: {:s}, audioProcessor: {:s}",
56 pref.getNoiseReduce(),
57 pref.getVadEnabled(),
58 pref.getEchoCanceller(),
59 pref.getAudioProcessor());
60}
61
63
64void
73
74void
76{
77 JAMI_LOG("Hardware input audio format available: {:s}", capture.toString());
78}
79
80void
85
86void
91
92void
97
98void
104
105void
107{
108 playbackStarted_ = started;
109}
110
111void
113{
114 std::lock_guard lock(audioProcessorMutex);
115 if (started) {
116 // create audio processor
117 createAudioProcessor();
118 } else {
119 // destroy audio processor
120 destroyAudioProcessor();
121 }
122 recordStarted_ = started;
123}
124
125// helper function
126static inline bool
128{
129 return
130 // user doesn't care which and there is not a system AEC
131 (echoCancellerPref == "auto" && !hasNativeAEC)
132 // user specifically wants audioProcessor
133 or (echoCancellerPref == "audioProcessor");
134}
135
136// helper function
137static inline bool
139{
140 return
141 // user doesn't care which and there is no system noise suppression
142 (noiseSuppressionPref == "auto" && !hasNativeNS)
143 // user specifically wants audioProcessor
144 or (noiseSuppressionPref == "audioProcessor");
145}
146
147void
149{
150 JAMI_INFO("[audiolayer] setHasNativeAEC: %d", hasNativeAEC);
151 std::lock_guard lock(audioProcessorMutex);
153 // if we have a current audio processor, tell it to enable/disable its own AEC
154 if (audioProcessor) {
155 audioProcessor->enableEchoCancel(shouldUseAudioProcessorEchoCancel(hasNativeAEC, pref_.getEchoCanceller()));
156 }
157}
158
159void
161{
162 JAMI_INFO("[audiolayer] setHasNativeNS: %d", hasNativeNS);
163 std::lock_guard lock(audioProcessorMutex);
165 // if we have a current audio processor, tell it to enable/disable its own noise suppression
166 if (audioProcessor) {
167 audioProcessor->enableNoiseSuppression(
169 }
170}
171
172// must acquire lock beforehand
173void
174AudioLayer::createAudioProcessor()
175{
176 auto nb_channels = std::max(audioFormat_.nb_channels, audioInputFormat_.nb_channels);
177 auto sample_rate = std::max(audioFormat_.sample_rate, audioInputFormat_.sample_rate);
178
179 sample_rate = std::clamp(sample_rate, 16000u, 48000u);
180
181 AudioFormat formatForProcessor {sample_rate, nb_channels};
182
183 unsigned int frame_size;
184 if (pref_.getAudioProcessor() == "speex") {
185 // TODO: maybe force this to be equivalent to 20ms? as expected by Speex
186 frame_size = sample_rate / 50u;
187 } else {
188 frame_size = sample_rate / 100u;
189 }
190
192 JAMI_WARNING("Output {}", audioFormat_.toString());
193 JAMI_WARNING("Starting audio processor with: [{} Hz, {} channels, {} samples/frame]",
194 sample_rate,
195 nb_channels,
196 frame_size);
197
198 if (pref_.getAudioProcessor() == "webrtc") {
199#if HAVE_WEBRTC_AP
200 JAMI_WARN("[audiolayer] using WebRTCAudioProcessor");
201 audioProcessor.reset(new WebRTCAudioProcessor(formatForProcessor, frame_size));
202#else
203 JAMI_ERR("[audiolayer] audioProcessor preference is webrtc, but library not linked! "
204 "using null AudioProcessor instead");
205 audioProcessor.reset();
206#endif
207 } else if (pref_.getAudioProcessor() == "speex") {
208#if HAVE_SPEEXDSP
209 JAMI_WARN("[audiolayer] using SpeexAudioProcessor");
210 audioProcessor.reset(new SpeexAudioProcessor(formatForProcessor, frame_size));
211#else
212 JAMI_ERR("[audiolayer] audioProcessor preference is Speex, but library not linked! "
213 "using null AudioProcessor instead");
214 audioProcessor.reset();
215#endif
216 } else if (pref_.getAudioProcessor() == "null") {
217 JAMI_WARN("[audiolayer] using null AudioProcessor");
218 audioProcessor.reset();
219 } else {
220 JAMI_ERR("[audiolayer] audioProcessor preference not recognized, using null AudioProcessor "
221 "instead");
222 audioProcessor.reset();
223 }
224
225 if (audioProcessor) {
226 audioProcessor->enableNoiseSuppression(
228
229 audioProcessor->enableAutomaticGainControl(pref_.isAGCEnabled());
230
231 audioProcessor->enableEchoCancel(shouldUseAudioProcessorEchoCancel(hasNativeAEC_, pref_.getEchoCanceller()));
232
233 audioProcessor->enableVoiceActivityDetection(pref_.getVadEnabled());
234 }
235}
236
237// must acquire lock beforehand
238void
239AudioLayer::destroyAudioProcessor()
240{
241 // delete it
242 audioProcessor.reset();
243}
244
245void
246AudioLayer::putUrgent(std::shared_ptr<AudioFrame> buffer)
247{
248 urgentRingBuffer_.put(std::move(buffer));
249}
250
251// Notify (with a beep) an incoming call when there is already a call in progress
252void
254{
255 if (not playIncomingCallBeep_)
256 return;
257
258 auto now = std::chrono::system_clock::now();
259
260 // Notify maximum once every 5 seconds
261 if (now < lastNotificationTime_ + std::chrono::seconds(5))
262 return;
263
264 lastNotificationTime_ = now;
265
267 size_t nbSample = tone.getSize();
268
269 /* Put the data in the urgent ring buffer */
272}
273
274std::shared_ptr<AudioFrame>
276{
277 if (auto fileToPlay = Manager::instance().getTelephoneFile()) {
278 auto fileformat = fileToPlay->getFormat();
279 bool resample = format != fileformat;
280
281 size_t readableSamples = resample ? rational<size_t>(writableSamples * (size_t) fileformat.sample_rate,
282 format.sample_rate)
283 .real<size_t>()
285
286 return resampler_->resample(fileToPlay->getNext(readableSamples, isRingtoneMuted_), format);
287 }
288 return {};
289}
290
291std::shared_ptr<AudioFrame>
293{
296
298 playbackQueue_.reset(new AudioFrameResizer(format, static_cast<int>(writableSamples)));
299 else
300 playbackQueue_->setFrameSize(static_cast<int>(writableSamples));
301
302 std::shared_ptr<AudioFrame> playbackBuf {};
303 while (!(playbackBuf = playbackQueue_->dequeue())) {
304 std::shared_ptr<AudioFrame> resampled;
305
308 resampled = resampler_->resample(std::move(urgentSamples), format);
309 } else if (auto toneToPlay = Manager::instance().getTelephoneTone()) {
310 resampled = resampler_->resample(toneToPlay->getNext(), format);
311 } else if (auto buf = bufferPool.getData(RingBufferPool::DEFAULT_ID)) {
312 resampled = resampler_->resample(std::move(buf), format);
313 } else {
314 std::lock_guard lock(audioProcessorMutex);
315 if (audioProcessor) {
316 auto silence = std::make_shared<AudioFrame>(format, writableSamples);
318 audioProcessor->putPlayback(silence);
319 }
320 break;
321 }
322
323 if (resampled) {
324 std::lock_guard lock(audioProcessorMutex);
325 if (audioProcessor) {
326 audioProcessor->putPlayback(resampled);
327 }
328 playbackQueue_->enqueue(std::move(resampled));
329 } else
330 break;
331 }
332
334
335 return playbackBuf;
336}
337
338void
339AudioLayer::putRecorded(std::shared_ptr<AudioFrame>&& frame)
340{
341 std::lock_guard lock(audioProcessorMutex);
342 if (audioProcessor && playbackStarted_ && recordStarted_) {
343 audioProcessor->putRecorded(std::move(frame));
344 while (auto rec = audioProcessor->getProcessed()) {
345 mainRingBuffer_->put(std::move(rec));
346 }
347 } else {
348 mainRingBuffer_->put(std::move(frame));
349 }
350
352}
353
354} // namespace jami
Main sound class.
Buffers extra samples.
std::shared_ptr< RingBuffer > mainRingBuffer_
Buffers for audio processing.
Definition audiolayer.h:267
std::unique_ptr< Resampler > resampler_
Manage sampling rate conversion.
Definition audiolayer.h:301
RingBuffer urgentRingBuffer_
Urgent ring buffer used for ringtones.
Definition audiolayer.h:291
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:268
void setHasNativeAEC(bool hasEAC)
std::shared_ptr< AudioFrame > getToPlay(AudioFormat format, size_t writableSamples)
size_t nativeFrameSize_
Definition audiolayer.h:286
bool isRingtoneMuted_
True if ringtone should be muted.
Definition audiolayer.h:244
void recordChanged(bool started)
AudioFormat audioFormat_
Sample Rate that should be sent to the sound card.
Definition audiolayer.h:279
void flushMain()
Flush main buffer.
unsigned int getSampleRate() const
Get the sample rate of the audio layer.
Definition audiolayer.h:186
const AudioPreference & pref_
Definition audiolayer.h:262
AudioFormat audioInputFormat_
Sample Rate for input.
Definition audiolayer.h:284
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:66
static LIBJAMI_TEST_EXPORT Manager & instance()
Definition manager.cpp:694
RingBufferPool & getRingBufferPool()
Return a pointer to the instance of the RingBufferPool.
Definition manager.cpp:3197
AudioFormat hardwareAudioFormatChanged(AudioFormat format)
Callback called when the audio layer initialised with its preferred format.
Definition manager.cpp:2656
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:70
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:230
#define JAMI_WARN(...)
Definition logger.h:229
#define JAMI_WARNING(formatstr,...)
Definition logger.h:242
#define JAMI_LOG(formatstr,...)
Definition logger.h:237
#define JAMI_INFO(...)
Definition logger.h:227
void fillWithSilence(AVFrame *frame)
void emitSignal(Args... args)
Definition jami_signal.h:64
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
Tone sample (dial, busy, ring, congestion)
#define jami_tracepoint(...)
Definition tracepoint.h:48