如何在Raspberry Pi Pico W上用CircuitPython流式播放URL中的MP3音频
Raspberry Pi Pico W MP3音频流式播放改造方案
原代码需等待MP3文件完全下载后才能播放,针对这个问题,我们可以通过内存缓冲区+自定义数据流对象实现边下载边播放的流式效果,无需等待完整下载。
修改后的完整代码
import adafruit_requests import wifi import socketpool import ssl import board import audiomp3 import audiobusio import os from collections import deque # 初始化I2S音频输出 audio = audiobusio.I2SOut(board.GP10, board.GP11, board.GP9) MP3_URL = "http://codeskulptor-demos.commondatastorage.googleapis.com/descent/background%20music.mp3" CHUNK_SIZE = 1024 # 预加载缓冲区大小,确保有足够数据启动播放 PRELOAD_SIZE = 8 * CHUNK_SIZE # 播放时的缓冲区上限,避免占用过多内存 BUFFER_LIMIT = 32 * CHUNK_SIZE # 连接WiFi wifi.radio.connect(os.getenv('WIFI_SSID'), os.getenv('WIFI_PASSWORD')) pool = socketpool.SocketPool(wifi.radio) requests = adafruit_requests.Session(pool, ssl.create_default_context()) # 自定义数据流类,模拟文件对象供MP3解码器读取 class StreamBuffer: def __init__(self): self.buffer = deque() self.total_received = 0 self.download_complete = False def write(self, data): # 控制缓冲区大小,防止内存溢出 while sum(len(chunk) for chunk in self.buffer) > BUFFER_LIMIT: pass self.buffer.append(data) self.total_received += len(data) def read(self, size=-1): # 读取指定大小的数据,缓冲区不足时等待(直到下载完成) result = b"" while len(result) < size or size == -1: if not self.buffer: if self.download_complete: break continue chunk = self.buffer.popleft() if size == -1: result += chunk else: take = min(size - len(result), len(chunk)) result += chunk[:take] if take < len(chunk): self.buffer.appendleft(chunk[take:]) return result try: # 初始化缓冲区 stream_buffer = StreamBuffer() # 发送GET请求,开启流式响应 response = requests.get(MP3_URL, stream=True) # 预加载数据,确保有足够内容启动播放 print("预加载音频数据...") preloaded = 0 for chunk in response.iter_content(chunk_size=CHUNK_SIZE): if chunk: stream_buffer.write(chunk) preloaded += len(chunk) if preloaded >= PRELOAD_SIZE: break # 初始化MP3解码器,传入自定义数据流对象 mp3_decoder = audiomp3.MP3Decoder(stream_buffer) # 根据音频实际采样率调整,保持原代码设置 mp3_decoder.sample_rate = 24000 # 开始播放 print("开始播放...") audio.play(mp3_decoder) # 后台继续下载剩余数据到缓冲区 for chunk in response.iter_content(chunk_size=CHUNK_SIZE): if chunk: stream_buffer.write(chunk) # 检查播放状态,播放结束则停止下载 if not audio.playing: break # 标记下载完成,确保解码器能读完剩余数据 stream_buffer.download_complete = True # 等待播放结束 while audio.playing: pass print("播放完成") except Exception as e: print("播放或下载出错:\n", str(e))
关键改动说明
- 自定义StreamBuffer类:模拟文件的
read和write方法,将网络下载的音频块存入双端队列缓冲区,供MP3解码器读取,实现数据流的衔接。 - 预加载机制:先下载一部分数据(8个块),确保解码器有足够的初始数据启动播放,避免播放卡顿。
- 缓冲区大小控制:设置缓冲区上限,防止过多数据占用Pico W有限的内存。
- 流式响应处理:使用
stream=True开启请求的流式响应,边下载边将数据写入缓冲区,同时解码器从缓冲区读取数据播放,实现边下边播。
内容的提问来源于stack exchange,提问作者Dukeofduke
相关产品推荐
相关产品推荐

