2017-07-04 98 views
2

我试图从我的麦克风录音,同时按下按钮,问题是我正在使用的库无法检测到保持事件。它只能检测在印刷机上,其中发生一次,这意味着麦克风只记录一个样本..在按键按键事件中多次调用on_press回调

import pyaudio 
import wave 
from pynput import keyboard 

CHUNK = 8192 
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)   
frames = [] 

def on_press(key): 
    if key == keyboard.Key.cmd_l: 
     print('- Started recording -'.format(key)) 
     try: 
      data = stream.read(CHUNK) 
      frames.append(data) 
     except IOError: 
      print 'warning: dropped frame' # can replace with 'pass' if no message desired 
    else: 
     print('incorrect character {0}, press cmd_l'.format(key)) 


def on_release(key): 
    print('{0} released'.format(
     key)) 
    if key == keyboard.Key.cmd_l: 
     print('{0} stop'.format(key)) 
     keyboard.Listener.stop 
     return False 

print("* recording") 


with keyboard.Listener(on_press=on_press, on_release=on_release) as listener: 
    listener.join() 

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() 

我使用键盘事件和pyaudio记录。 它似乎记录了cmd_l被按下时停止,当我释放。问题是生成的音频文件不包含任何内容,或者很短,如0.19s长。

我想这可能与stream.read有关,必须多次调用,并且按键仅被记录一次,因此只有一个样本正在被记录。

但是,如果是这种情况,我怎么让它调用stream.read多次,同时也检测到它也检测到它被释放时? 或者是否有支持键盘上的on_hold机制的库?

与线程更新方法:

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 

我尝试了不同的方法引入两个线程,其中一个监测键盘,而另一个挑选出的记录。但是这个解决方案不断下降帧,因此没有什么被实际记录?

怎么回事?

回答

0

下面是一个如何做到这一点的示例:我已经通过使单独的线程进行记录来修改代码。键盘按下/释放设置事件的状态,并且当事件被设置时,线程在循环中记录块。一个单独的事件用于告诉线程在完成记录后终止。这为我记录了多个32Kb块(8192 * 2 chans * 16位)。

import pyaudio 
import wave 
from pynput import keyboard 
import threading 

CHUNK = 8192 
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)   
frames = [] 

recordingEvent = threading.Event() # set to activate recording 
exitEvent = threading.Event()   # set to stop recording thread 


def on_press(key): 
    if key == keyboard.Key.ctrl: 
     print('- Started recording -'.format(key)) 
     recordingEvent.set() 
    else: 
     print('incorrect character {0}, press cmd_l'.format(key)) 


def on_release(key): 
    print('{0} released'.format(key)) 
    if key == keyboard.Key.ctrl: 
     print('{0} stop'.format(key)) 
     recordingEvent.clear() 
     keyboard.Listener.stop 
     return False 


def do_recording(): 
    while (not exitEvent.is_set()): 
     if (recordingEvent.wait(0.1)): 
      try: 
       data = stream.read(CHUNK) 
       # print len(data) 
       frames.append(data) 
      except IOError: 
       print 'warning: dropped frame' # can replace with 'pass' if no message desired 


class myRecorder(threading.Thread): 
    def __init__(self): 
     threading.Thread.__init__(self) 
    def run(self): 
     do_recording() 


# start recorder thread 
recordingThread = myRecorder() 
recordingThread.start() 

# monitor keyboard 
with keyboard.Listener(on_press=on_press, on_release=on_release) as listener: 
    listener.join() 

# stop recorder thread 
exitEvent.set()  
recordingThread.join() 

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() 

NB我用Ctrl键代替了Cmd键,因为它更适合我的机器。希望这是有用的。

+0

我试过类似的解决方案(我想,你似乎有比我更多的线程?)...但我不能记录你的或我的,它不断给予IOerror。 – Smo

0

您可以在this answer中执行类似于在单独线程上定期运行函数的功能。似乎是最简单的解决方案。