LCOV - code coverage report
Current view: top level - muse/framework/audio/engine/internal - audioengine.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 0.0 % 109 0
Test Date: 2026-11-05 04:07:02

            Line data    Source code
       1              : /*
       2              :  * SPDX-License-Identifier: GPL-3.0-only
       3              :  * MuseScore-CLA-applies
       4              :  *
       5              :  * MuseScore Studio
       6              :  * Music Composition & Notation
       7              :  *
       8              :  * Copyright (C) 2021 MuseScore Limited and others
       9              :  *
      10              :  * This program is free software: you can redistribute it and/or modify
      11              :  * it under the terms of the GNU General Public License version 3 as
      12              :  * published by the Free Software Foundation.
      13              :  *
      14              :  * This program is distributed in the hope that it will be useful,
      15              :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      16              :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      17              :  * GNU General Public License for more details.
      18              :  *
      19              :  * You should have received a copy of the GNU General Public License
      20              :  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
      21              :  */
      22              : 
      23              : #include "audioengine.h"
      24              : 
      25              : #include "global/defer.h"
      26              : 
      27              : #include "audio/common/audiosanitizer.h"
      28              : #include "audio/common/audioerrors.h"
      29              : 
      30              : #include "audiocontext.h"
      31              : 
      32              : #include "log.h"
      33              : 
      34              : using namespace muse;
      35              : using namespace muse::audio;
      36              : using namespace muse::audio::engine;
      37              : 
      38              : static constexpr int MAX_SUPPORTED_AUDIO_CHANNELS = 2;
      39              : 
      40            0 : AudioEngine::AudioEngine()
      41              : {
      42            0 : }
      43              : 
      44            0 : AudioEngine::~AudioEngine()
      45              : {
      46              :     ONLY_AUDIO_MAIN_OR_ENGINE_THREAD;
      47            0 : }
      48              : 
      49            0 : Ret AudioEngine::init(const OutputSpec& outputSpec)
      50              : {
      51              :     ONLY_AUDIO_ENGINE_THREAD;
      52              : 
      53            0 :     if (m_inited) {
      54            0 :         return make_ret(Ret::Code::Ok);
      55              :     }
      56              : 
      57            0 :     IF_ASSERT_FAILED(outputSpec.audioChannelCount <= MAX_SUPPORTED_AUDIO_CHANNELS) {
      58            0 :         return make_ret(Ret::Code::InternalError);
      59              :     }
      60              : 
      61            0 :     LOGI() << "[OutputSpec] sampleRate: " << outputSpec.sampleRate
      62            0 :            << ", samplesPerChannel: " << outputSpec.samplesPerChannel
      63            0 :            << ", audioChannelCount: " << outputSpec.audioChannelCount;
      64              : 
      65            0 :     m_outputSpec = outputSpec;
      66            0 :     m_operationType = OperationType::NoOperation;
      67              : 
      68            0 :     m_mixer = std::make_shared<MixerNode>();
      69            0 :     m_mixer->setOutputSpec(outputSpec);
      70              : 
      71            0 :     m_inited = true;
      72              : 
      73            0 :     return make_ret(Ret::Code::Ok);
      74              : }
      75              : 
      76            0 : void AudioEngine::deinit()
      77              : {
      78              :     ONLY_AUDIO_ENGINE_THREAD;
      79            0 :     if (m_inited) {
      80            0 :         m_inited = false;
      81              : 
      82            0 :         for (auto& p : m_contexts) {
      83            0 :             p.second->deinit();
      84              :         }
      85            0 :         m_contexts.clear();
      86              :     }
      87            0 : }
      88              : 
      89            0 : RetVal<std::shared_ptr<IAudioContext> > AudioEngine::addAudioContext(const AudioCtxId& ctxId)
      90              : {
      91              :     ONLY_AUDIO_ENGINE_THREAD;
      92              : 
      93              :     using RetType = RetVal<std::shared_ptr<IAudioContext> >;
      94              : 
      95            0 :     if (m_contexts.find(ctxId) != m_contexts.end()) {
      96            0 :         return RetType::make_ret(Err::AudioContextAlreadyExists);
      97              :     }
      98              : 
      99            0 :     auto ctx = std::make_shared<AudioContext>(ctxId, this);
     100            0 :     Ret ret = ctx->init();
     101            0 :     if (!ret) {
     102            0 :         return RetType::make_ret(ret);
     103              :     }
     104              : 
     105            0 :     ctx->setOutputSpec(m_outputSpec);
     106            0 :     ctx->setMode(ProcessMode::Idle);
     107              : 
     108            0 :     m_contexts[ctxId] = ctx;
     109              : 
     110            0 :     ctx->connect(m_mixer);
     111              : 
     112            0 :     return RetType::make_ok(ctx);
     113            0 : }
     114              : 
     115            0 : std::shared_ptr<IAudioContext> AudioEngine::context(const AudioCtxId& ctxId) const
     116              : {
     117            0 :     auto it = m_contexts.find(ctxId);
     118            0 :     if (it != m_contexts.end()) {
     119            0 :         return it->second;
     120              :     }
     121            0 :     return nullptr;
     122              : }
     123              : 
     124            0 : void AudioEngine::destroyContext(const AudioCtxId& ctxId)
     125              : {
     126            0 :     auto it = m_contexts.find(ctxId);
     127            0 :     if (it != m_contexts.end()) {
     128            0 :         auto& ctx = it->second;
     129            0 :         ctx->deinit();
     130            0 :         ctx->disconnect(m_mixer);
     131            0 :         m_contexts.erase(it);
     132              :     }
     133            0 : }
     134              : 
     135            0 : void AudioEngine::setOutputSpec(const OutputSpec& outputSpec)
     136              : {
     137              :     ONLY_AUDIO_ENGINE_THREAD;
     138              : 
     139            0 :     IF_ASSERT_FAILED(outputSpec.audioChannelCount <= MAX_SUPPORTED_AUDIO_CHANNELS) {
     140            0 :         return;
     141              :     }
     142              : 
     143            0 :     if (m_outputSpec == outputSpec) {
     144            0 :         return;
     145              :     }
     146              : 
     147            0 :     LOGI() << "[OutputSpec] sampleRate: " << outputSpec.sampleRate
     148            0 :            << ", samplesPerChannel: " << outputSpec.samplesPerChannel
     149            0 :            << ", audioChannelCount: " << outputSpec.audioChannelCount;
     150              : 
     151            0 :     m_outputSpec = outputSpec;
     152              : 
     153            0 :     if (m_mixer) {
     154            0 :         m_mixer->setOutputSpec(outputSpec);
     155              :     }
     156              : 
     157            0 :     m_outputSpecChanged.send(outputSpec);
     158              : }
     159              : 
     160            0 : OutputSpec AudioEngine::outputSpec() const
     161              : {
     162            0 :     return m_outputSpec;
     163              : }
     164              : 
     165            0 : async::Channel<OutputSpec> AudioEngine::outputSpecChanged() const
     166              : {
     167            0 :     return m_outputSpecChanged;
     168              : }
     169              : 
     170            0 : void AudioEngine::execOperation(OperationType type, const Operation& func)
     171              : {
     172              :     // wait end of processing
     173            0 :     while (m_processing) {
     174            0 :         LOGD() << "wait end of processing";
     175              :         using namespace std::chrono_literals;
     176            0 :         std::this_thread::sleep_for(1ms);
     177              :     }
     178              : 
     179            0 :     OperationType oldOperationType = m_operationType;
     180              : 
     181            0 :     m_operationType = type;
     182            0 :     AudioSanitizer::setOperationType(type);
     183              : 
     184            0 :     if (m_operationType == OperationType::QuickOperation) {
     185            0 :         m_quickOperationWaitMutex.lock();
     186              :     }
     187              : 
     188            0 :     func();
     189              : 
     190            0 :     if (m_operationType == OperationType::QuickOperation) {
     191            0 :         m_quickOperationWaitMutex.unlock();
     192              :     }
     193              : 
     194            0 :     m_operationType = oldOperationType;
     195            0 :     AudioSanitizer::setOperationType(oldOperationType);
     196            0 : }
     197              : 
     198            0 : samples_t AudioEngine::fillSilent(float* buffer, samples_t samplesPerChannel)
     199              : {
     200            0 :     std::memset(buffer, 0, samplesPerChannel * sizeof(float) * m_outputSpec.audioChannelCount);
     201            0 :     return 0;
     202              : }
     203              : 
     204            0 : samples_t AudioEngine::process(float* buffer, samples_t samplesPerChannel)
     205              : {
     206            0 :     m_processing = true;
     207            0 :     DEFER {
     208            0 :         m_processing = false;
     209            0 :     };
     210              : 
     211            0 :     fillSilent(buffer, samplesPerChannel); // clear buffer
     212              : 
     213            0 :     if (!m_inited) {
     214            0 :         return 0;
     215              :     }
     216              : 
     217              :     // check current operation
     218            0 :     switch (m_operationType) {
     219            0 :     case OperationType::Undefined: {
     220            0 :         UNREACHABLE;
     221              :         return 0;
     222              :     }
     223            0 :     case OperationType::NoOperation: {
     224              :         // normal playing
     225            0 :         m_mixer->process(buffer, samplesPerChannel);
     226            0 :         return samplesPerChannel;
     227              :     }
     228            0 :     case OperationType::QuickOperation: {
     229              :         // wait
     230            0 :         LOGD() << "wait end of quick operation";
     231            0 :         std::scoped_lock<std::mutex> lock(m_quickOperationWaitMutex);
     232            0 :         m_mixer->process(buffer, samplesPerChannel);
     233            0 :         return samplesPerChannel;
     234            0 :     }
     235            0 :     case OperationType::LongOperation: {
     236            0 :         return 0;
     237              :     }
     238              :     }
     239              : 
     240            0 :     return 0;
     241            0 : }
        

Generated by: LCOV version 2.0-1