0

0

无需预设时长,实现声音的持续播放与停止

霞舞

霞舞

发布时间:2025-08-21 19:18:01

|

584人浏览过

|

来源于php中文网

原创

无需预设时长,实现声音的持续播放与停止

本文旨在解决使用 PyAudio 库播放声音时,如何在不预先设定播放时长的情况下,实现声音的持续播放,并通过外部事件(例如按键释放)来停止声音。我们将分析问题代码,找出循环播放的错误原因,并提供修改方案,最终实现灵活的声音控制。

在使用 PyAudio 库进行音频编程时,一个常见的需求是在不预先确定播放时长的情况下,持续播放声音,并根据外部事件(例如,MIDI 控制器的按键释放)来停止播放。原始代码尝试通过一个 while 循环来实现这个功能,但存在一些逻辑上的问题,导致声音无法持续播放,或者无法正确停止。

分析与改进

原始代码的核心问题在于 while play == True 循环内部的逻辑。让我们再次审视一下:

while play == True:
    start_time = time.time()
    stream.write(vysledek)
    time.sleep(1)
    play = False

    stream.stop_stream()
play = True

这段代码的意图是循环播放声音数据 vysledek。然而,在每次循环迭代中,它会执行以下操作:

  1. 写入声音数据 stream.write(vysledek)。
  2. 暂停 1 秒 time.sleep(1)。
  3. 将 play 设置为 False,从而结束循环。
  4. 停止音频流 stream.stop_stream()。
  5. 在循环外部将 play = True,为下次MIDI触发做准备。

因此,循环只执行一次,导致声音只播放一秒钟。此外,stream.stop_stream() 被放置在循环内部,导致每次播放结束后立即停止音频流,这与持续播放的需求相悖。

解决方案

要实现持续播放,我们需要修改代码,移除不必要的 time.sleep(1) 和 play = False,并将 stream.stop_stream() 移到循环外部。更重要的是,我们需要根据MIDI消息来控制play变量,从而控制声音的播放和停止。

如此AI员工
如此AI员工

国内首个全链路营销获客AI Agent

下载

以下是修改后的代码示例:

import time
from rtmidi.midiutil import open_midiinput
import numpy as np
import pyaudio

p = pyaudio.PyAudio()
play = False  # 初始化为False,只有按下按键才播放

volume = 0.5
fs = 44100
fA = 440.0
fB = 493.88
fC = 523.25
fD = 587.33

frekvence = 440

frekvence_seznam = {
    (144, 32): fA,
    (144, 33): fB,
    (144, 34): fC,
    (144, 35): fD,
}

port = 0

midiin, port_name = open_midiinput(port)

stream = None # 初始化stream为None

try:
    while True:
        msg = midiin.get_message()

        if msg:
            message = msg
            klic = message[0]
            lepsi_klic = tuple(klic[:2])
            print(message[0])

            if lepsi_klic in frekvence_seznam:
                print("je to tam")
                frekvence = frekvence_seznam[lepsi_klic]

                period = 2 * np.pi
                x = period * np.arange(fs * 1) * frekvence / fs # duration改为1,或者更小的值,减少计算量

                sinus = np.sin(x)
                square = np.sign(sinus)
                triangle = 2/np.pi * np.arcsin(np.sin(x))
                saw = abs((x % period) - 1)
                curvy_triangle = (abs((x % period) - 1)) ** 2

                samples = (triangle).astype(np.float32)

                vysledek = volume * samples

                # 启动播放
                if not stream: # 如果stream未初始化,则初始化
                    stream = p.open(format=pyaudio.paFloat32,
                                    channels=1,
                                    rate=fs,
                                    output=True)
                play = True # 开始播放

            elif lepsi_klic == (128, 32) or lepsi_klic == (128, 33) or lepsi_klic == (128, 34) or lepsi_klic == (128, 35):  # 假设128代表按键释放
                print("停止播放")
                play = False  # 停止播放

            elif lepsi_klic == (144, 81):
                break # 退出主循环

        if play and stream: # 只有在play为True且stream已初始化时才播放
            stream.write(vysledek)

