Grabación responsiva de PyAudio

He visto el tutorial de grabación en el sitio web de PyAudio para grabar una grabación de longitud fija, pero me preguntaba cómo podría hacer lo mismo con una grabación no fija. Básicamente, quiero crear botones para iniciar y finalizar la grabación, pero no he encontrado nada al respecto. ¿Alguna idea, y no estoy buscando una biblioteca alternativa?

Lo mejor es utilizar la forma de grabación no bloqueante, es decir, que proporciona una función de callback que recibe llamadas desde el momento en que inicia la transmisión y sigue siendo llamada para cada bloque / búfer que se procesa hasta que detenga la transmisión.

En esa función de callback, por ejemplo, busca un valor booleano, y cuando es cierto, escribe el búfer entrante en una estructura de datos, cuando es falso, ignora el búfer entrante. Este booleano se puede configurar haciendo clic en un botón, por ejemplo.

EDITAR: vea el ejemplo de cable de audio: http://people.csail.mit.edu/hubert/pyaudio/#wire-callback-example La secuencia se abre con un argumento

stream_callback=my_callback 

Donde my_callback es una función regular declarada como

 def my_callback(in_data, frame_count, time_info, status) 

Esta función se llamará cada vez que haya un nuevo búfer disponible. in_data contiene la entrada, que desea grabar. En este ejemplo, in_data simplemente se devuelve en una tupla junto con pyaudio.paContinue . Lo que significa que el búfer entrante del dispositivo de entrada se vuelve a copiar en el búfer de salida que se envía al dispositivo de salida (es el mismo dispositivo, por lo tanto, su entrada de enrutamiento a la salida también se conoce como cable). Consulte la documentación de la API para obtener una explicación más detallada: http://people.csail.mit.edu/hubert/pyaudio/docs/#pyaudio.PyAudio.open

Entonces, en esta función puedes hacer algo como (esto es un extracto de un código que he escrito, que no está completo: uso algunas funciones que no se muestran. También reproduzco una onda sinusoidal en un canal y ruido en el otro en formato de 24 bits .):

 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 

Luego, puede establecer record_on y playback_on en True o False desde otra parte de su código mientras el flujo está abierto / en ejecución, lo que hace que la grabación y la reproducción se inicien o detengan independientemente sin interrumpir el flujo. Copio el in_data en una queue (segura para subprocesos), que es utilizada por otro hilo para escribir en el disco allí, de lo contrario la cola se volverá grande después de un tiempo.

BTW: pyaudio está basado en portaudio, que tiene mucha más documentación y consejos útiles. Por ejemplo ( http://portaudio.com/docs/v19-doxydocs/writing_a_callback.html ): la función de callback debe finalizar antes de que se presente un nuevo búfer, de lo contrario se perderán los búferes. Por lo tanto, escribir en un archivo dentro de la función de callback no suele ser una buena idea. (aunque la escritura en un archivo se almacena en búfer y no sé si se bloquea cuando se escribe en el disco)

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