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

Posted

技术标签:

【中文标题】输出音频文件未正确创建,或持续时间未知【英文标题】:Output audio file not created correctly, or has unknown duration time 【发布时间】:2017-12-07 00:35:22 【问题描述】:

我目前正在尝试录制一些话语,其中录制会话应在按下并按住某个键时开始,并在释放时停止。我制作了用于记录和存储数据的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 文件的持续时间未知?为什么?

【问题讨论】:

【参考方案1】:

你还没有填写你的名单,frames

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

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

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

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

我希望这对你有用。

干杯!

【讨论】:

好吧..我正在使用回调,所以当listener.stream.start_stream()被调用时,它应该记录。 @Smo 启动流并不会神奇地决定如何使用数据或将数据存储在哪里。你试过运行这个了吗?编辑:您应该重新阅读您的代码。你从来没有真正从流中读取或将信息放入frames,这正是这个答案解释如何做的。 但您的解决方案正在使用预先指定的持续时间,我正在尝试对键盘事件执行此操作。 我似乎已经将问题缩小到回调永远不会被调用.. @eenblam 我知道这个解决方案会起作用,但不是我想要的......你是对的......我目前没有将数据存储在任何地方......但问题似乎是回调没有被调用..【参考方案2】:

我有你要求的第一版。对不起所有这些global。我不熟悉pynput,所以我只是按照文档,使用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 时被调用一次,但在其他任何地方都没有被调用...

以上是关于输出音频文件未正确创建,或持续时间未知的主要内容,如果未能解决你的问题,请参考以下文章

win7 进入传奇提示音频设备无法初始化(插上耳机就可以玩,不想插入耳机如何解决)

FFMPEG:生成输入文件时间最长的7.1声道音频文件

在 C# 中合并两个音频文件

iOS 将 2 个或多个音频文件合并(混合)为 wav 格式

媒体基金会音频/视频捕获到 MPEG4FileSink 产生不正确的持续时间

具有立体声或多个音频流的媒体流的音频持续时间的特定 mediainfo 命令