except KeyboardInterrupt:
    print("Interrupted by user")

finally:
    if stream:
        stream.stop_stream()
        stream.close()
    p.terminate()
    midiin.close_port()
    del midiin

修改说明:

  1. 初始化 play 为 False: 确保程序启动时声音不会自动播放。
  2. 移除 time.sleep(1) 和 play = False: 避免循环提前结束。
  3. 将 stream.stop_stream() 和 stream.close() 移到循环外部: 在程序退出前才关闭音频流。
  4. 根据 MIDI 消息控制 play 变量: 当检测到按键按下时,将 play 设置为 True,当检测到按键释放时,将 play 设置为 False。这里假设MIDI消息144代表按键按下,128代表按键释放,你需要根据你的MIDI设备实际情况进行调整。
  5. 在主循环中持续写入数据: 当 play 为 True 时,持续将声音数据写入音频流。
  6. duration改为1: 在生成声音数据时,duration改为1,或者更小的值,减少计算量,提高响应速度。
  7. 增加stream初始化判断: 在启动播放前,判断stream是否已经初始化,如果没有初始化,则初始化stream。
  8. 异常处理: 增加try...except...finally块,确保程序在异常情况下也能正确关闭音频流和MIDI端口。

注意事项

  • MIDI 消息: 确保你了解你的 MIDI 设备发送的按键按下和释放消息的格式。不同的设备可能使用不同的消息类型和值。
  • 性能: 持续写入音频数据可能会消耗大量的 CPU 资源。如果性能成为问题,可以考虑使用缓冲技术来减少写入频率。
  • 异常处理: 在实际应用中,应该添加更完善的错误处理机制,以应对各种可能出现的异常情况。

总结

通过修改循环逻辑,并根据 MIDI 消息动态控制播放状态,我们成功实现了在不预先设定播放时长的情况下,持续播放声音,并通过外部事件来停止播放的功能。这个方法可以应用于各种需要实时控制音频播放的场景,例如音乐游戏、交互式音频装置等。

相关标签:

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

相关专题

更多
while的用法
while的用法

while的用法是“while 条件: 代码块”,条件是一个表达式,当条件为真时,执行代码块,然后再次判断条件是否为真,如果为真则继续执行代码块,直到条件为假为止。本专题为大家提供while相关的文章、下载、课程内容,供大家免费下载体验。

91

2023.09.25

html编辑相关教程合集
html编辑相关教程合集

本专题整合了html编辑相关教程合集,阅读专题下面的文章了解更多详细内容。

38

2026.01.21

三角洲入口地址合集
三角洲入口地址合集

本专题整合了三角洲入口地址合集,阅读专题下面的文章了解更多详细内容。

19

2026.01.21

AO3中文版入口地址大全
AO3中文版入口地址大全

本专题整合了AO3中文版入口地址大全,阅读专题下面的的文章了解更多详细内容。

255

2026.01.21

妖精漫画入口地址合集
妖精漫画入口地址合集

本专题整合了妖精漫画入口地址合集,阅读专题下面的文章了解更多详细内容。

62

2026.01.21

java版本选择建议
java版本选择建议

本专题整合了java版本相关合集,阅读专题下面的文章了解更多详细内容。

3

2026.01.21

Java编译相关教程合集
Java编译相关教程合集

本专题整合了Java编译相关教程,阅读专题下面的文章了解更多详细内容。

14

2026.01.21

C++多线程相关合集
C++多线程相关合集

本专题整合了C++多线程相关教程,阅读专题下面的的文章了解更多详细内容。

6

2026.01.21

无人机驾驶证报考 uom民用无人机综合管理平台官网
无人机驾驶证报考 uom民用无人机综合管理平台官网

无人机驾驶证(CAAC执照)报考需年满16周岁,初中以上学历,身体健康(矫正视力1.0以上,无严重疾病),且无犯罪记录。个人需通过民航局授权的训练机构报名,经理论(法规、原理)、模拟飞行、实操(GPS/姿态模式)及地面站训练后考试合格,通常15-25天拿证。

28

2026.01.21

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号