这是一款基于Adafruit CircuitPython和RP2350B开发板的音乐播放器。它可以播放存储在SD卡上或者flash的music目录下的MP3和WAV音频文件,并通过OLED显示屏显示当前播放的曲目信息。
1、项目功能介绍
1.支持MP3和WAV格式的音频文件播放。
2.自动扫描SD卡或者Flash的music目录,生成播放列表。
3.通过按键控制播放、暂停、下一曲等功能。
4.在OLED显示屏上显示当前播放的曲目信息。
5.支持播放音乐音阶。
2、设计思路
这个项目采用了模块化、可扩展的设计思路,充分利用了 CircuitPython 的优势实现。
- 存储介质的选择:
支持两种存储介质:SD卡和Flash。用户可以根据实际需求选择合适的存储方式。 通过抽象出 MusicPlayer 类,将存储方式的差异封装起来,使得上层代码不需要关心具体的存储位置。
- 音频文件的播放:
使用 Adafruit CircuitPython 提供的 audiocore 和 audiopwmio 模块,实现了对 WAV 和 MP3 音频文件的播放。 通过 play_wav_music 和 play_mp3_music 函数,将不同格式音频文件的播放逻辑抽象出来,方便后续扩展。
- 用户交互:
通过按键输入检测,实现了播放控制功能,如启动/暂停播放、播放下一曲等。 使用 OLED 显示屏显示当前播放的曲目信息,并实现了文本滚动效果,增强了用户体验。
- 模块化设计:
将不同功能模块拆分成单独的文件,如按键输入检测、SD卡挂载、音乐播放器逻辑等,提高了代码的可读性和可维护性。 通过组合使用这些模块,实现了音乐播放器的整体功能。
- 扩展性:
设计时考虑了扩展性,如新增 歌词显示等。通过抽象出 MusicPlayer 类,可以方便地添加新的存储介质支持,或者增加新的播放功能。
3、硬件要求&方案框图
1. RP2350B核心板+综合训练扩展板
2. SD卡模块(非必须)
4、软件流程图
5、软件依赖
1. Adafruit CircuitPython库
2. Adafruit SSD1306 OLED驱动库
3. Adafruit audiocore和audiopwmio音频播放库
6、使用说明
SD卡存储模式:
确保SD卡已插入开发板,并且SD卡上有MP3或WAV格式的音乐文件。
运行main()函数,将player = MusicPlayer(music_dir="/sd/")取消注释。
Flash存储模式:
确保MP3或WAV格式的音乐文件存放在设备的Flash文件系统的/music/目录下。
运行main()函数,将player = MusicPlayer()取消注释。
在两种模式下,播放器的基本功能都是一样的:
按键1:停止播放
按键2:开始/继续播放
按键3:播放下一曲
按键4:播放音乐音阶
OLED显示屏会显示当前播放的曲目信息,并在播放过程中滚动显示
7、主要代码片段及说明
1.按键输入检测(`get_key_info.py`)
该文件实现了按键输入的检测功能。它使用`analogio`模块读取模拟引脚的电压值,根据不同电压范围判断按键ID。
KEY_PRVENT_COUNT = 5
def get_key_value():
count = 0
sum_value = 0
while (count < KEY_PRVENT_COUNT):
sum_value += g_detect_pin.value
count += 1
time.sleep(0.005)
avg_value = sum_value /count
return avg_value
2. SD卡挂载(`mount_sd.py`)
该文件实现了SD卡的挂载功能,使用`storage`和`adafruit_sdcard`库完成。
def my_mount_sd():
sd_spi = bitbangio.SPI(board.GP9, MOSI=board.GP8, MISO=board.GP10)
sd_cs = DigitalInOut(board.GP7) # any pin!
sdcard = adafruit_sdcard.SDCard(sd_spi, sd_cs)
vfs = storage.VfsFat(sdcard)
storage.mount(vfs, "/sd")
3.音乐播放器主逻辑(`my_music_play.py`)
该文件实现了音乐播放器的主要逻辑,包括播放列表的生成、音乐播放、OLED显示等功能。
def play_loop(self):
# 显示歌曲名
if self.old_index != self.current_index:
self.display_string = self.get_curr_mane()
oled_display_ufont.my_display_text(self.display_string, 0, 8, 1)
self.display_string = oled_display_ufont.rotate_string(self.display_string, -1)
time.sleep(1)
elif self.is_playing:
pass
oled_display_ufont.my_display_text(self.display_string, 0, 8, 1)
self.display_string = oled_display_ufont.rotate_string(self.display_string, -1)
4. OLED显示(`oled_display_ufont.py`)
该文件实现了OLED显示屏的驱动和文本显示功能,使用了`adafruit_ssd1306`库和自定义的bitmap字体。
def my_display_text(string: str = "", x: int = 0, y: int = 0, color: int = 0xFFFF,
font = font, display = oled,
half_char: bool = True, auto_wrap: bool = False, line_spacing: int = 0):
display.fill(0)
font_size = font.font_size
initial_x = x
for char in range(len(string)):
if auto_wrap and ((x + font_size // 2 > display.width and ord(string[char]) < 128 and half_char) or
(x + font_size > display.width and (not half_char or ord(string[char]) > 128))):
y += font_size + line_spacing
x = initial_x
# 对控制字符的处理
if string[char] == '\n':
y += font_size + line_spacing
x = initial_x
continue
elif string[char] == '\t':
x = ((x // font_size) + 1) * font_size + initial_x % font_size
continue
elif ord(string[char]) < 16:
continue
# 超过范围的字符不会显示*
if x > display.width or y > display.height:
continue
str_bit = font.get_bitmap(string[char])
my_draw_char_2(oled, str_bit, x, y, color)
x += font_size
display.show()
def rotate_string(s, n):
"""
将字符串 s 循环向右移动 n 个字符
"""
n = n % len(s) # 确保 n 在字符串长度范围内
return s[-n:] + s[:-n]
5.音频播放(`play_wav_mp3.py`和`play_musical_scale.py`)
这两个文件实现了WAV、MP3音频文件的播放,以及音乐音阶的播放。
def play_wav_music(path, wait=True):
global g_muisc_file
while g_audio_handle.playing:
g_audio_handle.stop()
time.sleep(1)
g_muisc_file = open(path, "rb")
wav = audiocore.WaveFile(g_muisc_file)
print("playing: ", path)
g_audio_handle.play(wav)
if (wait == True):
while g_audio_handle.playing:
pass
print("stopped")
def play_mp3_music(path, wait=True):
global g_muisc_file
while g_audio_handle.playing:
g_audio_handle.stop()
time.sleep(1)
try:
g_muisc_file = open(path, "rb")
mp3 = audiomp3.MP3Decoder(g_muisc_file)
except OSError:
print("错误: 文件格式不支持或文件损坏")
return
print("playing: ", path)
g_audio_handle.play(mp3)
if (wait == True):
while g_audio_handle.playing:
pass
print("stopped")
def play_do_to_si(pwm):
# Play double-buffered
sample = audiocore.RawSample(sine_wave, single_buffer=False)
pwm.play(sample, loop=True)
time.sleep(play_duration)
# changing the wave takes effect almost immediately
musical_scale = [261.63, 293.66, 329.63, 349.23, 392.00, 440.00, 493.88]
for m in musical_scale:
for i in range(length):
sine_wave[i] = int(math.sin(math.pi * 2 * i / (length * m/440.0)) * (2 ** 15))
time.sleep(play_duration)
pwm.stop()
6.文件说明
- `get_key_info.py`:实现按键输入检测的功能。
- `mount_sd.py`:实现SD卡挂载的功能。
- `my_music_pla.py`:音乐播放器的主要逻辑实现。
- `oled_display_ufont.py`: OLED显示屏的驱动和文本显示功能。
- `play_musical_scale.py`:播放音乐音阶的功能实现。
- `play_wav_mp3.py`:音频文件播放的功能实现。
- `code.py`:程序入口文件,运行该文件可启动音乐播放器。
8、注意事项
1.确保SD卡上或者flash中的MP3和WAV文件格式正确,否则可能无法正常播放。
2.按键输入检测是基于模拟量输入的,可能需要根据实际硬件进行调整。
3. OLED显示屏的驱动和字体文件需要事先准备好。
4.音乐音阶的播放是使用合成的方式实现的,可能与真实音乐有所不同。
5、基于“adafruit-circuitpython-solderparty_rp2350_stamp_xl-en_US-10.0.0-alpha.4.uf2”运行。
6、播放的MP3文件最好是恒定比特率CBR(Constant Bit-Rate)的。
9、心得体会
- 模拟按键的精准识别:通过电压范围判断按键ID的方案,需特别注意;添加多次采样取平均,有效抑制信号抖动;
- SD卡挂载时:设计目录扫描缓存机制减少IO操作;
- MP3播放时增加文件异常处理机制,保证播放流畅。
- SD和显示屏都使用了软SPI实现,在播放SD卡上文件时,程序执行效率较差,有待改进。
10、未来优化方向
- 支持歌词同步显示(LRC格式解析);
- 添加EQ音效处理模块;
- 增加播放状态图标(▶/❚❚)提供明确操作反馈;