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 "oggencoder.h"
24 :
25 : #ifdef SYSTEM_OPUSENC
26 : #include <opus/opusenc.h>
27 : #else
28 : #include "opusenc.h"
29 : #endif
30 :
31 : #include <algorithm>
32 : #include <climits>
33 :
34 : #include "global/io/iodevice.h"
35 :
36 : #include "log.h"
37 :
38 : using namespace muse::audio;
39 : using namespace muse::audio::encode;
40 :
41 0 : OggEncoder::OggEncoder(const SoundTrackFormat& format, io::IODevice& dstDevice)
42 0 : : AbstractAudioEncoder(format), m_dstDevice{&dstDevice}
43 : {
44 0 : DO_ASSERT(m_dstDevice);
45 0 : }
46 :
47 0 : OggEncoder::~OggEncoder() noexcept
48 : {
49 0 : if (m_opusEncoder) {
50 0 : ope_encoder_destroy(m_opusEncoder);
51 : }
52 0 : }
53 :
54 0 : bool OggEncoder::begin(const samples_t)
55 : {
56 0 : OggOpusComments* comments = ope_comments_create();
57 0 : if (!comments) {
58 0 : return false;
59 : }
60 :
61 0 : int error = 0;
62 :
63 0 : const OpusEncCallbacks callbacks{
64 0 : [] (void* userData, const unsigned char* ptr, opus_int32 len) -> int {
65 0 : auto* dstDevice = static_cast<io::IODevice*>(userData);
66 0 : IF_ASSERT_FAILED(dstDevice) {
67 0 : return 1;
68 : }
69 :
70 0 : const auto numBytesToWrite = static_cast<std::size_t>(len);
71 0 : if (dstDevice->write(ptr, numBytesToWrite) != numBytesToWrite) {
72 0 : return 1;
73 : }
74 :
75 0 : return 0;
76 : },
77 0 : [] (void*) -> int { return 0; }
78 : };
79 :
80 0 : m_opusEncoder = ope_encoder_create_callbacks(&callbacks, m_dstDevice, comments, m_format.outputSpec.sampleRate,
81 0 : m_format.outputSpec.audioChannelCount, 0, &error);
82 0 : ope_comments_destroy(comments);
83 :
84 0 : if (error != OPE_OK || !m_opusEncoder) {
85 0 : if (m_opusEncoder) {
86 0 : ope_encoder_destroy(m_opusEncoder);
87 0 : m_opusEncoder = nullptr;
88 : }
89 0 : return false;
90 : }
91 :
92 0 : ope_encoder_ctl(m_opusEncoder, OPUS_SET_BITRATE(m_format.bitRate * 1000));
93 :
94 0 : return true;
95 : }
96 :
97 0 : size_t OggEncoder::encode(const samples_t samplesPerChannel, const float* input)
98 : {
99 0 : IF_ASSERT_FAILED(m_opusEncoder) {
100 0 : return 0;
101 : }
102 :
103 0 : if (samplesPerChannel == 0) {
104 0 : return 0;
105 : }
106 :
107 0 : const int n = static_cast<int>(std::min(samplesPerChannel, static_cast<samples_t>(INT_MAX)));
108 0 : const int code = ope_encoder_write_float(m_opusEncoder, input, n);
109 :
110 0 : return code == OPE_OK ? static_cast<size_t>(n) : 0;
111 : }
112 :
113 0 : size_t OggEncoder::end()
114 : {
115 0 : IF_ASSERT_FAILED(m_opusEncoder) {
116 0 : return 0;
117 : }
118 :
119 0 : ope_encoder_drain(m_opusEncoder);
120 :
121 0 : return 0;
122 : }
|