PyAudio 响应式录音

4

我在PyAudio网站上看到了有关录制固定长度录音的教程,但我想知道如何使用非固定录音实现相同的操作?基本上,我想创建按钮来启动和结束录音,但我没有找到任何相关信息。 有什么想法吗?我不想使用其他库。

3个回答

8
最好使用非阻塞的录音方式,即提供一个回调函数,该函数从您启动流的那一刻开始调用,并在处理每个块/缓冲区时继续调用,直到您停止流为止。
在回调函数中,您可以检查一个布尔值,例如,当它为真时,将传入的缓冲区写入数据结构,当它为假时忽略传入的缓冲区。例如,可以通过单击按钮设置此布尔值。
编辑:请查看 wire audio 的示例:http://people.csail.mit.edu/hubert/pyaudio/#wire-callback-example。该流是使用一个参数打开的。
stream_callback=my_callback

如果my_callback是一个常规函数,声明为

def my_callback(in_data, frame_count, time_info, status)

每当有新的缓冲区可用时,此函数将被调用。in_data包含您想要记录的输入。在这个例子中,in_data只是与pyaudio.paContinue一起以元组的形式返回。这意味着从输入设备传入的缓冲区被放置/复制回发送到输出设备的输出缓冲区(它们是同一设备,因此实际上是将输入路由到输出,也就是线)。有关更多解释,请参见API文档:http://people.csail.mit.edu/hubert/pyaudio/docs/#pyaudio.PyAudio.open 所以在这个函数中,您可以做类似于以下代码的事情(这是我编写的一些代码的摘录,不完整:我使用了一些未显示的函数。另外,我在一个通道上播放正弦波,在另一个通道上播放噪声,以24位格式进行)。

record_on = False
playback_on = False

recorded_frames = queue.Queue()

def callback_play_sine(in_data, frame_count, time_info, status):
    if record_on:
        global recorded_frames
        recorded_frames.put(in_data)

    if playback_on:
        left_channel_data = mysine.next_block(frame_count) * MAX_INT24 * gain
        right_channel_data = ((np.random.rand(frame_count) * 2) - 1) * MAX_INT24 * gain
        data = interleave_channels(max_nr_of_channels, (left_output_channel, left_channel_data), (right_output_channel, right_channel_data))
        data = convert_int32_to_24bit_bytestream(data)
    else:
        data = np.zeros(frame_count*max_nr_of_channels).tostring()

    if stop_callback:
        callback_flag = pyaudio.paComplete
    else:
        callback_flag = pyaudio.paContinue

    return data, callback_flag

您可以在流正在运行的同时,从代码的其他部分将record_onplayback_on设置为TrueFalse,从而独立启动或停止录制和播放,而不会中断流。我将in_data复制到一个(线程安全的)队列中,另一个线程用它来写入磁盘,否则队列会在一段时间后变得很大。
顺便说一下:pyaudio基于portaudio,后者有更多的文档和有用的提示。例如(http://portaudio.com/docs/v19-doxydocs/writing_a_callback.html):回调函数必须在呈现新缓冲区之前完成,否则缓冲区将丢失。因此,在回调函数内部写入文件通常不是一个好主意。(虽然写入文件会被缓冲,并且我不知道它在最终写入磁盘时是否会阻塞)

我不太明白你的意思。你能进一步解释一下这个方法吗? - Malik Brahimi
我不熟悉C或C++,你能否解释一些Python呢? - Malik Brahimi
这可能再简单不过了吧? - Malik Brahimi
我找到了一个更好的方法,使用Pygame,只需在程序更新时添加即可,与普通录制程序一样。 - Malik Brahimi
1
这是我用来模拟按钮按下的工具。请参见下方。 - Malik Brahimi
显示剩余5条评论

0
import pyaudio
import wave

import pygame, sys
from pygame.locals import *

pygame.init()
scr = pygame.display.set_mode((640, 480))
recording = True

CHUNK = 1024
FORMAT = pyaudio.paInt16
CHANNELS = 2
RATE = 44100
RECORD_SECONDS = 5
WAVE_OUTPUT_FILENAME = "output.wav"

p = pyaudio.PyAudio()

stream = p.open(format=FORMAT,
                channels=CHANNELS,
                rate=RATE,
                input=True,
                frames_per_buffer=CHUNK)

print("* recording")

frames = []

while True:
    if recording:
        data = stream.read(CHUNK)
        frames.append(data)

    for event in pygame.event.get():
        if event.type == KEYDOWN and recording:
            print("* done recording")

            stream.stop_stream()
            stream.close()
            p.terminate()

            wf = wave.open(WAVE_OUTPUT_FILENAME, 'wb')
            wf.setnchannels(CHANNELS)
            wf.setsampwidth(p.get_sample_size(FORMAT))
            wf.setframerate(RATE)
            wf.writeframes(b''.join(frames))
            wf.close()
            recording = False

        if event.type == QUIT:
            pygame.quit(); sys.exit()

-1

当我将其编译为exe时,这就是我想到的。将参数传递给

exeparser = argparse.ArgumentParser()
parser.add_argument('-t', dest='time', action='store')
args = parser.parse_args()
time = int(args.time)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接