Ring Daemon 16.0.0
Loading...
Searching...
No Matches
speex.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 "speex.h"
19
20#include "audio/audiolayer.h"
21
22#ifndef _MSC_VER
23#if __has_include(<speex/speexdsp_config_types.h>)
24#include <speex/speexdsp_config_types.h>
25#else
26#include <speex/speex_config_types.h>
27#endif
28#endif
29extern "C" {
30#include <speex/speex_echo.h>
31#include <speex/speex_preprocess.h>
32}
33
34#include <cstdint>
35#include <memory>
36#include <vector>
37
38namespace jami {
39
40inline AudioFormat
45
47 : AudioProcessor(format.withSampleFormat(AV_SAMPLE_FMT_S16), frameSize)
48 , echoState(speex_echo_state_init_mc((int) frameSize,
49 (int) frameSize * 16,
50 (int) format_.nb_channels,
51 (int) format_.nb_channels),
53 , procBuffer(std::make_unique<AudioFrame>(format.withSampleFormat(AV_SAMPLE_FMT_S16P), frameSize_))
54{
55 JAMI_DBG("[speex-dsp] SpeexAudioProcessor, frame size = %d (=%d ms), channels = %d",
56 frameSize,
59 // set up speex echo state
61
62 // speex specific value to turn feature on (need to pass a pointer to it)
64
65 // probability integers, i.e. 50 means 50%
66 // vad will be true if speex's raw probability calculation is higher than this in any case
68
69 // vad will be true if voice was active last frame
70 // AND speex's raw probability calculation is higher than this
72
73 // maximum noise suppression in dB (negative)
75
76 // set up speex preprocess states, one for each channel
77 // note that they are not enabled here, but rather in the enable* functions
78 for (unsigned int i = 0; i < format_.nb_channels; i++) {
80 = SpeexPreprocessStatePtr(speex_preprocess_state_init((int) frameSize,
81 (int) format_.sample_rate),
83
84 // set max noise suppression level
88
89 // set up voice activity values
93 &probStart);
97
98 // keep track of this channel's preprocessor state
99 preprocessorStates.push_back(std::move(channelPreprocessorState));
100 }
101
102 JAMI_INFO("[speex-dsp] Done initializing");
103}
104
105void
107{
108 JAMI_DBG("[speex-dsp] enableEchoCancel %d", enabled);
109 // need to set member variable so we know to do it in getProcessed
110 shouldAEC = enabled;
111
112 if (enabled) {
113 // reset the echo canceller
114 speex_echo_state_reset(echoState.get());
115
116 for (auto& channelPreprocessorState : preprocessorStates) {
117 // attach our already-created echo canceller
120 echoState.get());
121 }
122 } else {
123 for (auto& channelPreprocessorState : preprocessorStates) {
124 // detach echo canceller (set it to NULL)
125 // don't destroy it though, we will reset it when necessary
128 NULL);
129 }
130 }
131}
132
133void
135{
136 JAMI_DBG("[speex-dsp] enableNoiseSuppression %d", enabled);
138
139 // for each preprocessor
140 for (auto& channelPreprocessorState : preprocessorStates) {
141 // set denoise status
145 // set de-reverb status
149 }
150}
151
152void
154{
155 JAMI_DBG("[speex-dsp] enableAutomaticGainControl %d", enabled);
157
158 // for each preprocessor
159 for (auto& channelPreprocessorState : preprocessorStates) {
160 // set AGC status
164 }
165}
166
167void
169{
170 JAMI_DBG("[speex-dsp] enableVoiceActivityDetection %d", enabled);
171
172 shouldDetectVoice = enabled;
173
175 for (auto& channelPreprocessorState : preprocessorStates) {
179 }
180}
181
182std::shared_ptr<AudioFrame>
184{
185 if (tidyQueues()) {
186 return {};
187 }
188
189 auto playback = playbackQueue_.dequeue();
190 auto record = recordQueue_.dequeue();
191
192 if (!playback || !record) {
193 return {};
194 }
195
196 std::shared_ptr<AudioFrame> processed;
197 if (shouldAEC) {
198 // we want to echo cancel
199 // multichannel, output into processed
200 processed = std::make_shared<AudioFrame>(record->getFormat(), record->getFrameSize());
201 speex_echo_cancellation(echoState.get(),
202 (int16_t*) record->pointer()->data[0],
203 (int16_t*) playback->pointer()->data[0],
204 (int16_t*) processed->pointer()->data[0]);
205 } else {
206 // don't want to echo cancel, so just use record frame instead
208 }
209
210 deinterleaveResampler.resample(processed->pointer(), procBuffer->pointer());
211
212 // overall voice activity
213 bool overallVad = false;
214 // current channel voice activity
215 int channelVad;
216
217 // run preprocess on each channel
218 int channel = 0;
219 for (auto& channelPreprocessorState : preprocessorStates) {
220 // preprocesses in place, returns voice activity boolean
221 channelVad = speex_preprocess_run(channelPreprocessorState.get(), (int16_t*)procBuffer->pointer()->data[channel]);
222
223 // boolean OR
225
226 channel += 1;
227 }
228
229 interleaveResampler.resample(procBuffer->pointer(), processed->pointer());
230
231 // add stabilized voice activity to the AudioFrame
232 processed->has_voice = shouldDetectVoice && getStabilizedVoiceActivity(overallVad);
233 return processed;
234}
235
236} // namespace jami
Main sound class.
std::shared_ptr< AudioFrame > dequeue()
Notifies owner of a new frame.
unsigned int frameDurationMs_
AudioFrameResizer playbackQueue_
bool tidyQueues()
Helper method for audio processors, should be called at start of getProcessed() Pops frames from audi...
bool getStabilizedVoiceActivity(bool voiceStatus)
Stablilizes voice activity.
AudioFrameResizer recordQueue_
int resample(const AVFrame *input, AVFrame *output)
Resample a frame.
void enableVoiceActivityDetection(bool enabled) override
Set the status of voice activity detection.
Definition speex.cpp:168
void enableEchoCancel(bool enabled) override
Set the status of echo cancellation.
Definition speex.cpp:106
SpeexAudioProcessor(AudioFormat format, unsigned frameSize)
Definition speex.cpp:46
std::shared_ptr< AudioFrame > getProcessed() override
Process and return a single AudioFrame.
Definition speex.cpp:183
void enableNoiseSuppression(bool enabled) override
Set the status of noise suppression includes de-reverb, de-noise, high pass filter,...
Definition speex.cpp:134
void enableAutomaticGainControl(bool enabled) override
Set the status of automatic gain control.
Definition speex.cpp:153
#define JAMI_DBG(...)
Definition logger.h:216
#define JAMI_INFO(...)
Definition logger.h:215
void emitSignal(Args... args)
Definition ring_signal.h:64
AudioFormat audioFormatToSampleFormat(AudioFormat format)
Definition speex.cpp:41
Structure to hold sample rate and channel number associated with audio data.