~aleteoryx/muditaos

ref: 70126e07acf51ca04c936c4c57d6b430001c5fe9 muditaos/module-audio/Audio/decoder/decoderMP3.cpp -rw-r--r-- 8.8 KiB
70126e07 — jimmorrisson [EGD-4439] New filesystem handling - module audio. (#1037) 5 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
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
// Copyright (c) 2017-2020, 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);
    }

    void decoderMP3::fetchTagsSpecific()
    {

        std::fseek(fd, firstValidFrameFileOffset + 4, SEEK_SET);
        auto buff = std::make_unique<uint8_t[]>(firstValidFrameByteSize);

        std::fread(buff.get(), 1, firstValidFrameByteSize, fd);

        xing_info_t xinfo = {};
        if (parseXingHeader(buff.get(), firstValidFrameByteSize, &xinfo)) {
            // valid xing header found
            tag->total_duration_s = xinfo.TotalFrames * (samplesPerFrame) / sampleRate;
        }
        else {
            // Scan through whole file and count frames
            uint32_t frames_count = get_frames_count();
            tag->total_duration_s = frames_count * (samplesPerFrame) / sampleRate;
        }

        std::rewind(fd);
    }

    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;
    }

} // namespace audio