Skip to content

feat: 听歌识曲 - music recognition (song identification) #1063

Description

@LWWZH

简介与背景

当前 FeelUOwn 缺少听歌识曲(music recognition)功能,用户无法通过音频片段识别正在播放或周围环境中的歌曲。本提案计划实现该功能,让用户能够通过麦克风录音或捕获当前播放器输出进行歌曲识别。

方案概述

通过音频指纹服务(如 ACRCloud / AudD)识别歌曲,将结果匹配到已注册 provider 的歌曲并播放。实现分为两个阶段:Phase 1 支持麦克风模式(识别环境音乐),Phase 2 支持播放器输出捕获(识别当前播放的歌曲)。


Phase 1: 麦克风模式(MVP)

1.1 音频捕获

新增录音模块,使用跨平台库捕获麦克风输入:

  • 方案:sounddevice(基于 PortAudio,跨平台)
  • 录音时长:5-10 秒片段
  • 格式:16-bit PCM,44100 Hz,单声道
  • 输出:WAV 字节流 / base64 编码
  • 抗噪:支持简单的录音前音量检测(避免空白片段)
# feeluown/recognizer/recorder.py
class Recorder:
    def record(self, duration: float = 6) -> bytes:
        """Record from microphone, return WAV bytes."""

    def has_signal(self) -> bool:
        """Check if there is audio signal above threshold."""

1.2 识别服务客户端

集成第三方音频指纹服务:

服务 免费额度 说明
ACRCloud ~1000次/月 成熟 SDK,支持 base64 音频片段,识别率高
AudD ~200次/月 简洁 REST API,支持文件/URL/麦克风

选择 ACRCloud 作为首选(成熟度最高),AudD 作为备选。

# feeluown/recognizer/service.py
class RecognizerService:
    def __init__(self, config):
        self.host = config.ACR_HOST
        self.access_key = config.ACR_ACCESS_KEY
        self.access_secret = config.ACR_ACCESS_SECRET

    async def recognize(self, audio_data: bytes) -> RecognitionResult | None:
        """Send audio data to recognition service, return result."""

@dataclass
class RecognitionResult:
    title: str
    artists: list[str]
    album: str | None
    score: float       # confidence 0~100
    duration: int      # ms
    external_url: str  # link to the service's result page

1.3 歌曲匹配

识别结果匹配到本地 provider 的歌曲:

# feeluown/recognizer/matcher.py
class SongMatcher:
    def __init__(self, library):
        self._library = library

    async def match(self, result: RecognitionResult) -> list[SongModel]:
        """Search in all providers and return matched songs sorted by confidence."""

利用现有 app.library.a_search() 跨 provider 搜索,按标题/艺术家相似度排序。

1.4 配置管理

在设置中添加识别服务配置项(通过 config.deffield()):

配置项 说明
RECOGNIZER_ENABLED 启用/禁用(bool,默认 false)
RECOGNIZER_SERVICE 服务商选择("acrcloud" / "audd")
RECOGNIZER_ACR_HOST ACRCloud 主机地址
RECOGNIZER_ACR_ACCESS_KEY ACRCloud Access Key
RECOGNIZER_ACR_ACCESS_SECRET ACRCloud Access Secret(加密存储)
RECOGNIZER_RECORD_DURATION 录音时长秒数(int,默认 6)
RECOGNIZER_SHOW_NOTIFICATION 识别成功后显示通知(bool,默认 true)

1.5 UI 集成

在播放器控制栏添加"识别"按钮:

按钮行为:

  1. 点击后按钮变为红色脉冲动画,开始录音
  2. 录音完成后发送识别请求
  3. 成功:弹出结果对话框,显示匹配歌曲,可点击播放
  4. 失败:显示 msg("未识别" / "网络错误")

结果对话框RecognizerResultDialog):

  • 显示识别到的歌曲信息(封面、标题、艺术家、专辑)
  • 匹配到的 provider 歌曲列表(可点击播放)
  • "重新识别" 按钮
  • 自动添加到播放历史的选项

1.6 键盘快捷键

新增全局快捷键:

  • Ctrl+Shift+R / Cmd+Shift+R:开始听歌识曲
  • 可在设置中自定义

Phase 2: 播放器输出捕获(后续迭代)

难点:当前播放器基于 libmpvfeeluown/player/mpvplayer.py),不暴露原始 PCM 音频数据,无法直接获取当前播放的音频流。

方案探索(三选一)

方案 优势 劣势
① mpv ao_capture 纯软件方案,无额外依赖 需扩展 python-mpvfeeluown/mpv.py),CFFI 绑定层改动大
② OS 级音频回环 通用方案,不依赖 mpv 跨平台差异大:WASAPI/Win + PulseAudio/Linux + CoreAudio/macOS;实现复杂
③ 虚拟音频设备 稳定,音质无损 需用户安装驱动(如 VB-Cable、BlackHole),门槛高

建议优先探索 方案①ao_capture),如不可行则转用 方案②(平台适配)。

Phase 2 的 UI 行为会有所不同:

  • 识别当前正在播放的歌曲
  • 按钮可显示"识别当前播放"
  • 支持识别结果与当前歌曲对比(确认播放的是什么版本)

文件结构

feeluown/recognizer/              # 新建模块
├── __init__.py                   # 插件入口:enable/disable
├── recorder.py                   # 音频录制
├── service.py                    # 识别服务客户端
├── matcher.py                    # 歌曲匹配
├── models.py                     # RecognitionResult 等数据模型
└── ui.py                         # 按钮、对话框等 UI 组件

修改的现有文件:

  • feeluown/gui/uimain/player_bar.py — 添加识别按钮
  • feeluown/gui/components/btns.py — 新增识别按钮组件
  • feeluown/app/app.py — 注册 recognizer 模块(可选)

实现步骤(PR 拆分建议)

PR 1: 基础设施(~300 行)

  • 创建 feeluown/recognizer/ 模块和插件入口
  • 实现 models.py(RecognitionResult 等)
  • 实现 recorder.py(麦克风录音)
  • 配置项注册

PR 2: 识别服务集成(~250 行)

  • 实现 service.py(ACRCloud 客户端)
  • 实现 matcher.py(跨 provider 匹配)
  • 错误处理和重试逻辑

PR 3: UI 与交互(~200 行)

  • 添加识别按钮到播放器栏
  • 实现结果对话框
  • 键盘快捷键支持
  • 通知提示

PR 4: 完善与优化

  • 播放器输出捕获(Phase 2,难度高)
  • 识别历史记录
  • 批量识别(识别本地文件夹)
  • 离线指纹(Chromaprint/AcoustID)

不在本次范围内

  • 完整的离线音频指纹库(需要大量歌曲数据训练)
  • 歌词自动滚动匹配(需要实时音频对齐)
  • 翻唱/现场版识别(指纹服务能力有限)
  • 跨平台音频回环捕获的完整实现(Phase 2 作为探索性工作)

参考

  • ACRCloud API 文档
  • AudD API 文档
  • feeluown/player/mpvplayer.py — 当前播放器实现
  • feeluown/mpv.py — python-mpv CFFI 绑定
  • feeluown/gui/uimain/player_bar.py — 播放器控制栏 UI
  • feeluown/gui/components/btns.py — 现有按钮组件
  • feeluown/utils/request.py — 网络请求工具
  • feeluown/plugin.py — 插件系统
  • feeluown/local/__init__.py — 参考插件实现

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions