~aleteoryx/muditaos

ref: 8ac720610b72fddd3fc604aa12b37f5f2a93e5be muditaos/module-audio/Audio/decoder/decoderMP3.cpp -rw-r--r-- 8.2 KiB
8ac72061 — Piotr Tański [EGD-7916] First name typo fixed for French 4 years ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#define MINIMP3_ONLY_MP3
#define MINIMP3_NO_SIMD
#define MINIMP3_IMPLEMENTATION

#include "decoderMP3.hpp"

#include <array>
#include <cstdio>

namespace audio
{

    decoderMP3::decoderMP3(const char *fileName) : Decoder(fileName)
    {

        if (fileSize == 0) {
            return;
        }

        mp3d = std::make_unique<mp3dec_t>();

        mp3dec_init(mp3d.get());

        if (!find_first_valid_frame()) {
            return;
        }

        isInitialized = true;
    }

    void decoderMP3::setPosition(float pos)
    {
        decoderNotFirstRun = false;

        std::fseek(fd, (pos * fileSize) + firstValidFrameFileOffset, SEEK_SET);

        // TODO: M.P Currently calculating MP3 position is unsupported, in general seeking is supported though.
        // position += (float) ((float) (samplesToReadChann / chanNumber) / (float) sampleRate);
    }

    bool decoderMP3::find_first_valid_frame()
    {

        mp3dec_frame_info_t info = {};

        int32_t bytesAvailable = DECODER_BUFFER_SIZE;
        uint32_t bufferIndex   = 0;

        auto decBuffer = std::make_unique<uint8_t[]>(DECODER_BUFFER_SIZE);

        std::rewind(fd);

        if (std::fread(decBuffer.get(), 1, DECODER_BUFFER_SIZE, fd) == 0) {
            return false;
        }

        for (;;) {
            // refill buffer if necessary(only if over 87,5% of bytes are consumed)
            if (bufferIndex > (DECODER_BUFFER_SIZE - (DECODER_BUFFER_SIZE / 8))) {
                memcpy(&decBuffer[0], &decBuffer[bufferIndex], bytesAvailable);
                uint32_t bytesRead =
                    std::fread(&decBuffer[bytesAvailable], 1, DECODER_BUFFER_SIZE - bytesAvailable, fd);

                if (bytesRead == 0) {
                    return false;
                }

                bytesAvailable += bytesRead;
                bufferIndex = 0;
            }

            for (;;) {
                uint32_t smpl =
                    mp3dec_decode_frame(mp3d.get(), &decBuffer[bufferIndex], bytesAvailable, nullptr, &info);
                bufferIndex += info.frame_bytes;
                bytesAvailable -= info.frame_bytes;

                // Valid frame
                if (smpl && info.frame_bytes) {

                    // Fill necessary parameters
                    samplesPerFrame           = smpl;
                    sampleRate                = info.hz;
                    chanNumber                = info.channels;
                    firstValidFrameByteSize   = (144 * info.bitrate_kbps * 1000 / info.hz);
                    firstValidFrameFileOffset = std::ftell(fd) - bytesAvailable - firstValidFrameByteSize;

                    std::rewind(fd);

                    return true;
                }
                // Decoder skipped ID3 or invalid data
                else if ((smpl == 0) && info.frame_bytes) {
                }
                // insufficient data
                else if ((info.frame_bytes == 0) && (smpl == 0)) {
                    break;
                }
            }
        }
    }

    uint32_t decoderMP3::get_frames_count()
    {

        int32_t bytesAvailable   = DECODER_BUFFER_SIZE;
        uint32_t frames_count    = 0;
        mp3dec_frame_info_t info = {};
        bool last_refill         = false;

        uint32_t bufferIndex = 0;
        auto decBuffer       = std::make_unique<uint8_t[]>(DECODER_BUFFER_SIZE);

        // Jump to the file beginning
        std::rewind(fd);

        /* Fill decBuffer */
        if (std::fread(decBuffer.get(), 1, DECODER_BUFFER_SIZE, fd) == 0) {
            return 0;
        }

    refill:

        // refill buffer if necessary(only if over 87,5% of bytes are consumed)
        if (bufferIndex > (DECODER_BUFFER_SIZE - (DECODER_BUFFER_SIZE / 8))) {
            memcpy(&decBuffer[0], &decBuffer[bufferIndex], bytesAvailable);
            uint32_t bytesRead =
                std::fread(decBuffer.get() + bytesAvailable, 1, DECODER_BUFFER_SIZE - bytesAvailable, fd);

            if (bytesRead != (DECODER_BUFFER_SIZE - bytesAvailable)) {
                last_refill = true;
            }

            bytesAvailable += bytesRead;
            bufferIndex = 0;
        }

        while (1) {

            uint32_t smpl = mp3dec_decode_frame(mp3d.get(), &decBuffer[bufferIndex], bytesAvailable, nullptr, &info);
            bufferIndex += info.frame_bytes;
            bytesAvailable -= info.frame_bytes;

            // Valid frame
            if (smpl && info.frame_bytes) {
                frames_count++;
            }

            // Decoder skipped ID3 or invalid data
            if ((smpl == 0) && info.frame_bytes) {
                frames_count++;
            }

            // Last frame
            if (last_refill && (info.frame_bytes == 0) && (smpl == 0)) {
                return frames_count;
            }

            // insufficient data
            if ((info.frame_bytes == 0) && (smpl == 0)) {
                goto refill;
            }
        }
    }

    uint32_t decoderMP3::decode(uint32_t samplesToRead, int16_t *pcmData)
    {
        mp3dec_frame_info_t info = {0, 0, 0, 0, 0, 0};

        if (!decoderNotFirstRun) {
            decoderBuffer    = std::make_unique<uint8_t[]>(DECODER_BUFFER_SIZE);
            pcmsamplesbuffer = std::make_unique<uint16_t[]>(pcmsamplesbuffer_size);

            // Fill decoderBuffer
            uint32_t bytesRead = std::fread(decoderBuffer.get(), 1, DECODER_BUFFER_SIZE, fd);

            if (bytesRead == 0) {
                return 0;
            }

            bytesAvailable   = DECODER_BUFFER_SIZE;
            decoderBufferIdx = 0;

            decoderNotFirstRun = true;
        }
        else if (!lastRefill) {
            bytesAvailable = DECODER_BUFFER_SIZE - decoderBufferIdx;
        }

        uint32_t samplesFetched = pcmsamplesbuffer_idx;

    refill:

        // refill buffer if necessary(only if over 87,5% of bytes consumed)
        if (!lastRefill && (decoderBufferIdx > (DECODER_BUFFER_SIZE - (DECODER_BUFFER_SIZE / 8)))) {
            memcpy(&decoderBuffer[0], &decoderBuffer[decoderBufferIdx], bytesAvailable);
            uint32_t bytesRead =
                std::fread(&decoderBuffer[bytesAvailable], 1, DECODER_BUFFER_SIZE - bytesAvailable, fd);

            if (bytesRead != (DECODER_BUFFER_SIZE - bytesAvailable)) {
                lastRefill = true;
            }

            bytesAvailable += bytesRead;
            decoderBufferIdx = 0;
        }

        uint32_t samplesToReadChann = samplesToRead;

        while (1) {
            uint32_t smpl = 0;
            if (samplesFetched < samplesToReadChann) {
                smpl = mp3dec_decode_frame(mp3d.get(),
                                           &decoderBuffer[decoderBufferIdx],
                                           bytesAvailable,
                                           (short *)&pcmsamplesbuffer[samplesFetched],
                                           &info);
            }

            bytesAvailable -= info.frame_bytes;
            decoderBufferIdx += info.frame_bytes;

            // Valid frame
            if (smpl && info.frame_bytes) {
                samplesFetched += smpl * info.channels;
            }

            if (samplesFetched >= samplesToReadChann) {
                break;
            }

            // Insufficient data
            if (!lastRefill && (info.frame_bytes == 0) && (smpl == 0)) {
                goto refill;
            }

            if (lastRefill && (info.frame_bytes == 0) && (smpl == 0)) {
                return 0;
            }
        }

        // Copy decoded samples to dest buffer
        memcpy(pcmData, &pcmsamplesbuffer[0], samplesToReadChann * sizeof(int16_t));

        // Manage samples in internal buffer
        pcmsamplesbuffer_idx = samplesFetched - samplesToReadChann;
        memmove(&pcmsamplesbuffer[0], &pcmsamplesbuffer[samplesToReadChann], pcmsamplesbuffer_idx * sizeof(int16_t));

        /* Calculate frame duration in seconds */
        position += (float)((float)(chanNumber == 2 ? samplesToRead / chanNumber : samplesToRead) / (float)sampleRate);

        return samplesToRead;
    }

    auto decoderMP3::getBitWidth() -> unsigned int
    {
        static constexpr auto mp3bitWidth = 16U;
        return mp3bitWidth;
    }

} // namespace audio