当前位置:首页 > 文章列表 > Golang > Go教程 > Go beep 怎样给音频流添加暂停控制

Go beep 怎样给音频流添加暂停控制

来源:17golang原创 2026-10-31 05:43:05 0浏览 收藏

我在给 Go 播放器加“暂停/继续”按钮时,最省事的做法不是改写底层音频生成器,而是用 beep.Ctrl 包一层 beep.Streamer。把 Paused 设为 true 时,Ctrl 继续向扬声器提供流,但输出的是静音;恢复时改回 false。如果播放中的 Ctrl 要被修改,还要把变更放在 speaker.Lock 和 speaker.Unlock 之间。

官方地址:https://pkg.go.dev/github.com/gopxl/beep

下面只讨论单个音频流的暂停控制,不把“暂停静音”“停止当前流”和“暂停整个扬声器”混成一个动作。

beep.Ctrl 为什么能暂停 Streamer

beep.Ctrl 的职责很小:它保存一个被包装的 Streamer 和一个 Paused 布尔字段。正常状态下,Ctrl 把底层流的数据继续交给 Stream;暂停状态下,Ctrl 让这一段输出变成静音。这样做的好处是底层解码位置仍由原来的 Streamer 管理,恢复播放时不会因为重新创建对象而从头开始。

从使用关系上看,应该让 speaker.Play 接收 Ctrl,而不是直接接收原始 Streamer:

Go beep 原始 Streamer 经过 beep.Ctrl 和 Paused 状态控制后交给 speaker 的静态结构图
图1:结构说明图,展示 Streamer 进入 Ctrl 后由 Paused 控制输出状态,不是运行截图。
package main

import (
    "github.com/gopxl/beep"
    "github.com/gopxl/beep/speaker"
)

func start(stream beep.Streamer, rate beep.SampleRate) *beep.Ctrl {
    // Ctrl 只负责增加暂停状态,不改变底层 Streamer 的类型。
    ctrl := &beep.Ctrl{Streamer: stream}

    // 初始化扬声器后始终播放 Ctrl,后续按钮只修改 ctrl.Paused。
    if err := speaker.Init(rate, rate.N(100000000)); err != nil {
        panic(err)
    }
    speaker.Play(ctrl)
    return ctrl
}

示例中的缓冲区是约 100 毫秒,换算成纳秒后传给 SampleRate.N。实际项目应按响应速度和播放稳定性调整这个值;这里的重点是把控制对象交给 speaker,而不是声称某个固定缓冲区适合所有设备。

播放中修改 Paused 要锁住 speaker

播放开始后,speaker 会持续从正在播放的 Streamer 拉取样本。如果此时另一个 goroutine 直接改 ctrl.Paused 或替换 ctrl.Streamer,就可能和读取过程同时发生。官方 speaker 文档给出的边界是:修改正在播放的 Streamer 前先锁住 speaker,改完立即解锁。

func setPaused(ctrl *beep.Ctrl, paused bool) {
    // 锁定期间 speaker 不会继续拉取正在播放的 Streamer。
    speaker.Lock()
    ctrl.Paused = paused
    // 只在修改控制字段的最短区间内持锁,避免造成播放卡顿。
    speaker.Unlock()
}

按钮、快捷键或定时器都可以调用这个函数。暂停和恢复都只改一个状态字段,因此锁的持有时间应尽量短;不要把文件读取、网络请求或复杂业务逻辑放进锁内。

Go beep 播放线程通过 speaker.Lock 保护 Ctrl.Paused 修改并在结束时释放 Ctrl.Streamer 的边界图
图2:边界示意图,展示 speaker 锁保护控制对象修改以及底层流释放关系,不是运行结果。

暂停静音和彻底停止不是一回事

Paused=true 代表“继续保留这个流,但当前输出静音”。它适合用户点击暂停后稍后继续的场景。官方文档还说明,如果把 Ctrl.Streamer 设为 nil,Ctrl 会被视为 drained,也就是不再继续消费原来的流。

func stop(ctrl *beep.Ctrl) {
    speaker.Lock()
    // nil 表示不再保留底层流;这不是可恢复的暂停。
    ctrl.Streamer = nil
    speaker.Unlock()
}

如果业务还要支持“停止后重新播放”,建议把原始流的创建与 Ctrl 的控制分开:停止时释放旧的解码流,下一次播放重新打开资源并创建新的 Ctrl。不要把已经置为 nil 的 Ctrl 当成暂停对象继续复用。

资源生命周期要跟着底层流走

Ctrl 本身只是包装层,不会替你关闭文件、解码器或其他实现了 beep.StreamCloser 的资源。创建可关闭的音频流后,要让拥有资源的一方负责关闭;如果播放是异步的,不能在调用 speaker.Play 后立刻退出主函数或提前关闭底层流。

func playUntilDone(stream beep.StreamSeekCloser, rate beep.SampleRate) {
    defer stream.Close()

    ctrl := &beep.Ctrl{Streamer: stream}
    if err := speaker.Init(rate, rate.N(100000000)); err != nil {
        // 初始化失败时仍由当前函数关闭已经拿到的流。
        panic(err)
    }

    // 有限流用 PlayAndWait,避免主函数先退出导致播放被截断。
    speaker.PlayAndWait(ctrl)
}
需求应该做什么不要怎么做
暂时没有声音在锁内设置 Paused=true重新解码并丢弃当前流
恢复播放在锁内设置 Paused=false直接并发修改播放中的对象
彻底停止在锁内将 Streamer 置为 nil,再关闭拥有者资源把停止后的 Ctrl 当作可恢复暂停
有限音频播放使用 PlayAndWait 或自行等待结束调用异步 Play 后立即退出

结论与相关问题

Go beep 给音频流添加暂停控制,可以按“原始 Streamer → beep.Ctrl → speaker.Play”组织。暂停只切换 Paused,继续播放再切回 false;播放中修改状态要短暂锁住 speaker;真正停止则应让 Streamer 脱离 Ctrl,并把底层资源的关闭责任收好。

  • 暂停后会不会丢失当前播放位置?不会,Ctrl 仍然包着原来的 Streamer;它只是暂时输出静音。
  • 可以用 speaker.Suspend 暂停单个音频吗?不适合。Suspend 面向整个扬声器,单个流应使用 beep.Ctrl。
  • 为什么修改 Paused 需要 Lock?因为 speaker 可能正在从播放中的 Streamer 拉取数据,锁用于避免并发修改。
版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
Go beep 怎样把两个音频流混合播放Go beep 怎样把两个音频流混合播放
上一篇
Go beep 怎样把两个音频流混合播放
Go beep 怎样把有限音频流包装为循环流
下一篇
Go beep 怎样把有限音频流包装为循环流
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    543次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    516次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    500次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    485次学习
查看更多
AI推荐
  • PubMedQA数据集详解:生物医学问答基准、功能与应用指南
    PubMedQA
    深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
    804次使用
  • H2O EvalGPT:开源LLM大模型评估与排行榜工具
    H2O EvalGPT
    H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
    795次使用
  • LMArena是什么?伯克利AI模型评估平台使用指南与功能解析
    LMArena
    LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
    883次使用
  • 斯坦福HELM:大语言模型Holistic Evaluation整体评估框架详解
    HELM
    深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
    853次使用
  • MMBench详解:多模态大模型基准测试、功能特点与使用指南
    MMBench
    MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
    683次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议 和 隐私政策
返回登录
  • 重置密码