声音设备输出溢出

问题描述 投票:0回答:1

我在使用 Python 的 sounddevice 模块时遇到了未知的麻烦。 下面的代码输出一个 GUI,您可以在其中输出用户定义频率的正弦波。此外,还可以使用用户定义的频率和幅度/量的正弦波来调制其幅度。 按下“播放”按钮后,我收到输出溢出并且音频立即开始滞后。 如果我删除代码中的幅度调制选项并且听到平滑的正弦信号,则不会出现此问题。

我知道这段代码可能会使用太多的 CPU 时间来保证音频信号的流畅。

有人在类似的项目中遇到过类似的问题吗?或者有人对声音设备模块足够熟悉来帮助我吗?

代码的很大一部分是从 Github 上的这个示例复制粘贴的。

我绝不是一个经验丰富的程序员。

非常感谢任何帮助!

import argparse import sys import tkinter as tk import sounddevice as sd import numpy as np # the following function and parsing part is copy-paste from the link above # and with a few modifications of the remaining code wouldn't be crucial I guess. # I left it there because I wanted to spare myself the modifications. def int_or_str(text): """Helper function for argument parsing.""" try: return int(text) except ValueError: return text parser = argparse.ArgumentParser(add_help=False) parser.add_argument( '-l', '--list-devices', action='store_true', help='show list of audio devices and exit') args, remaining = parser.parse_known_args() if args.list_devices: print(sd.query_devices()) parser.exit(0) parser = argparse.ArgumentParser( description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter, parents=[parser]) parser.add_argument( 'frequency', nargs='?', metavar='FREQUENCY', type=float, default=500, help='frequency in Hz (default: %(default)s)') parser.add_argument( '-d', '--device', type=int_or_str, help='output device (numeric ID or substring)') parser.add_argument( '-a', '--amplitude', type=float, default=0.2, help='amplitude (default: %(default)s)') args = parser.parse_args(remaining) start_idx = 0 def play(): samplerate = sd.query_devices(args.device, 'output')['default_samplerate'] # the callback function is basically copy-paste from the link abouve # except I added the modulating sine wave def callback(outdata, frames, time, status): if status: print(status, file=sys.stderr) global start_idx t = (start_idx + np.arange(frames)) / samplerate t = t.reshape(-1, 1) am_amount = float(am_amt_1.get()) a_modulator = np.sin(2*np.pi*float(am_freq_1.get())*t)*am_amount+1-am_amount carrier = np.sin(2 * np.pi * float(freq_1.get()) * t) outdata[:] = carrier*a_modulator start_idx += frames with sd.OutputStream(device=args.device, channels=1, callback=callback, samplerate=samplerate): input() #don't really know what this is doing but there's no audio at all without it # setting up the GUI main = tk.Tk() main.title('Test') main.geometry('10000x10000') # entries for frequency in hz and am amount freq_1 = tk.Entry(main) freq_1.grid(row = 0, column = 1) am_freq_1 = tk.Entry(main) am_freq_1.grid(row=1,column=1) am_amt_1 = tk.Entry(main) am_amt_1.grid(row=2,column=1) # labels f1 = tk.Label(main,text='Frequency') f1.grid(row=0,column=0) amf_1 = tk.Label(main,text='AM Frequency') amf_1.grid(row=1,column=0) amamt_1 = tk.Label(main,text='AM Amount') amamt_1.grid(row=2,column=0) # play button executing the above play function pl = tk.Button(main, text='Play',command = play) pl.grid(row=3,column=1) main.mainloop()
我使用笔记本电脑的内置输出设备和 M-Audio Fast Track Pro 尝试了该程序。

python output audio-processing python-sounddevice modulation
1个回答
0
投票
正如我所怀疑的,只要您不在回调中调用 tkinter 函数,问题就会解决;如果将值传递给 play 函数,一切似乎都正常。

为了简洁起见,我还对其余部分进行了一些重构。您需要在

input()

 中添加 
with
 的原因是设备会立即关闭;我在这里用 sleep 代替了它,所以声音播放 2 秒然后退出。

如果您的声音设备名称中不包含

Speakers

,您需要在 
device=...
 行中进行更改。

import sys import time import tkinter as tk import numpy as np import sounddevice as sd def play(*, device, freq: float, am_freq: float, am_amount: float): samplerate = sd.query_devices(device, "output")["default_samplerate"] start_idx = 0 def callback(outdata, frames, time, status): nonlocal start_idx if status: print(status, file=sys.stderr) t = (start_idx + np.arange(frames)) / samplerate t = t.reshape(-1, 1) a_modulator = ( np.sin(2 * np.pi * am_freq * t) * am_amount + 1 - am_amount ) carrier = np.sin(2 * np.pi * freq * t) outdata[:] = carrier * a_modulator start_idx += frames with sd.OutputStream( device=device, channels=1, callback=callback, samplerate=samplerate, ): time.sleep(2) # Play for a while def make_box(parent, label, value, y): box = tk.Entry(parent) box.insert(0, value) label = tk.Label(parent, text=label) label.grid(row=y, column=0) box.grid(row=y, column=1) return (label, box) def main(): root = tk.Tk() root.title("Test") freq_label, freq_box = make_box(root, "Frequency", "500", 0) am_freq_label, am_freq_box = make_box(root, "AM Frequency", "25", 1) am_amt_label, am_amt_box = make_box(root, "AM Amount", "0.5", 2) pl = tk.Button( root, text="Play", command=( lambda: play( device="Speakers", freq=float(freq_box.get()), am_freq=float(am_freq_box.get() or 0), am_amount=float(am_amt_box.get() or 0), ) ), ) pl.grid(row=3, column=1) root.mainloop() if __name__ == "__main__": main()
    
© www.soinside.com 2019 - 2024. All rights reserved.