如何同时录制2个音频源?



我想录制音轨并将其保存 2 个不同的.wav文件。音轨应延迟保存约 6 秒,每个.wav应为 12 秒。

我尝试使用多处理和pyaudio来做到这一点,但我无法让它工作

请注意,我是python的初学者,这是我关于stackoverflow的第一篇文章!

def func1():
#Record and save a 12 seconds long .wav 
def func2():
#Record and save a 12 seconds long .wav 
if __name__ == '__main__':
p1 = Process(target=func1)
p1.start()
p2 = Process(target=func2)
p2.start()
p1.join()
p2.join()
#start func2 6 seconds after func1

I would expect a data structure like this:
|---1.wav---|---1.wav---|---1.wav---|
|---2.wav---|---2.wav---|---2.wav---|
6sec  12sec 18sec 24sec 30sec 36sec 42sec

编辑: 我想出了一些似乎运行良好的代码。它的延迟为 .144 秒。我很高兴改进此代码。此代码使用线程而不是多处理。

import pyaudio
import wave
from threading import Thread
import time
from datetime import datetime
FORMAT = pyaudio.paInt16
CHANNELS = 2
RATE = 44100
CHUNK = 1024
CHUNK1 = 1024
RECORD_SECONDS = 12
WAVE_OUTPUT_FILENAME1 = name = "outputs/output_1"+datetime.now().strftime("%m:%d:%Y-") 
WAVE_OUTPUT_FILENAME2 = name = "outputs/output_2"+datetime.now().strftime("%m:%d:%Y-") 

def func1():
while 1==1:
global FORMAT
global CHANNELS
global RATE
global CHUNK
global RECORD_SECONDS
global WAVE_OUTPUT_FILENAME1
WAVE_OUTPUT_FILENAME1 = name = "outputs/output1_"#+datetime.now().strftime("%m:%d:%Y-") 
audio = pyaudio.PyAudio()
stream = audio.open(format=FORMAT, channels=CHANNELS,
rate=RATE, input=True,
frames_per_buffer=CHUNK)
print("recording...")
frames = []
WAVE_OUTPUT_FILENAME1 = WAVE_OUTPUT_FILENAME1+datetime.now().strftime("%H;%M;%S.%f--") 
for i in range(0, int(RATE / CHUNK * RECORD_SECONDS)):
data = stream.read(CHUNK)
frames.append(data)
WAVE_OUTPUT_FILENAME1 = WAVE_OUTPUT_FILENAME1 + datetime.now().strftime("%H;%M;%S.%f")+".wav"
print("finished recording")

# stop Recording
stream.stop_stream()
stream.close()
audio.terminate()

waveFile = wave.open(WAVE_OUTPUT_FILENAME1, 'wb')
waveFile.setnchannels(CHANNELS)
waveFile.setsampwidth(audio.get_sample_size(FORMAT))
waveFile.setframerate(RATE)
waveFile.writeframes(b''.join(frames))
waveFile.close() 
def func2():
time.sleep(6)
while 1==1:
global FORMAT
global CHANNELS
global RATE
global CHUNK1
global RECORD_SECONDS
global WAVE_OUTPUT_FILENAME2
WAVE_OUTPUT_FILENAME2 = name = "outputs/output2_"#+datetime.now().strftime("%m:%d:%Y-") 
audio = pyaudio.PyAudio()
stream = audio.open(format=FORMAT, channels=CHANNELS,
rate=RATE, input=True,
frames_per_buffer=CHUNK1)
print("recording...")
frames = []
WAVE_OUTPUT_FILENAME2 = WAVE_OUTPUT_FILENAME2+datetime.now().strftime("%H;%M;%S.%f--") 
for i in range(0, int(RATE / CHUNK1 * RECORD_SECONDS)):
data = stream.read(CHUNK1)
frames.append(data)
WAVE_OUTPUT_FILENAME2 = WAVE_OUTPUT_FILENAME2 + datetime.now().strftime("%H;%M;%S.%f")+".wav"
print("finished recording")

# stop Recording
stream.stop_stream()
stream.close()
audio.terminate()
waveFile = wave.open(WAVE_OUTPUT_FILENAME2, 'wb')
waveFile.setnchannels(CHANNELS)
waveFile.setsampwidth(audio.get_sample_size(FORMAT))
waveFile.setframerate(RATE)
waveFile.writeframes(b''.join(frames))
waveFile.close() 
if __name__ == '__main__':
Thread(target = func1).start()
Thread(target = func2).start()

你为什么认为你需要multiprocessing? 我认为这只会使事情复杂化

如何只记录 6 秒(或更小)的块/帧并将正确的帧写入每个文件。

我有点得意忘形,写了一个不错的类来做这件事:

import pyaudio
import wave
import time

