LCOV - code coverage report
Current view: top level - src/media/audio - audiolayer.cpp (source / functions) Hit Total Coverage
Test: jami-coverage-filtered.info Lines: 51 174 29.3 %
Date: 2024-04-19 19:18:04 Functions: 13 34 38.2 %

          Line data    Source code
       1             : /*
       2             :  *  Copyright (C) 2004-2024 Savoir-faire Linux Inc.
       3             :  *
       4             :  *  Author: Emmanuel Milou <emmanuel.milou@savoirfairelinux.com>
       5             :  *  Author: Alexandre Savard <alexandre.savard@savoirfairelinux.com>
       6             :  *
       7             :  *  This program is free software; you can redistribute it and/or modify
       8             :  *  it under the terms of the GNU General Public License as published by
       9             :  *  the Free Software Foundation; either version 3 of the License, or
      10             :  *  (at your option) any later version.
      11             :  *
      12             :  *  This program is distributed in the hope that it will be useful,
      13             :  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
      14             :  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      15             :  *  GNU General Public License for more details.
      16             :  *
      17             :  *  You should have received a copy of the GNU General Public License
      18             :  *  along with this program; if not, write to the Free Software
      19             :  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
      20             :  */
      21             : 
      22             : #include "audiolayer.h"
      23             : #include "logger.h"
      24             : #include "manager.h"
      25             : #include "audio/ringbufferpool.h"
      26             : #include "audio/resampler.h"
      27             : #include "tonecontrol.h"
      28             : #include "client/ring_signal.h"
      29             : 
      30             : #include "audio-processing/null_audio_processor.h"
      31             : #include "tracepoint.h"
      32             : #if HAVE_WEBRTC_AP
      33             : #include "audio-processing/webrtc.h"
      34             : #endif
      35             : #if HAVE_SPEEXDSP
      36             : #include "audio-processing/speex.h"
      37             : #endif
      38             : 
      39             : #include <ctime>
      40             : #include <algorithm>
      41             : 
      42             : namespace jami {
      43             : 
      44          34 : AudioLayer::AudioLayer(const AudioPreference& pref)
      45          68 :     : isCaptureMuted_(pref.getCaptureMuted())
      46          34 :     , isPlaybackMuted_(pref.getPlaybackMuted())
      47          68 :     , captureGain_(pref.getVolumemic())
      48          34 :     , playbackGain_(pref.getVolumespkr())
      49          34 :     , pref_(pref)
      50          34 :     , mainRingBuffer_(
      51          34 :           Manager::instance().getRingBufferPool().getRingBuffer(RingBufferPool::DEFAULT_ID))
      52          34 :     , audioFormat_(Manager::instance().getRingBufferPool().getInternalAudioFormat())
      53          34 :     , audioInputFormat_(Manager::instance().getRingBufferPool().getInternalAudioFormat())
      54          34 :     , urgentRingBuffer_("urgentRingBuffer_id", SIZEBUF, audioFormat_)
      55          34 :     , resampler_(new Resampler)
      56         204 :     , lastNotificationTime_()
      57             : {
      58          34 :     urgentRingBuffer_.createReadOffset(RingBufferPool::DEFAULT_ID);
      59             : 
      60         102 :     JAMI_LOG("[audiolayer] AGC: {:d}, noiseReduce: {:s}, VAD: {:d}, echoCancel: {:s}, audioProcessor: {:s}",
      61             :               pref_.isAGCEnabled(),
      62             :               pref.getNoiseReduce(),
      63             :               pref.getVadEnabled(),
      64             :               pref.getEchoCanceller(),
      65             :               pref.getAudioProcessor());
      66          34 : }
      67             : 
      68          34 : AudioLayer::~AudioLayer() {}
      69             : 
      70             : void
      71         100 : AudioLayer::hardwareFormatAvailable(AudioFormat playback, size_t bufSize)
      72             : {
      73         300 :     JAMI_LOG("Hardware audio format available : {:s} {}", playback.toString(), bufSize);
      74         100 :     audioFormat_ = Manager::instance().hardwareAudioFormatChanged(playback);
      75         100 :     audioInputFormat_.sampleFormat = audioFormat_.sampleFormat;
      76         100 :     urgentRingBuffer_.setFormat(audioFormat_);
      77         100 :     nativeFrameSize_ = bufSize;
      78         100 : }
      79             : 
      80             : void
      81           0 : AudioLayer::hardwareInputFormatAvailable(AudioFormat capture)
      82             : {
      83           0 :     JAMI_LOG("Hardware input audio format available : {:s}", capture.toString());
      84           0 : }
      85             : 
      86             : void
      87           0 : AudioLayer::devicesChanged()
      88             : {
      89           0 :     emitSignal<libjami::AudioSignal::DeviceEvent>();
      90           0 : }
      91             : 
      92             : void
      93           0 : AudioLayer::flushMain()
      94             : {
      95           0 :     Manager::instance().getRingBufferPool().flushAllBuffers();
      96           0 : }
      97             : 
      98             : void
      99         386 : AudioLayer::flushUrgent()
     100             : {
     101         386 :     urgentRingBuffer_.flushAll();
     102         386 : }
     103             : 
     104             : void
     105           0 : AudioLayer::flush()
     106             : {
     107           0 :     Manager::instance().getRingBufferPool().flushAllBuffers();
     108           0 :     urgentRingBuffer_.flushAll();
     109           0 : }
     110             : 
     111             : void
     112         211 : AudioLayer::playbackChanged(bool started)
     113             : {
     114         211 :     playbackStarted_ = started;
     115         211 : }
     116             : 
     117             : void
     118         211 : AudioLayer::recordChanged(bool started)
     119             : {
     120         211 :     std::lock_guard lock(audioProcessorMutex);
     121         211 :     if (started) {
     122             :         // create audio processor
     123           0 :         createAudioProcessor();
     124             :     } else {
     125             :         // destroy audio processor
     126         211 :         destroyAudioProcessor();
     127             :     }
     128         211 :     recordStarted_ = started;
     129         211 : }
     130             : 
     131             : // helper function
     132             : static inline bool
     133           0 : shouldUseAudioProcessorEchoCancel(bool hasNativeAEC, const std::string& echoCancellerPref)
     134             : {
     135             :     return
     136             :         // user doesn't care which and there is not a system AEC
     137           0 :         (echoCancellerPref == "auto" && !hasNativeAEC)
     138             :         // user specifically wants audioProcessor
     139           0 :         or (echoCancellerPref == "audioProcessor");
     140             : }
     141             : 
     142             : // helper function
     143             : static inline bool
     144           0 : shouldUseAudioProcessorNoiseSuppression(bool hasNativeNS, const std::string& noiseSuppressionPref)
     145             : {
     146             :     return
     147             :         // user doesn't care which and there is no system noise suppression
     148           0 :         (noiseSuppressionPref == "auto" && !hasNativeNS)
     149             :         // user specifically wants audioProcessor
     150           0 :         or (noiseSuppressionPref == "audioProcessor");
     151             : }
     152             : 
     153             : void
     154          31 : AudioLayer::setHasNativeAEC(bool hasNativeAEC)
     155             : {
     156          31 :     JAMI_INFO("[audiolayer] setHasNativeAEC: %d", hasNativeAEC);
     157          31 :     std::lock_guard lock(audioProcessorMutex);
     158          31 :     hasNativeAEC_ = hasNativeAEC;
     159             :     // if we have a current audio processor, tell it to enable/disable its own AEC
     160          31 :     if (audioProcessor) {
     161           0 :         audioProcessor->enableEchoCancel(
     162           0 :             shouldUseAudioProcessorEchoCancel(hasNativeAEC, pref_.getEchoCanceller()));
     163             :     }
     164          31 : }
     165             : 
     166             : void
     167          34 : AudioLayer::setHasNativeNS(bool hasNativeNS)
     168             : {
     169          34 :     JAMI_INFO("[audiolayer] setHasNativeNS: %d", hasNativeNS);
     170          34 :     std::lock_guard lock(audioProcessorMutex);
     171          34 :     hasNativeNS_ = hasNativeNS;
     172             :     // if we have a current audio processor, tell it to enable/disable its own noise suppression
     173          34 :     if (audioProcessor) {
     174           0 :         audioProcessor->enableNoiseSuppression(
     175           0 :             shouldUseAudioProcessorNoiseSuppression(hasNativeNS, pref_.getNoiseReduce()));
     176             :     }
     177          34 : }
     178             : 
     179             : // must acquire lock beforehand
     180             : void
     181           0 : AudioLayer::createAudioProcessor()
     182             : {
     183           0 :     auto nb_channels = std::max(audioFormat_.nb_channels, audioInputFormat_.nb_channels);
     184           0 :     auto sample_rate = std::max(audioFormat_.sample_rate, audioInputFormat_.sample_rate);
     185             : 
     186           0 :     sample_rate = std::clamp(sample_rate, 16000u, 48000u);
     187             : 
     188           0 :     AudioFormat formatForProcessor {sample_rate, nb_channels};
     189             : 
     190             :     unsigned int frame_size;
     191           0 :     if (pref_.getAudioProcessor() == "speex") {
     192             :         // TODO: maybe force this to be equivalent to 20ms? as expected by speex
     193           0 :         frame_size = sample_rate / 50u;
     194             :     } else {
     195           0 :         frame_size = sample_rate / 100u;
     196             :     }
     197             : 
     198           0 :     JAMI_WARNING("Input {}", audioInputFormat_.toString());
     199           0 :     JAMI_WARNING("Output {}", audioFormat_.toString());
     200           0 :     JAMI_WARNING("Starting audio processor with: [{} Hz, {} channels, {} samples/frame]",
     201             :               sample_rate,
     202             :               nb_channels,
     203             :               frame_size);
     204             : 
     205           0 :     if (pref_.getAudioProcessor() == "webrtc") {
     206             : #if HAVE_WEBRTC_AP
     207           0 :         JAMI_WARN("[audiolayer] using WebRTCAudioProcessor");
     208           0 :         audioProcessor.reset(new WebRTCAudioProcessor(formatForProcessor, frame_size));
     209             : #else
     210             :         JAMI_ERR("[audiolayer] audioProcessor preference is webrtc, but library not linked! "
     211             :                  "using null AudioProcessor instead");
     212             :         audioProcessor.reset();
     213             : #endif
     214           0 :     } else if (pref_.getAudioProcessor() == "speex") {
     215             : #if HAVE_SPEEXDSP
     216           0 :         JAMI_WARN("[audiolayer] using SpeexAudioProcessor");
     217           0 :         audioProcessor.reset(new SpeexAudioProcessor(formatForProcessor, frame_size));
     218             : #else
     219             :         JAMI_ERR("[audiolayer] audioProcessor preference is speex, but library not linked! "
     220             :                  "using null AudioProcessor instead");
     221             :         audioProcessor.reset();
     222             : #endif
     223           0 :     } else if (pref_.getAudioProcessor() == "null") {
     224           0 :         JAMI_WARN("[audiolayer] using null AudioProcessor");
     225           0 :         audioProcessor.reset();
     226             :     } else {
     227           0 :         JAMI_ERR("[audiolayer] audioProcessor preference not recognized, using null AudioProcessor "
     228             :                  "instead");
     229           0 :         audioProcessor.reset();
     230             :     }
     231             : 
     232           0 :     if (audioProcessor) {
     233           0 :         audioProcessor->enableNoiseSuppression(
     234           0 :             shouldUseAudioProcessorNoiseSuppression(hasNativeNS_, pref_.getNoiseReduce()));
     235             : 
     236           0 :         audioProcessor->enableAutomaticGainControl(pref_.isAGCEnabled());
     237             : 
     238           0 :         audioProcessor->enableEchoCancel(
     239           0 :             shouldUseAudioProcessorEchoCancel(hasNativeAEC_, pref_.getEchoCanceller()));
     240             : 
     241           0 :         audioProcessor->enableVoiceActivityDetection(pref_.getVadEnabled());
     242             :     }
     243           0 : }
     244             : 
     245             : // must acquire lock beforehand
     246             : void
     247         211 : AudioLayer::destroyAudioProcessor()
     248             : {
     249             :     // delete it
     250         211 :     audioProcessor.reset();
     251         211 : }
     252             : 
     253             : void
     254           0 : AudioLayer::putUrgent(std::shared_ptr<AudioFrame> buffer)
     255             : {
     256           0 :     urgentRingBuffer_.put(std::move(buffer));
     257           0 : }
     258             : 
     259             : // Notify (with a beep) an incoming call when there is already a call in progress
     260             : void
     261           0 : AudioLayer::notifyIncomingCall()
     262             : {
     263           0 :     if (not playIncomingCallBeep_)
     264           0 :         return;
     265             : 
     266           0 :     auto now = std::chrono::system_clock::now();
     267             : 
     268             :     // Notify maximum once every 5 seconds
     269           0 :     if (now < lastNotificationTime_ + std::chrono::seconds(5))
     270           0 :         return;
     271             : 
     272           0 :     lastNotificationTime_ = now;
     273             : 
     274           0 :     Tone tone("440/160", getSampleRate(), audioFormat_.sampleFormat);
     275           0 :     size_t nbSample = tone.getSize();
     276             : 
     277             :     /* Put the data in the urgent ring buffer */
     278           0 :     urgentRingBuffer_.flushAll();
     279           0 :     urgentRingBuffer_.put(tone.getNext(nbSample));
     280           0 : }
     281             : 
     282             : std::shared_ptr<AudioFrame>
     283           0 : AudioLayer::getToRing(AudioFormat format, size_t writableSamples)
     284             : {
     285           0 :     if (auto fileToPlay = Manager::instance().getTelephoneFile()) {
     286           0 :         auto fileformat = fileToPlay->getFormat();
     287           0 :         bool resample = format != fileformat;
     288             : 
     289           0 :         size_t readableSamples = resample ? (rational<size_t>(writableSamples, format.sample_rate)
     290           0 :                                              * (size_t) fileformat.sample_rate)
     291           0 :                                                 .real<size_t>()
     292           0 :                                           : writableSamples;
     293             : 
     294           0 :         return resampler_->resample(fileToPlay->getNext(readableSamples, isRingtoneMuted_), format);
     295           0 :     }
     296           0 :     return {};
     297             : }
     298             : 
     299             : std::shared_ptr<AudioFrame>
     300           0 : AudioLayer::getToPlay(AudioFormat format, size_t writableSamples)
     301             : {
     302           0 :     notifyIncomingCall();
     303           0 :     auto& bufferPool = Manager::instance().getRingBufferPool();
     304             : 
     305           0 :     if (not playbackQueue_)
     306           0 :         playbackQueue_.reset(new AudioFrameResizer(format, writableSamples));
     307             :     else
     308           0 :         playbackQueue_->setFrameSize(writableSamples);
     309             : 
     310           0 :     std::shared_ptr<AudioFrame> playbackBuf {};
     311           0 :     while (!(playbackBuf = playbackQueue_->dequeue())) {
     312           0 :         std::shared_ptr<AudioFrame> resampled;
     313             : 
     314           0 :         if (auto urgentSamples = urgentRingBuffer_.get(RingBufferPool::DEFAULT_ID)) {
     315           0 :             bufferPool.discard(1, RingBufferPool::DEFAULT_ID);
     316           0 :             resampled = resampler_->resample(std::move(urgentSamples), format);
     317           0 :         } else if (auto toneToPlay = Manager::instance().getTelephoneTone()) {
     318           0 :             resampled = resampler_->resample(toneToPlay->getNext(), format);
     319           0 :         } else if (auto buf = bufferPool.getData(RingBufferPool::DEFAULT_ID)) {
     320           0 :             resampled = resampler_->resample(std::move(buf), format);
     321             :         } else {
     322           0 :             std::lock_guard lock(audioProcessorMutex);
     323           0 :             if (audioProcessor) {
     324           0 :                 auto silence = std::make_shared<AudioFrame>(format, writableSamples);
     325           0 :                 libav_utils::fillWithSilence(silence->pointer());
     326           0 :                 audioProcessor->putPlayback(silence);
     327           0 :             }
     328           0 :             break;
     329           0 :         }
     330             : 
     331           0 :         if (resampled) {
     332           0 :             std::lock_guard lock(audioProcessorMutex);
     333           0 :             if (audioProcessor) {
     334           0 :                 audioProcessor->putPlayback(resampled);
     335             :             }
     336           0 :             playbackQueue_->enqueue(std::move(resampled));
     337           0 :         } else
     338           0 :             break;
     339           0 :     }
     340             : 
     341             :     jami_tracepoint(audio_layer_get_to_play_end);
     342             : 
     343           0 :     return playbackBuf;
     344           0 : }
     345             : 
     346             : void
     347           0 : AudioLayer::putRecorded(std::shared_ptr<AudioFrame>&& frame)
     348             : {
     349           0 :     std::lock_guard lock(audioProcessorMutex);
     350           0 :     if (audioProcessor && playbackStarted_ && recordStarted_) {
     351           0 :         audioProcessor->putRecorded(std::move(frame));
     352           0 :         while (auto rec = audioProcessor->getProcessed()) {
     353           0 :             mainRingBuffer_->put(std::move(rec));
     354           0 :         }
     355             :     } else {
     356           0 :         mainRingBuffer_->put(std::move(frame));
     357             :     }
     358             : 
     359             :     jami_tracepoint(audio_layer_put_recorded_end, );
     360           0 : }
     361             : 
     362             : } // namespace jami

Generated by: LCOV version 1.14