LCOV - code coverage report
Current view: top level - muse/framework/audio/engine/internal/export - mp3encoder.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 0.0 % 53 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 "mp3encoder.h"
      24              : 
      25              : #include <cmath>
      26              : 
      27              : #ifdef SYSTEM_LAME
      28              : #  include <lame/lame.h>
      29              : #else
      30              : #  include "lame.h"
      31              : #endif
      32              : 
      33              : #include "global/io/iodevice.h"
      34              : 
      35              : #include "log.h"
      36              : 
      37              : using namespace muse::audio;
      38              : using namespace muse::audio::encode;
      39              : 
      40              : struct LameHandler
      41              : {
      42            0 :     explicit LameHandler(const SoundTrackFormat& format)
      43            0 :     {
      44            0 :         DO_ASSERT(flags);
      45              : 
      46            0 :         lame_set_errorf(flags, [](const char* msg, va_list /*ap*/) {
      47            0 :             LOGE() << msg;
      48            0 :         });
      49            0 :         lame_set_debugf(flags, [](const char* msg, va_list /*ap*/) {
      50            0 :             LOGD() << msg;
      51            0 :         });
      52            0 :         lame_set_msgf(flags, [](const char* msg, va_list /*ap*/) {
      53            0 :             LOGI() << msg;
      54            0 :         });
      55              : 
      56            0 :         lame_set_num_channels(flags, format.outputSpec.audioChannelCount);
      57            0 :         lame_set_in_samplerate(flags, format.outputSpec.sampleRate);
      58            0 :         lame_set_brate(flags, format.bitRate);
      59            0 :     }
      60              : 
      61            0 :     ~LameHandler() noexcept
      62              :     {
      63            0 :         lame_close(flags);
      64            0 :     }
      65              : 
      66            0 :     bool init()
      67              :     {
      68            0 :         return lame_init_params(flags) >= 0;
      69              :     }
      70              : 
      71              :     lame_global_flags* flags = lame_init();
      72              : };
      73              : 
      74            0 : Mp3Encoder::Mp3Encoder(const SoundTrackFormat& format, io::IODevice& dstDevice)
      75            0 :     : AbstractAudioEncoder(format), m_handler{std::make_unique<LameHandler>(format)}, m_dstDevice{&dstDevice}
      76              : {
      77            0 :     DO_ASSERT(m_dstDevice);
      78            0 : }
      79              : 
      80            0 : Mp3Encoder::~Mp3Encoder() noexcept = default;
      81              : 
      82            0 : bool Mp3Encoder::begin(const samples_t /*totalSamplesNumber*/)
      83              : {
      84              :     // LAME (lame.h): mp3buf for one encode call — worst case ≈ 1.25 * num_samples_per_channel + 7200 bytes;
      85              :     // flush needs at least 7200. Same buffer is used for encode + lame_encode_flush, hence margin + ceil.
      86            0 :     const samples_t n = m_format.outputSpec.samplesPerChannel > 0 ? m_format.outputSpec.samplesPerChannel : 4096;
      87            0 :     const double sz = 7200.0 + 1.25 * static_cast<double>(n) + 7200.0;
      88            0 :     m_outputBuffer.resize(static_cast<size_t>(std::ceil(sz)) + 512);
      89              : 
      90            0 :     if (!m_handler->init()) {
      91            0 :         return false;
      92              :     }
      93              : 
      94            0 :     return true;
      95              : }
      96              : 
      97            0 : size_t Mp3Encoder::encode(const samples_t samplesPerChannel, const float* input)
      98              : {
      99            0 :     if (samplesPerChannel > m_format.outputSpec.samplesPerChannel) {
     100            0 :         LOGE() << "Chunk size exceeds buffer capacity";
     101            0 :         return 0;
     102              :     }
     103              : 
     104            0 :     const int encodedBytes = lame_encode_buffer_interleaved_ieee_float(m_handler->flags, input, static_cast<int>(samplesPerChannel),
     105              :                                                                        m_outputBuffer.data(),
     106            0 :                                                                        static_cast<int>(m_outputBuffer.size()));
     107              : 
     108            0 :     if (encodedBytes < 0) {
     109            0 :         LOGE() << "LAME encoder failed: " << encodedBytes;
     110            0 :         return 0;
     111              :     }
     112              : 
     113            0 :     if (encodedBytes > 0) {
     114            0 :         const size_t written = m_dstDevice->write(m_outputBuffer.data(), static_cast<size_t>(encodedBytes));
     115            0 :         if (written != static_cast<size_t>(encodedBytes)) {
     116            0 :             return 0;
     117              :         }
     118              :     }
     119              : 
     120            0 :     return samplesPerChannel;
     121              : }
     122              : 
     123            0 : size_t Mp3Encoder::end()
     124              : {
     125            0 :     const int encodedBytes = lame_encode_flush(m_handler->flags,
     126              :                                                m_outputBuffer.data(),
     127            0 :                                                static_cast<int>(m_outputBuffer.size()));
     128            0 :     if (encodedBytes > 0) {
     129            0 :         m_dstDevice->write(m_outputBuffer.data(), static_cast<size_t>(encodedBytes));
     130              :     }
     131              : 
     132            0 :     return 0;
     133              : }
        

Generated by: LCOV version 2.0-1