Skip to content

Commit 9bc89f5

Browse files
committed
MP3Decoder: better handle INDATA_UNDERFLOW & MAINDATA_UNDERFLOW
This allows playback of some 128k http streams from somafm, though glitches occur with some regularity. ```py import time import adafruit_connection_manager import adafruit_requests import audiobusio import audiomixer import audiomp3 import board import wifi pool = adafruit_connection_manager.get_radio_socketpool(wifi.radio) ssl_context = adafruit_connection_manager.get_radio_ssl_context(wifi.radio) requests = adafruit_requests.Session(pool, ssl_context) # todo: parse PLS files like https://somafm.com/nossl/dronezone.pls # todo: figure out why https URLs don't work at all (missing select?) # STREAMING_URL = "http://ice2.somafm.com/dronezone-128-mp3" STREAMING_URL = "http://ice4.somafm.com/tikitime-128-mp3" def get_mp3_stream(): if STREAMING_URL.startswith("http:") or STREAMING_URL.startswith("https:"): return requests.get(STREAMING_URL, headers={"connection": "close"}).socket return open(STREAMING_URL, "rb") mixer_buffer_size = 1152 * 16 mp3_buffer = bytearray(16384) with audiobusio.I2SOut( bit_clock=board.D12, word_select=board.D13, data=board.D11 ) as i2s, get_mp3_stream() as stream, audiomp3.MP3Decoder( stream, mp3_buffer ) as sample, audiomixer.Mixer( channel_count=2, sample_rate=44100, buffer_size=mixer_buffer_size ) as m: v = m.voice[0] print(sample) i2s.play(m) v.play(sample, loop=False) while v.playing: time.sleep(0.1) ```
1 parent 974b21a commit 9bc89f5

File tree

1 file changed

+37
-4
lines changed

1 file changed

+37
-4
lines changed

shared-module/audiomp3/MP3Decoder.c

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323

2424
#define MAX_BUFFER_LEN (MAX_NSAMP * MAX_NGRAN * MAX_NCHAN * sizeof(int16_t))
2525

26+
#define DO_DEBUG (0)
27+
2628
#if defined(MICROPY_UNIX_COVERAGE)
2729
#define background_callback_prevent() ((void)0)
2830
#define background_callback_allow() ((void)0)
@@ -38,6 +40,9 @@ STATIC bool stream_readable(void *stream) {
3840
}
3941

4042
mp_int_t ret = stream_p->ioctl(stream, MP_STREAM_POLL, MP_STREAM_POLL_RD | MP_STREAM_POLL_ERR | MP_STREAM_POLL_HUP, &errcode);
43+
if (DO_DEBUG) {
44+
mp_printf(&mp_plat_print, "stream_readable ioctl() -> %d [errcode=%d]\n", ret, errcode);
45+
}
4146
return ret != 0;
4247
}
4348

@@ -51,7 +56,13 @@ STATIC ssize_t stream_read(void *stream, void *buf, size_t len) {
5156
return -EINVAL;
5257
}
5358
mp_uint_t out_sz = stream_p->read(MP_OBJ_FROM_PTR(stream), buf, len, &errcode);
59+
if (DO_DEBUG) {
60+
mp_printf(&mp_plat_print, "stream_read(%d) -> %d\n", (int)len, (int)out_sz);
61+
}
5462
if (out_sz == MP_STREAM_ERROR) {
63+
if (DO_DEBUG) {
64+
mp_printf(&mp_plat_print, "errcode=%d\n", errcode);
65+
}
5566
return -errcode; // CIRCUITPY-CHANGE: returns negative errcode value
5667
} else {
5768
return out_sz;
@@ -127,6 +138,10 @@ static bool mp3file_update_inbuf_always(audiomp3_mp3file_obj_t *self) {
127138
self->inbuf.write_off += n_read;
128139
}
129140

141+
if (DO_DEBUG) {
142+
mp_printf(&mp_plat_print, "new avail=%d eof=%d\n", (int)INPUT_BUFFER_AVAILABLE(self->inbuf), self->eof);
143+
}
144+
130145
// Return true iff there are at least some useful bytes in the buffer
131146
return INPUT_BUFFER_AVAILABLE(self->inbuf) > 0;
132147
}
@@ -395,18 +410,25 @@ audioio_get_buffer_result_t audiomp3_mp3file_get_buffer(audiomp3_mp3file_obj_t *
395410
uint32_t *buffer_length) {
396411
if (!self->inbuf.buf) {
397412
*buffer_length = 0;
413+
if (DO_DEBUG) {
414+
mp_printf(&mp_plat_print, "%s:%d\n", __FILE__, __LINE__);
415+
}
398416
return GET_BUFFER_ERROR;
399417
}
400418
if (!single_channel_output) {
401419
channel = 0;
402420
}
403421

404-
*buffer_length = self->frame_buffer_size;
422+
size_t frame_buffer_size_bytes = self->frame_buffer_size;
423+
*buffer_length = frame_buffer_size_bytes;
405424

406425
if (channel == self->other_channel) {
407426
*bufptr = (uint8_t *)(self->pcm_buffer[self->other_buffer_index] + channel);
408427
self->other_channel = -1;
409428
self->samples_decoded += *buffer_length / sizeof(int16_t);
429+
if (DO_DEBUG) {
430+
mp_printf(&mp_plat_print, "%s:%d\n", __FILE__, __LINE__);
431+
}
410432
return GET_BUFFER_MORE_DATA;
411433
}
412434

@@ -428,15 +450,26 @@ audioio_get_buffer_result_t audiomp3_mp3file_get_buffer(audiomp3_mp3file_obj_t *
428450
CONSUME(self, BYTES_LEFT(self) - bytes_left);
429451

430452
if (err) {
431-
*buffer_length = 0;
432-
return GET_BUFFER_DONE;
453+
memset(buffer, 0, frame_buffer_size_bytes);
454+
if (DO_DEBUG) {
455+
mp_printf(&mp_plat_print, "%s:%d err=%d\n", __FILE__, __LINE__, err);
456+
}
457+
if (err != ERR_MP3_INDATA_UNDERFLOW && err != ERR_MP3_MAINDATA_UNDERFLOW) {
458+
memset(buffer, 0, self->frame_buffer_size);
459+
*buffer_length = 0;
460+
self->eof = true;
461+
return GET_BUFFER_ERROR;
462+
}
433463
}
434464

435-
self->samples_decoded += *buffer_length / sizeof(int16_t);
465+
self->samples_decoded += frame_buffer_size_bytes / sizeof(int16_t);
436466

437467
mp3file_skip_id3v2(self);
438468
int result = mp3file_find_sync_word(self) ? GET_BUFFER_MORE_DATA : GET_BUFFER_DONE;
439469

470+
if (DO_DEBUG) {
471+
mp_printf(&mp_plat_print, "%s:%d result=%d\n", __FILE__, __LINE__, result);
472+
}
440473
if (INPUT_BUFFER_SPACE(self->inbuf) > 512) {
441474
background_callback_add(
442475
&self->inbuf_fill_cb,

0 commit comments

Comments
 (0)