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

时间:2019-07-25 17:12:58

标签: python pyaudio python-sounddevice

我想录制一个音轨并保存2个不同的.wav文件。音轨的保存时间应为ca左右。 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()

1 个答案:

答案 0 :(得分:1)

您为什么认为您需要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()

一些区别:

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

希望所有帮助/有意义!