You can now, e.g.,
```
with open("/whatever.mp3") as mp3_file:
mp3_file.seek(16000*30)
decoder.file = mp3_file
i2s.play(decoder)
```
to start about 30 seconds into a 128kbit/s CBR track.
If a track is looped, the loop will start at the beginning.
This also changes the behavior if the track is started & stopped: it will
continue from where it left off, except if it had prevously run to
completion. To get other behavior, you can seek the file and then re-assign
the file property.
This gets MP3 playback of a soma.fm stream working for up to a minute
at a time, though it's still vulnerable to network glitches.
* the buffer can empty, in which case a single block of audio
plays repeatedly in max headroom stutter fashion
* the server eventually (after 1 to 5 5 minutes) stops getting packets
at all. At this point stream playback stops, with the internal error
indicating a problem MP3 decoding (which doesn't quite make sense):
-9, ERR_MP3_INVALID_HUFFCODES.
* other combinations of audiomixer buffer & mp3 buffer might give
different results
```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 * 2
mp3_buffer = bytearray(32768)
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)
```
An mp3 decoder (note that this needs `audiocore.get_buffer`, not
enabled on devices):
```py
import sys
import audiomp3
import audiocore
GET_BUFFER_DONE, GET_BUFFER_MORE_DATA, GET_BUFFER_ERROR = range(3)
with audiomp3.MP3Decoder(sys.argv[1]) as decoder, open(sys.argv[2], "wb") as target:
while True:
res, samples = audiocore.get_buffer(decoder)
if res != GET_BUFFER_ERROR:
target.write(samples)
if res != GET_BUFFER_MORE_DATA:
break
```
this doesn't actually add any tests though
This can sort-of play MP3s from a http request, but the buffering is
not good enough to play glitch-free. A new kind of buffer that can
read ahead further without blocking is needed.
This runs in the Renode simulator and enables easier tracing and
debugging of the CircuitPython core. This port can also serve as
a starting point for new ports because it implements the minimal
necessary for the CP core to run.
It is enabled on:
* SAMD51 boards with 1MB flash (SKU ending in 20A)
* Feather RP2040 DVI. Others have PIO usb host.
* All ESP32 boards.
* All nRF boards
Fixes#8676
If there's only one device on the bus, the chip_select pin of the
peripheral can be fixed in hardware, therefore lowering the number of
pins required on the microcontroller side.
This patch allows this by making the chip_select pin optional.
If there's only one device on the bus, the chip_select pin of the
peripheral can be fixed in hardware, therefore lowering the number of
pins required on the microcontroller side.
This patch allows this by making the chip_select pin optional.