class OverlappedRecorder:
def __init__(
self, secs_per_file, secs_between_file, *,
num_channels=2, sample_rate=48000,
sample_format=pyaudio.paInt16,
):
# various constants needed later
self.num_channels = num_channels
self.sample_width = pyaudio.get_sample_size(sample_format)
self.sample_rate = sample_rate
self.frames_between_start = int(secs_between_file * sample_rate)
self.frames_per_file = int(secs_per_file * sample_rate)
# mutable state needed to keep everything going
self.files = []
self.frames_till_next_file = 0
self.pa = pyaudio.PyAudio()
self.stream = self.pa.open(
format=sample_format, channels=num_channels,
rate=sample_rate, frames_per_buffer=1024,
input=True, start=False,
stream_callback=self._callback,
)
def sleep_while_active(self):
while self.stream.is_active():
time.sleep(0.2)
def begin_wave_file(self):
"internal function to start a new WAV file"
path = time.strftime(
'recording-%Y-%m-%d-%H.%M.%S.wav',
time.localtime()
)
file = wave.open(path, 'wb')
file.setnchannels(self.num_channels)
file.setsampwidth(self.sample_width)
file.setframerate(self.sample_rate)
self.files.append(file)
# context manager stuff, recording starts when entered using "with"
def __enter__(self):
self.stream.start_stream()
return self
# exiting shuts everything down
def __exit__(self, exc_type, exc_val, exc_tb):
self.stream.stop_stream()
self.stream.close()
self.pa.terminate()
for file in self.files:
file.close()
# called by pyaudio when a new set of frames are ready
def _callback(self, data, frame_count, time_info, status):
self.frames_till_next_file -= frame_count
# see if we need to start a new file
if self.frames_till_next_file < 0:
self.frames_till_next_file += self.frames_between_start
self.begin_wave_file()
# can't remove from lists while iterating
# keep a list of files to close and remove later
done = []
for file in self.files:
remain = self.frames_per_file - file.getnframes()
# add appropriate amount of data to all open files
if frame_count < remain:
file.writeframesraw(data)
else:
remain *= self.sample_width * self.num_channels
file.writeframesraw(data[:remain])
done.append(file)
# close anything that finished
for file in done:
file.close()
self.files.remove(file)
# tell pyaudio to keep going
return (None, pyaudio.paContinue)

基本用法是:创建一个对象,使用with输入它,它将开始录制,当您退出时,它将停止并清理。

rec = OverlappedRecorder(12, 6)
with rec:
time.sleep(30)

将让它运行 30 秒,或者您可以执行以下操作:

with OverlappedRecorder(12, 6) as rec:
rec.sleep_while_active()

让它运行,直到你按 Ctrl+C 杀死程序,或者你可以在那里调用input()以使其在你按 Enter 时停止,或者你喜欢的任何其他内容。

对您发布的代码的一些评论:

如果要
  • 修改变量,则只需声明global
  • 变量
  • 为什么你们有单独的功能? 为什么不只有一个功能,而只是延迟start()第二个Thread
  • 你为什么要设置这么多次WAVE_OUTPUT_FILENAME1? 只需保存start_timeend_time,然后一次性格式化字符串
  • 您不必分块read(),如果您知道它将适合内存,只需一次性阅读所有内容即可
  • 您不需要继续开始和停止录制,只需在每个线程中打开一次,如果幸运的话,当您将 WAV 文件写入磁盘时,样本会累积在缓冲区中

像这样:

import pyaudio
import wave
import time
from datetime import datetime
from threading import Thread
FORMAT = pyaudio.paInt16
CHANNELS = 2
RATE = 44100
RECORD_SECONDS = 12

def recorder(prefix):
audio = pyaudio.PyAudio()
stream = audio.open(
format=FORMAT, channels=CHANNELS,
rate=RATE, input=True,
)
try:
while True:
start_time = datetime.now()
print("recording started", start_time)
data = stream.read(RATE * RECORD_SECONDS, False)
end_time = datetime.now()
print("finished", end_time)
name = f'{prefix}{start_time:%Y-%m-%d-%H-%M-%S.%f}-{end_time:%H-%M-%S.%f}.wav'
print("writing", name)
with wave.open(name, 'wb') as waveFile:
waveFile.setnchannels(CHANNELS)
waveFile.setsampwidth(audio.get_sample_size(FORMAT))
waveFile.setframerate(RATE)
waveFile.writeframes(data)
finally:
stream.stop_stream()
stream.close()
audio.terminate()

if __name__ == '__main__':
Thread(target=recorder, args=('outputs/output_1-',)).start()
time.sleep(6)
Thread(target=recorder, args=('outputs/output_2-',)).start()

一些区别:

  • 使用线程的版本代码要少得多!
  • 我的版本允许任意数量的文件,而不会为每个文件占用多个操作系统线程(有 Python 线程,pyaudio有一个内部线程负责音频缓冲区)
  • 我的版本保存部分文件

希望所有的帮助/有意义!

相关内容

  • 没有找到相关文章

最新更新