10 #define MINIMP3_IMPLEMENTATION
11 #define MINIMP3_FLOAT_OUTPUT
16 constexpr size_t MinMp3DataSize
= 16384;
17 constexpr size_t MaxMp3DataSize
= MinMp3DataSize
* 8;
19 size_t append_file_data(std::istream
&file
, alure::Vector
<uint8_t> &data
, size_t count
)
21 size_t old_size
= data
.size();
22 if(old_size
>= MaxMp3DataSize
|| count
== 0)
24 count
= std::min(count
, MaxMp3DataSize
- old_size
);
25 data
.resize(old_size
+ count
);
28 file
.read(reinterpret_cast<char*>(data
.data()+old_size
), count
);
29 size_t got
= file
.gcount();
30 data
.resize(old_size
+ got
);
35 size_t find_i3dv2(alure::ArrayView
<uint8_t> data
)
37 if(data
.size() > 10 && memcmp(data
.data(), "ID3", 3) == 0)
38 return (((data
[6]&0x7f) << 21) | ((data
[7]&0x7f) << 14) |
39 ((data
[8]&0x7f) << 7) | ((data
[9]&0x7f) )) + 10;
48 class Mp3Decoder final
: public Decoder
{
49 UniquePtr
<std::istream
> mFile
;
51 Vector
<uint8_t> mFileData
;
54 Vector
<float> mSampleData
;
55 mp3dec_frame_info_t mLastFrame
{};
56 mutable std::mutex mMutex
;
58 mutable std::streamsize mSampleCount
{-1};
59 ChannelConfig mChannels
{ChannelConfig::Mono
};
60 SampleType mSampleType
{SampleType::UInt8
};
63 static int decodeFrame(std::istream
&file
, mp3dec_t
&mp3
, Vector
<uint8_t> &file_data
,
64 float *sample_data
, mp3dec_frame_info_t
*frame_info
)
66 if(file_data
.size() < MinMp3DataSize
&& !file
.eof())
68 size_t todo
= MinMp3DataSize
- file_data
.size();
69 append_file_data(file
, file_data
, todo
);
72 int samples_to_get
= mp3dec_decode_frame(&mp3
, file_data
.data(), file_data
.size(),
73 sample_data
, frame_info
);
74 while(samples_to_get
== 0 && !file
.eof())
76 if(append_file_data(file
, file_data
, MinMp3DataSize
) == 0)
78 samples_to_get
= mp3dec_decode_frame(&mp3
, file_data
.data(), file_data
.size(),
79 sample_data
, frame_info
);
81 return samples_to_get
;
85 Mp3Decoder(UniquePtr
<std::istream
> file
, Vector
<uint8_t>&& initial_data
,
86 const mp3dec_t
&mp3
, const mp3dec_frame_info_t
&first_frame
,
87 ChannelConfig chans
, SampleType stype
, int srate
) noexcept
88 : mFile(std::move(file
)), mFileData(std::move(initial_data
)), mMp3(mp3
)
89 , mLastFrame(first_frame
), mChannels(chans
), mSampleType(stype
), mSampleRate(srate
)
91 ~Mp3Decoder() override
{ }
93 ALuint
getFrequency() const noexcept override
;
94 ChannelConfig
getChannelConfig() const noexcept override
;
95 SampleType
getSampleType() const noexcept override
;
97 uint64_t getLength() const noexcept override
;
98 bool seek(uint64_t pos
) noexcept override
;
100 std::pair
<uint64_t,uint64_t> getLoopPoints() const noexcept override
;
102 ALuint
read(ALvoid
*ptr
, ALuint count
) noexcept override
;
105 ALuint
Mp3Decoder::getFrequency() const noexcept
{ return mSampleRate
; }
106 ChannelConfig
Mp3Decoder::getChannelConfig() const noexcept
{ return mChannels
; }
107 SampleType
Mp3Decoder::getSampleType() const noexcept
{ return mSampleType
; }
109 uint64_t Mp3Decoder::getLength() const noexcept
111 if(LIKELY(mSampleCount
>= 0))
114 std::lock_guard
<std::mutex
> _(mMutex
);
117 std::streamsize oldfpos
= mFile
->tellg();
118 if(oldfpos
< 0 || !mFile
->seekg(0))
124 Vector
<uint8_t> file_data
;
129 append_file_data(*mFile
, file_data
, MinMp3DataSize
);
131 size_t id_size
= find_i3dv2(file_data
);
134 if(id_size
<= file_data
.size())
135 file_data
.erase(file_data
.begin(), file_data
.begin()+id_size
);
138 mFile
->ignore(id_size
- file_data
.size());
143 std::streamsize count
= 0;
145 // Read the next frame.
146 mp3dec_frame_info_t frame_info
{};
147 int samples_to_get
= decodeFrame(*mFile
, mp3
, file_data
, nullptr, &frame_info
);
148 if(samples_to_get
<= 0) break;
150 // Don't continue if the frame changed format
151 if((mChannels
== ChannelConfig::Mono
&& frame_info
.channels
!= 1) ||
152 (mChannels
== ChannelConfig::Stereo
&& frame_info
.channels
!= 2) ||
153 mSampleRate
!= frame_info
.hz
)
156 // Keep going to the next frame
157 if(file_data
.size() >= (size_t)frame_info
.frame_bytes
)
158 file_data
.erase(file_data
.begin(), file_data
.begin()+frame_info
.frame_bytes
);
161 mFile
->ignore(frame_info
.frame_bytes
- file_data
.size());
164 count
+= samples_to_get
;
166 mSampleCount
= count
;
169 mFile
->seekg(oldfpos
);
173 bool Mp3Decoder::seek(uint64_t pos
) noexcept
175 // Use temporary local storage to avoid trashing current data in case of
177 Vector
<uint8_t> file_data
;
182 // Seeking to somewhere in the file. Backup the current file position and
183 // reset back to the beginning.
184 // TODO: Obvious optimization: Track the current sample offset and don't
185 // rewind if seeking forward.
187 std::streamsize oldfpos
= mFile
->tellg();
188 if(oldfpos
< 0 || !mFile
->seekg(0))
191 append_file_data(*mFile
, file_data
, MinMp3DataSize
);
193 size_t id_size
= find_i3dv2(file_data
);
196 if(id_size
<= file_data
.size())
197 file_data
.erase(file_data
.begin(), file_data
.begin()+id_size
);
200 mFile
->ignore(id_size
- file_data
.size());
207 // Read the next frame.
208 mp3dec_frame_info_t frame_info
{};
209 int samples_to_get
= decodeFrame(*mFile
, mp3
, file_data
, nullptr, &frame_info
);
210 if(samples_to_get
<= 0) break;
212 // Don't continue if the frame changed format
213 if((mChannels
== ChannelConfig::Mono
&& frame_info
.channels
!= 1) ||
214 (mChannels
== ChannelConfig::Stereo
&& frame_info
.channels
!= 2) ||
215 mSampleRate
!= frame_info
.hz
)
218 if((uint64_t)samples_to_get
> pos
- curpos
)
220 // Desired sample is within this frame, decode the samples and go
221 // to the desired offset.
222 Vector
<float> sample_data(MINIMP3_MAX_SAMPLES_PER_FRAME
);
223 samples_to_get
= decodeFrame(*mFile
, mp3
, file_data
, sample_data
.data(),
226 if((uint64_t)samples_to_get
> pos
- curpos
)
228 sample_data
.resize(samples_to_get
* frame_info
.channels
);
229 sample_data
.erase(sample_data
.begin(),
230 sample_data
.begin() + (pos
-curpos
)*frame_info
.channels
);
231 file_data
.erase(file_data
.begin(), file_data
.begin()+frame_info
.frame_bytes
);
232 mSampleData
= std::move(sample_data
);
233 mFileData
= std::move(file_data
);
234 mLastFrame
= frame_info
;
240 // Keep going to the next frame
241 if(file_data
.size() >= (size_t)frame_info
.frame_bytes
)
242 file_data
.erase(file_data
.begin(), file_data
.begin()+frame_info
.frame_bytes
);
245 mFile
->ignore(frame_info
.frame_bytes
- file_data
.size());
248 curpos
+= samples_to_get
;
251 // Seeking failed. Restore original file position.
253 mFile
->seekg(oldfpos
);
257 std::pair
<uint64_t,uint64_t> Mp3Decoder::getLoopPoints() const noexcept
259 return {0, std::numeric_limits
<uint64_t>::max()};
262 ALuint
Mp3Decoder::read(ALvoid
*ptr
, ALuint count
) noexcept
271 std::lock_guard
<std::mutex
> _(mMutex
);
274 ALuint todo
= count
-total
;
276 if(!mSampleData
.empty())
278 // Write out whatever samples we have.
279 todo
= std::min
<ALuint
>(todo
, mSampleData
.size()/mLastFrame
.channels
);
281 size_t numspl
= todo
*mLastFrame
.channels
;
282 if(mSampleType
== SampleType::Float32
)
284 std::copy(mSampleData
.begin(), mSampleData
.begin()+numspl
, dst
.f
);
289 mp3dec_f32_to_s16(mSampleData
.data(), dst
.s
, numspl
);
292 mSampleData
.erase(mSampleData
.begin(), mSampleData
.begin()+numspl
);
298 // Read directly into the output buffer if it doesn't need conversion
299 // and there's enough guaranteed room.
301 if(mSampleType
== SampleType::Float32
&&
302 todo
*mLastFrame
.channels
>= MINIMP3_MAX_SAMPLES_PER_FRAME
)
306 mSampleData
.resize(MINIMP3_MAX_SAMPLES_PER_FRAME
);
307 samples_ptr
= mSampleData
.data();
310 mp3dec_frame_info_t frame_info
{};
311 int samples_to_get
= decodeFrame(*mFile
, mMp3
, mFileData
, samples_ptr
, &frame_info
);
312 if(samples_to_get
<= 0)
318 // Format changing not supported. End the stream.
319 if((mChannels
== ChannelConfig::Mono
&& frame_info
.channels
!= 1) ||
320 (mChannels
== ChannelConfig::Stereo
&& frame_info
.channels
!= 2) ||
321 mSampleRate
!= frame_info
.hz
)
327 // Remove used file data, update sample storage size with what we got
328 mFileData
.erase(mFileData
.begin(), mFileData
.begin()+frame_info
.frame_bytes
);
329 mLastFrame
= frame_info
;
330 if(!mSampleData
.empty())
331 mSampleData
.resize(samples_to_get
* frame_info
.channels
);
334 dst
.f
+= samples_to_get
* frame_info
.channels
;
335 total
+= samples_to_get
;
343 Mp3DecoderFactory::Mp3DecoderFactory() noexcept
347 Mp3DecoderFactory::~Mp3DecoderFactory()
351 SharedPtr
<Decoder
> Mp3DecoderFactory::createDecoder(UniquePtr
<std::istream
> &file
) noexcept
353 Vector
<uint8_t> initial_data
;
358 // Make sure the file is valid and we get some samples.
359 if(append_file_data(*file
, initial_data
, MinMp3DataSize
) == 0)
362 // If the file contains an ID3v2 tag, skip it.
363 // TODO: Read it? Does it have e.g. sample length or loop points?
364 size_t id_size
= find_i3dv2(initial_data
);
367 if(id_size
<= initial_data
.size())
368 initial_data
.erase(initial_data
.begin(), initial_data
.begin()+id_size
);
371 file
->ignore(id_size
- initial_data
.size());
372 initial_data
.clear();
374 // Refill initial data buffer after clearing the ID3 chunk.
375 append_file_data(*file
, initial_data
, MinMp3DataSize
- initial_data
.size());
378 mp3dec_frame_info_t frame_info
{};
379 int samples_to_get
= mp3dec_decode_frame(&mp3
, initial_data
.data(), initial_data
.size(),
380 nullptr, &frame_info
);
381 while(samples_to_get
== 0 && !file
->eof())
383 if(append_file_data(*file
, initial_data
, MinMp3DataSize
) == 0)
385 samples_to_get
= mp3dec_decode_frame(&mp3
, initial_data
.data(), initial_data
.size(),
386 nullptr, &frame_info
);
391 if(frame_info
.hz
< 1)
394 ChannelConfig chans
= ChannelConfig::Mono
;
395 if(frame_info
.channels
== 1)
396 chans
= ChannelConfig::Mono
;
397 else if(frame_info
.channels
== 2)
398 chans
= ChannelConfig::Stereo
;
402 SampleType stype
= SampleType::Int16
;
403 if(ContextImpl::GetCurrent()->isSupported(chans
, SampleType::Float32
))
404 stype
= SampleType::Float32
;
406 return MakeShared
<Mp3Decoder
>(std::move(file
), std::move(initial_data
), mp3
,
407 frame_info
, chans
, stype
, frame_info
.hz
);