Mobile wallpaper 1
1265 字
6 分钟
music-cli 开发
2025-11-01
统计加载中...

music-cli 开发日志#

前言(为什么要做这个)#

主要是我一个同学很喜欢听歌,而且很关注各类的音乐播放软件,但是电脑上还没有找到像手机端salt player一样很好用的本地音乐播放器,我就经常让他自己做一个,然后说着说着我自己也产生了兴趣,但是我不会前端,就做了这么一个命令行的音乐播放器(虽然说写的非常屎,现在还有很多bug,哎~ )

技术栈:Go(go 1.25.3),使用 github.com/faiface/beep 负责音频播放,github.com/dhowden/tag 读取元数据/歌词,golang.org/x/term 操作终端尺寸与 raw 模式,若干小工具包用于终端字符串宽度与 ANSI 颜色处理。

主要实现与关键点#

支持的格式#

  • flac
  • mp3
  • wav(不能读取音乐标签)

文件与模块一览#

  • main.go:入口,调用 player.PageController() 启动交互流程。
  • input_handler/用户输出处理目录,包括各种界面用户输入处理的逻辑和页面切换控制器。
  • player/:播放核心目录,包含播放、输入控制、歌词解析、进度条等:
    • player.goPlayer 结构体,负责解码(mp3/flac/wav)、初始化 beep 流、启动播放、管理 beep.Ctrl 用于暂停/恢复,以及歌词加载逻辑。。
    • lyric.go:逐字歌词解析与渲染逻辑,支持把相同时间戳的原文/译文配对并逐字高亮。
    • process_bar.go:进度条逻辑,按终端宽度动态计算进度长度并绘制。
  • utils/:若干工具函数:
    • center.go:把带颜色的字符串居中显示(使用 stripansi 删除 ANSI 再测宽度)。
    • path.goWalkDirListDirPrintPathInfo 等目录/分页展示辅助。

关键依赖在 go.mod 中可以看到:beeptagstripansigo-runewidth 等。

播放流程#

  1. 启动页面控制器
  2. 用户输入路径(handleHomeInput),若是目录进入菜单,否则直接播放文件
  3. 在菜单中使用 ListDir 列出音乐文件和子目录,分页显示,+下一页,-上一页, q 退出,0播放当前目录所有音乐,a递归遍历目录并播放所有文件,加上r后可以随机播放
  4. 选择播放时创建 PlayerNewPlayer(path)),Init() 打开文件并根据扩展名用相应解码器(mp3.Decodeflac.Decodewav.Decode)。
  5. 使用 beep.Ctrl{Streamer: p.streamer} 作为控制器,在 speaker.Play(beep.Seq(ctrl, beep.Callback(...))) 中播放并等待播放完成信号。
  6. 播放期间开启两个并发协程:一个绘制进度条(progressBar.printBar),另一个负责歌词显示(lyrics.print)。输出使用 ANSI 控制序列定位,printMu 用于避免多协程输出冲突,并且会开启一个监听用户输入的协程(handlePlayInput),根据用户输入向其他协程发送信号来控制行为
  7. 输入监听在 raw 模式下逐字读取字节,空格控制暂停/播放,-上一首,+下一首,q退出。

歌词实现细节#

  • 借助 github.com/dhowden/tag 读取音频元数据中的歌词(如果存在)。
  • 使用正则匹配时间戳信息和歌词信息
  • lyric.go 实现了逐字解析:把带时间戳的行拆成若干 word(包含相对时间与文本),并把相同时间戳的行配对为原文+译文(lyricPair)。
  • 渲染时根据播放器当前播放样本位置计算当前时间(通过 streamer.Position()format.SampleRate 换算),定位到当前行和当前字并高亮已播放的字(蓝色)和未播放字(深灰)。
  • 为了解决歌词闪烁,每一行只有当变化的时候才会重新渲染(虽然直到现在当逐字歌词词变化速度过快的时候还是会闪烁,我是真服了)

进度条与终端适配#

  • progressBar 读取终端宽度(term.GetSize),减去显示当前/总时长位置后绘制进度块。
  • 时间计算使用 streamer.Len() 与采样率换算为总时长,再通过 Position() 换算当前时间。
  • 绘制时读取实时终端大小,并在每次绘制前清理目标行,借助 utils.Center 使部分文本居中。

代码迭代时间线#

  • 实现了进度条和音乐播放部分
  • 实现了歌词时间戳和原文的解析
  • 实现了歌词的显示
  • 成功解析双语歌词
  • 实现歌词原文的显示
  • 加上歌词译文的显示
  • 同时显示上一句,当前和下一句歌词
  • 实现了逐字歌词的解析
  • 成功显示逐字歌词
  • 能播放文件夹内容
  • 能递归或者不递归播放
  • 显示当前文件夹歌曲和目录
  • 分页显示,可切换页数
  • 能指定页数
  • 显示歌曲名和作者
  • 实现随机播放(单曲,递归所有,当前文件夹所有)
  • 能切换目录
  • 终端大小改变时刷新来防止显示混乱
  • 期间还修复了各种bug,像是闪烁(现在都还没完全解决),显示混乱(并发导致的),播放wav会闪退(似乎是因为标签解析库不能解析wav文件),等等。

下一步计划#

  • 修复当前终端大小改变后歌曲名和作者不显示的问题(
music-cli 开发
https://github.com/sokx6/music-cli/
作者
Locxl
发布于
2025-11-01
许可协议
CC BY-SA 4.0

部分信息可能已经过时

封面
示例歌曲
示例艺术家
封面
示例歌曲
示例艺术家
0:00 / 0:00