输出音频文件未正确创建或持续时间未知。

9

我目前正在尝试录制一些话语,在这个过程中,记录会话应该在按下并按住键时开始,并在释放时停止。我编写了用于记录和存储数据的Python脚本..

from pynput import keyboard
import time
import pyaudio
import wave

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

p = pyaudio.PyAudio()
frames = []

def callback(in_data, frame_count, time_info, status):
    return (in_data, pyaudio.paContinue)

class MyListener(keyboard.Listener):
    def __init__(self):
        super(MyListener, self).__init__(self.on_press, self.on_release)
        self.key_pressed = None

        self.stream = p.open(format=FORMAT,
                             channels=CHANNELS,
                             rate=RATE,
                             input=True,
                             frames_per_buffer=CHUNK,
                             stream_callback = self.callback)
        print self.stream.is_active()

    def on_press(self, key):
        if key == keyboard.Key.cmd_l:
            self.key_pressed = True

    def on_release(self, key):
        if key == keyboard.Key.cmd_l:
            self.key_pressed = False

    def callback(self,in_data, frame_count, time_info, status):
        if self.key_pressed == True:
            return (in_data, pyaudio.paContinue)
        elif self.key_pressed == False:
            return (in_data, pyaudio.paComplete)
        else:
            return (in_data,pyaudio.paAbort)


listener = MyListener()
listener.start()
started = False

while True:
    time.sleep(0.1)
    if listener.key_pressed == True and started == False:
        started = True
        listener.stream.start_stream()
        print "start Stream"

    elif listener.key_pressed == False and started == True:
        print "Something coocked"
        listener.stream.stop_stream()
        listener.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()

        started = False

脚本存在的问题是音频文件似乎没有录制任何内容,当我播放它时,文件的持续时间是未知的?

我不确定这里可能出了什么问题..?

更新:

新版本带有输出:

from pynput import keyboard
import time
import pyaudio
import StringIO
import multiprocessing
from multiprocessing import Process, Queue, queues
import wave

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

p = pyaudio.PyAudio()
frames = []

stream_queue = Queue()



class MyListener(keyboard.Listener):
    def __init__(self):
        super(MyListener, self).__init__(on_press=self.on_press, on_release=self.on_release)
        self.key_pressed = None


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

        print ("Stream active? " + str(self.stream.is_active()))

    def on_press(self, key):
        if key == keyboard.Key.cmd_l:
            self.key_pressed = True

    def on_release(self, key):
        if key == keyboard.Key.cmd_l:
            self.key_pressed = False

    def callback(self,in_data, frame_count, time_info, status):
        print "callback"
        if self.key_pressed == True:
            #stream_queue.put(in_data)
            frames.append(data)
            return (in_data, pyaudio.paContinue)

        elif self.key_pressed == False:
            #stream_queue.put(in_data)
            frames.append(data)
            return (in_data, pyaudio.paComplete)

        else:
            return (in_data,pyaudio.paAbort)


listener = MyListener()
listener.start()
started = False

while True:
    time.sleep(0.1)
    if listener.key_pressed == True and started == False:
        started = True
        listener.stream.start_stream()
        print ("Start stream -  Key is down")

    elif listener.key_pressed == True and started == True:
        print("stream has started and key is still down")
        print("Stream is active? " + str(listener.stream.is_active()))
        print("Stream is stopped? " + str(listener.stream.is_stopped()))
        print("Stream is time? " + str(listener.stream.get_time()))

    elif listener.key_pressed == False and started == True:
        print("Key has been released")
        listener.stream.stop_stream()
        listener.stream.close()
        print("stream has been closed")
        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()

        started = False

输出:

python File2.py 
Stream active? True
callback
Start stream -  Key is down
stream has started and key is still down
Stream is active? False
Stream is stopped? False
Stream is time? 134638.797766
stream has started and key is still down
Stream is active? False
Stream is stopped? False
Stream is time? 134638.902259
stream has started and key is still down
Stream is active? False
Stream is stopped? False
Stream is time? 134639.006739
stream has started and key is still down
Stream is active? False
Stream is stopped? False
Stream is time? 134639.111282
stream has started and key is still down
Stream is active? False
Stream is stopped? False
Stream is time? 134639.215573
stream has started and key is still down
Stream is active? False
Stream is stopped? False
Stream is time? 134639.320448
stream has started and key is still down
Stream is active? False
Stream is stopped? False
Stream is time? 134639.424682
stream has started and key is still down
Stream is active? False
Stream is stopped? False
Stream is time? 134639.528631
stream has started and key is still down
Stream is active? False
Stream is stopped? False
Stream is time? 134639.633699
stream has started and key is still down
Stream is active? False
Stream is stopped? False
Stream is time? 134639.738129
stream has started and key is still down
Stream is active? False
Stream is stopped? False
Stream is time? 134639.842747
Key has been released
stream has been closed
^CTraceback (most recent call last):
  File "File2.py", line 67, in <module>
    time.sleep(0.1)
KeyboardInterrupt
MacBook-Pro:~$ play output.wav 

output.wav:

 File Size: 44        
  Encoding: Signed PCM    
  Channels: 2 @ 16-bit   
Samplerate: 44100Hz      
Replaygain: off         
  Duration: unknown      

In:0.00% 00:00:00.00 [00:00:00.00] Out:0     [      |      ]        Clip:0    
Done.

我觉得奇怪的是:

  • listener.stream.start_stream()之后,流不活跃
  • callback回调消息只被打印一次,但应该在每次回调存储数据到帧时打印,这似乎只发生了一次
  • 输出的output.wav文件持续时间未知?为什么?
2个回答

5

