Ring Daemon
Loading...
Searching...
No Matches
speex.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 "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++) {
79 auto channelPreprocessorState = SpeexPreprocessStatePtr(speex_preprocess_state_init((int) frameSize,
80 (int) format_.sample_rate),
82
83 // set max noise suppression level
85
86 // set up voice activity values
90
91 // keep track of this channel's preprocessor state
92 preprocessorStates.push_back(std::move(channelPreprocessorState));
93 }
94
95 JAMI_INFO("[speex-dsp] Done initializing");
96}
97
98void
100{
101 JAMI_DBG("[speex-dsp] enableEchoCancel %d", enabled);
102 // need to set member variable so we know to do it in getProcessed
103 shouldAEC = enabled;
104
105 if (enabled) {
106 // reset the echo canceller
107 speex_echo_state_reset(echoState.get());
108
109 for (auto& channelPreprocessorState : preprocessorStates) {
110 // attach our already-created echo canceller
112 }
113 } else {
114 for (auto& channelPreprocessorState : preprocessorStates) {
115 // detach echo canceller (set it to NULL)
116 // don't destroy it though, we will reset it when necessary
118 }
119 }
120}
121
122void
124{
125 JAMI_DBG("[speex-dsp] enableNoiseSuppression %d", enabled);
127
128 // for each preprocessor
129 for (auto& channelPreprocessorState : preprocessorStates) {
130 // set denoise status
132 // set de-reverb status
134 }
135}
136
137void
139{
140 JAMI_DBG("[speex-dsp] enableAutomaticGainControl %d", enabled);
142
143 // for each preprocessor
144 for (auto& channelPreprocessorState : preprocessorStates) {
145 // set AGC status
147 }
148}
149
150void
152{
153 JAMI_DBG("[speex-dsp] enableVoiceActivityDetection %d", enabled);
154
155 shouldDetectVoice = enabled;
156
158 for (auto& channelPreprocessorState : preprocessorStates) {
160 }
161}
162
163std::shared_ptr<AudioFrame>
165{
166 if (tidyQueues()) {
167 return {};
168 }
169
170 auto playback = playbackQueue_.dequeue();
171 auto record = recordQueue_.dequeue();
172
173 if (!playback || !record) {
174 return {};
175 }
176
177 std::shared_ptr<AudioFrame> processed;
178 if (shouldAEC) {
179 // we want to echo cancel
180 // multichannel, output into processed
181 processed = std::make_shared<AudioFrame>(record->getFormat(), record->getFrameSize());
182 speex_echo_cancellation(echoState.get(),
183 (int16_t*) record->pointer()->data[0],
184 (int16_t*) playback->pointer()->data[0],
185 (int16_t*) processed->pointer()->data[0]);
186 } else {
187 // don't want to echo cancel, so just use record frame instead
189 }
190
191 deinterleaveResampler.resample(processed->pointer(), procBuffer->pointer());
192
193 // overall voice activity
194 bool overallVad = false;
195 // current channel voice activity
196 int channelVad;
197
198 // run preprocess on each channel
199 int channel = 0;
200 for (auto& channelPreprocessorState : preprocessorStates) {
201 // preprocesses in place, returns voice activity boolean
203 (int16_t*) procBuffer->pointer()->data[channel]);
204
205 // boolean OR
207
208 channel += 1;
209 }
210
211 interleaveResampler.resample(procBuffer->pointer(), processed->pointer());
212
213 // add stabilized voice activity to the AudioFrame
214 processed->has_voice = shouldDetectVoice && getStabilizedVoiceActivity(overallVad);
215 return processed;
216}
217
218} // 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:151
void enableEchoCancel(bool enabled) override
Set the status of echo cancellation.
Definition speex.cpp:99
SpeexAudioProcessor(AudioFormat format, unsigned frameSize)
Definition speex.cpp:46
std::shared_ptr< AudioFrame > getProcessed() override
Process and return a single AudioFrame.
Definition speex.cpp:164
void enableNoiseSuppression(bool enabled) override
Set the status of noise suppression includes de-reverb, de-noise, high pass filter,...
Definition speex.cpp:123
void enableAutomaticGainControl(bool enabled) override
Set the status of automatic gain control.
Definition speex.cpp:138
#define JAMI_DBG(...)
Definition logger.h:228
#define JAMI_INFO(...)
Definition logger.h:227
void emitSignal(Args... args)
Definition jami_signal.h:64
AudioFormat audioFormatToSampleFormat(AudioFormat format)
Definition speex.cpp:41
Structure to hold sample rate and channel number associated with audio data.