Closed Flix01 closed 4 years ago
The first thing to decide is whether or not drmp3_read_pcm_frames()
is the right tool for the job. Internet radio is one of the few scenarios where I feel like the low level API might be a better match. This is drmp3dec_decode_frame()
which takes a raw data buffer and returns the number of PCM frames decoded and the number of bytes consumed (via the info parameter). Just something for you to consider in case it might be more suitable for you. Look at the code for drmp3_decode_next_frame_ex__callbacks()
for example usage. Note that it decodes one MP3 frame which is made up of many PCM frames.
To answer your questions more directly:
drmp3_read_pcm_frames()
, initialize using the callback version of drmp3_init()
. In your read callback, always return the number of bytes dr_mp3 requests - if you return less than this, dr_mp3 will treat it as the end of the stream and stop decoding (of course, if you actually are at the end of the stream you should return whatever you have available and no more). Each time you call drmp3_read_pcm_frames()
, set your desired frame count to the capacity of your output buffer. The return value of drmp3_read_pcm_frames()
will be the number of frames actually decoded. Run it in a loop.DRMP3_DATA_CHUNK_SIZE
- that's an implementation detail. If you're using the low-level API that I mentioned above (drmp3dec_decode_frame()
), your minimum data size passed into the function should actually be DRMP3_MIN_DATA_CHUNK_SIZE
, unless you've approached the end of the stream in which case you just pass in as much as you've got (so yes, your assertion is correct about preventing decoding of samples that don't exist).Minor clarification, DRMP3_MIN_DATA_CHUNK_SIZE
is not public so you won't be able to use that directly unless you put the implementation of dr_mp3 before wherever it is you use it. It is coded to 16KB.
Thanks for your detailed explanation.
I'll try to use the low-level API soon. Regarding my two questions:
If you want to use drmp3_read_pcm_frames(), initialize using the callback version of drmp3_init(). In your read callback, always return the number of bytes dr_mp3 requests - if you return less than this, dr_mp3 will treat it as the end of the stream and stop decoding (of course, if you actually are at the end of the stream you should return whatever you have available and no more). Each time you call drmp3_read_pcm_frames(), set your desired frame count to the capacity of your output buffer. The return value of drmp3_read_pcm_frames() will be the number of frames actually decoded. Run it in a loop.
Yes, that's what I'm already doing, basically just something like:
Except that my stream never ends (but I guess I can return 0 from the curl body callback after a call to drmp3_uninit(&mp3); to change radio station). I can't simply set my desired frame count to the capacity of my pcm output buffer (not shown above) when I call drmp3_read_pcm_frames(), because otherwise drmp3 tries to read more encoded data than available. I'm trying to use the bitrate info I can extract from the curl header callback (not shown above) together with the sampleRate and the channels fields drmp3 can give me to calculate the number of pcm frames... it works, but it's not robust enough.
Moreover I would like code to handle connection drop and resume: that produces an mp3 stream broken at one point; sometimes I see that drmp3 reads a big amount of encoded data in these cases and hits the assert in mp3_callback (I suspect that maybe curl sends some other non-mp3 data when connection is back, but I still have to investigate: in any case it's not a priority at this point).
Forget about DRMP3_DATA_CHUNK_SIZE - that's an implementation detail. If you're using the low-level API that I mentioned above (drmp3dec_decode_frame()), your minimum data size passed into the function should actually be DRMP3_MIN_DATA_CHUNK_SIZE, unless you've approached the end of the stream in which case you just pass in as much as you've got (so yes, your assertion is correct about preventing decoding of samples that don't exist).
Minor clarification, DRMP3_MIN_DATA_CHUNK_SIZE is not public so you won't be able to use that directly unless you put the implementation of dr_mp3 before wherever it is you use it. It is coded to 16KB.
Got it! I used to set DRMP3_DATA_CHUNK_SIZE to the minimum (16384), to have the smallest read granularity and minimize the possibility of read overflows. But I'll try the low level API now.
No problem with hard-coding DRMP3_MIN_DATA_CHUNK_SIZE at all.
In short: I'll try the low-level API, reporting possible issues here.
Thanks for your help!
OK, so basically you want to know how many PCM frames will be decoded from an encoded buffer of a particular size? Nothing in dr_mp3 will do that for you directly. This requires running through the encoded data and, at a minimum, decoding the headers of each MP3 frame and adding up the frame counts. Doing this will require you to repeatedly call drmp3dec_decode_frame()
.
I think it's a good idea to consider the low-level API. With this one you would just loop until you run out of input data. If you want to go down that route, you may also want to consider using minimp3 directly - dr_mp3 is a wrapper around minimp3.
It works like a charm! And the code is even easier than before! Thank you for your help. Basically I just use 2 structs and 2 functions and that's all:
drmp3dec mp3;
drmp3dec_frame_info mp3info;
drmp3dec_init(...);
drmp3dec_decode_frame(...);
(it's so easy that I can even switch from dr_mp3.h to minimp3.h with an in-code definition).
Basically what I do is to only call drmp3dec_decode_frame(...) in steps of 16384 encoded buffer chunks. And code seems more robust when connection is lost and resumed too.
Maybe I'll make a gist when I clean up the code (unluckily using OpenAL, not miniaudio).
Thank you again.
Made a gist here mini_mp3_radio_decoder.c.
Hello. First of all: thank you for making this wonderful header-only audio decoding library!
Second: I'm currently trying to use dr_mp3 to decode mp3 internet radios.
encoded_size_in_bytes>=DRMP3_DATA_CHUNK_SIZE
available, I should only decode something like:encoded_size_in_bytes_to_use = encoded_size_in_bytes - encoded_size_in_bytes%DRMP3_DATA_CHUNK_SIZE
, so that drmp3 doesn't try to read encoded samples that do not exist. Right?Thanks in advance for your help.