我有你的请求的第一版。抱歉使用了这么多全局变量,因为我不熟悉 pynput,所以我只是按照文档中最简单的例子进行了操作。以下是代码,可以在win7和python3上正常运行。按住 space 开始录制,按 esc 退出脚本。

from pynput import keyboard
import pyaudio
import wave
import time

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

record_on = False
complete_tag = False
frames = []

def callback(in_data, frame_count, time_info, status):
    print("callback called")
    callback_flag = pyaudio.paContinue
    # global record_on
    if record_on:
        # global frames
        frames.append(in_data)
    if complete_tag:
        callback_flag = pyaudio.paComplete

    return in_data, callback_flag

def on_press(key):
    global record_on
    print(record_on)
    if key == keyboard.Key.space:
        record_on = True

def on_release(key):
    global record_on
    global complete_tag
    record_on = False
    complete_tag = True
    if key == keyboard.Key.esc:
        return False

if __name__ == '__main__':
    p = pyaudio.PyAudio()
    stream = p.open(format=FORMAT,
                channels=CHANNELS,
                rate=RATE,
                input=True,
                frames_per_buffer=CHUNK,
                stream_callback=callback)
    with keyboard.Listener(
            on_press=on_press,
            on_release=on_release) as listener:
        listener.join()
    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()

更新:

我刚刚重写了你的回调函数,这可能对你来说很好用,但对我来说不是。

def callback(self,in_data, frame_count, time_info, status):
    print("callback")
    if self.key_pressed == True:
        #stream_queue.put(in_data)
        print("record")
        frames.append(in_data)
        return (in_data, pyaudio.paContinue)

    elif self.key_pressed == False:
        #stream_queue.put(in_data)
        frames.append(in_data)
        return (in_data, pyaudio.paComplete)

    else:
        print("not record")
        return (in_data,pyaudio.paContinue)

你不理解回调函数,当你调用 p.open with callback 时,数据从硬件传输时将调用回调函数。因此,逻辑应该像我的版本一样写在回调函数中,而不是像 while 1: time.sleep(0.1) 这样的代码块中。
所以,你所有的问题都在第一个回调函数调用后,流接收到PAabort,然后停止流。因此,回调函数只会被调用一次,所以你的 .wav 文件只有元数据,没有持续时间。
我已经修改了你的整个代码。
from pynput import keyboard
import pyaudio
import wave

CHUNK = 8192
FORMAT = pyaudio.paInt16
CHANNELS = 2
RATE = 44100
WAVE_OUTPUT_FILENAME = "output.wav"

class MyListener(keyboard.Listener):
    def __enter__(self):
        self.p = pyaudio.PyAudio()
        self.stream = self.p.open(format=FORMAT,
                             channels=CHANNELS,
                             rate=RATE,
                             input=True,
                             frames_per_buffer=CHUNK,
                             stream_callback = self.callback)
        self.start()
        return self
    def __init__(self):
        super(MyListener, self).__init__(on_press=self.on_press, on_release=self.on_release)
        self.key_pressed = False
        self.complete_tag = False
        self.frames = []

    def on_press(self, key):
        if key == keyboard.Key.space:
            self.key_pressed = True

    def on_release(self, key):
        if key == keyboard.Key.space:
            self.key_pressed = False
            self.complete = True
        if key == keyboard.Key.esc:
            return False

    def callback(self,in_data, frame_count, time_info, status):
        callback_flag = pyaudio.paContinue
        if self.key_pressed:
            self.frames.append(in_data)
        if self.complete_tag:
            callback_flag = pyaudio.paComplete
        return in_data, callback_flag

    def __exit__(self, exc_type, exc_value, traceback):
        self.stream.stop_stream()
        self.stream.close()
        self.p.terminate()
        self.stop()
        wf = wave.open(WAVE_OUTPUT_FILENAME, 'wb')
        wf.setnchannels(CHANNELS)
        wf.setsampwidth(self.p.get_sample_size(FORMAT))
        wf.setframerate(RATE)
        wf.writeframes(b''.join(self.frames))
        wf.close()

with MyListener() as listener:
        listener.join()

是的...这个解决方案可行...但是我仍然有点困惑为什么我的版本不起作用...我似乎已经定位到问题出在回调函数没有被调用,或者只在p.open时被调用一次,但在其他地方没有被调用... - Smo

2

您还没有填写您的列表frames

您应该这样做来填充您的列表:

for i in range(0, int(RATE / CHUNK * RECORD_SECONDS)):
    data = stream.read(CHUNK)
    frames.append(data)

因为您在这里使用了一个空列表:

wf.writeframes(b''.join(frames))

我希望这对你有用。祝好!

@Smo,启动流并不能神奇地决定如何消耗数据或将其存储在哪里。你试过运行这个程序吗?编辑:你应该重新阅读你的代码。你实际上从流中没有读取任何信息或将信息放入“frames”中,而这正是这个答案所解释的。 - eenblam
但是你的解决方案使用了预先指定的持续时间,而我正试图使用键盘事件来实现。 - Smo
我似乎已经将问题缩小到回调从未被调用。 - Smo
@Smo 好的,我仔细检查了文档。你不调用 read() 是正确的,因为你不想阻塞。接下来:你有多确定回调函数没有被调用?它是在单独的线程中执行的。 - eenblam
@smo “每次进入while循环时都应该调用它”...是吗?1.如果按下键并且您正在录制,则给定流程控制语句,while循环应继续。2.回调的执行取决于新数据到达缓冲区的时间,而不是while循环中发生的事情,除了开始/停止事件。...或者您是指每次进入“if listener.key_pressed == True and started == False:”块时? - eenblam
显示剩余4条评论

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