PyAudio Как записывать звуки микрофона и системы в один поток?

я пытаюсь создать приложение с pyaudio, которое записывает звук динамика и микрофона, но я просто не знаю, как записать их оба. Я попытался включить стереомикшер, но это не сработало, потому что я мог слушать звук только из компьютерные колонки. Этот код, который я использую, записывает звук с микрофона по умолчанию в фоновом потоке с помощью pyaudio:

import pyaudio
import wave
import threading
import time
import subprocess

CHUNK = 1024
FORMAT = pyaudio.paInt16
CHANNELS = 2
RATE = 44100
RECORD_SECONDS = 5
WAVE_OUTPUT_FILENAME = "tmp/tmp.wav"

class recorder:
    def __init__(self):
        self.going = False
        self.process = None
        self.filename = "ScreenCapture.mpg"
    def record(self,filename):
        try:
            if self.process.is_alive():
                self.going = False
        except AttributeError:
            print("test")
        self.process = threading.Thread(target=self._record)
        self.process.start()
        self.filename = filename
    def _record(self):
        p = pyaudio.PyAudio()
        stream = p.open(format=FORMAT,
                        channels=CHANNELS,
                        rate=RATE,
                        input=True,
                        frames_per_buffer=CHUNK)

        print("* recording")

        frames = []

        self.going = True

        while self.going:
            data = stream.read(CHUNK)
            frames.append(data)

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


    def stop_recording(self):
        self.going = False

Я использую Windows, и я также могу использовать другую библиотеку, а не только PyAudio. Мне просто нужно получить эту работу.

Изменить: я нашел этот код для записи вывода динамика, но не смог заставить его работать в своем приложении:

import pyaudio
import wave

CHUNK = 1024
FORMAT = pyaudio.paInt16
CHANNELS = 2
RATE = 44100
RECORD_SECONDS = 5
WAVE_OUTPUT_FILENAME = "output.wav"

p = pyaudio.PyAudio()

SPEAKERS = p.,get_default_output_device_info()["hostApi"] 
stream = p.open(format=FORMAT,
                channels=CHANNELS,
                rate=RATE,
                input=True,
                frames_per_buffer=CHUNK,
                input_host_api_specific_stream_info=SPEAKERS,
                as_loopback=True) 

print("* recording")

frames = []

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

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

ОБНОВЛЕНИЕ: я мог записывать и динамик, и микрофон с помощью этого:

import pyaudio
import wave
import numpy as np


CHUNK = 1024
FORMAT = pyaudio.paInt16
RATE = 44100
RECORD_SECONDS = 2
WAVE_OUTPUT_FILENAME = "tmp.wav"


p = pyaudio.PyAudio()


for i in range(0, p.get_device_count()):
    print(i, p.get_device_info_by_index(i)['name'])


#stream using as_loopback to get sound from OS
stream = p.open(
    format = FORMAT,
    channels = 2,
    rate = RATE,
    input=True,
    frames_per_buffer=CHUNK,
    input_device_index=2,
    as_loopback=True)

##stream using my Microphone's input device
stream2 = p.open(
    format = FORMAT,
    channels = 1,
    rate = RATE,
    input=True,
    frames_per_buffer=CHUNK,
    input_device_index=1)
    #as_loopback=False)


frames = []
frames2 = []


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


#frames = as_loopback sound data (Speakers)
frames= b''.join(frames);

#frames2 = sound data of Microphone
frames2= b''.join(frames2);

#decoding Speaker data
Sdecoded = np.frombuffer(frames, 'int16')

#decoding the microphone data
Mdecoded = np.frombuffer(frames2, 'int16')

#converting Speaker data into a Numpy vector (making life easier when picking up audio channels)
Sdecoded= np.array(Sdecoded, dtype='int16') 

#getting the data on the right side
direito=Sdecoded[1::2]

#getting the data on the left side
esquerdo=Sdecoded[::2]

#mixing everything to mono = add right side + left side + Microphone decoded data that is already mono
mix=(direito+esquerdo+Mdecoded)

#ensuring no value goes beyond the limits of short int
signal=np.clip(mix, -32767, 32766)

#encode the data again
encodecoded = wave.struct.pack("%dh"%(len(signal)), *list(signal))


#stop all streams and terminate pyaudio
stream.stop_stream()
stream.close()
stream2.stop_stream()
stream2.close()
p.terminate()


#recording mixed audio in mono 
wf = wave.open(WAVE_OUTPUT_FILENAME, 'wb')
wf.setnchannels(1)
wf.setsampwidth(p.get_sample_size(FORMAT))
wf.setframerate(RATE)
wf.writeframes((encodecoded))
wf.close()

Но когда я пытаюсь смешать свой код, он не работает. Что происходит? Кажется, я почти решил это.

import numpy as np
import pyaudio
import wave
import threading
import time
import subprocess

CHUNK = 1024
FORMAT = pyaudio.paInt16
RATE = 44100
RECORD_SECONDS = 5
WAVE_OUTPUT_FILENAME = "tmp/tmp.wav"

p = pyaudio.PyAudio()

for i in range(0, p.get_device_count()):
    print(i, p.get_device_info_by_index(i)['name'])


class recorder:
    def __init__(self):
        self.going = False
        self.process = None
        self.filename = "ScreenCapture.mpg"
    def record(self,filename):
        try:
            if self.process.is_alive():
                self.going = False
        except AttributeError:
                print("test")
        self.process = threading.Thread(target=self._record)
        self.process.start()
        self.filename = filename
    def _record(self):
        p = pyaudio.PyAudio()
        #stream using as_loopback to get sound from OS
        stream = p.open(
            format=FORMAT,
            channels=2,
            rate=RATE,
            input=True,
            frames_per_buffer=CHUNK,
            input_device_index=2,
            as_loopback=True)
        ##stream using my Microphone's input device
        stream2 = p.open(
            format=FORMAT,
            channels=1,
            rate=RATE,
            input=True,
            frames_per_buffer=CHUNK,
            input_device_index=1)
        # as_loopback=False)
        #print("* recording")

        frames = []
        frames2= []

        self.going = True

        while self.going:
            data = stream.read(CHUNK)
            data2 = stream2.read(CHUNK)
            frames.append(data)
            frames2.append(data2)
        # frames = as_loopback sound data (Speakers)
        frames = b''.join(frames);

        # frames2 = sound data of Microphone
        frames2 = b''.join(frames2);

        # decoding Speaker data
        Sdecoded = np.frombuffer(frames, 'int16')

        # decoding the microphone data
        Mdecoded = np.frombuffer(frames2, 'int16')

        # converting Speaker data into a Numpy vector (making life easier when picking up audio channels)
        Sdecoded = np.array(Sdecoded, dtype='int16')

        # getting the data on the right side
        direito = Sdecoded[1::2]

        # getting the data on the left side
        esquerdo = Sdecoded[::2]

        # mixing everything to mono = add right side + left side + Microphone decoded data that is already mono

        mix = (direito + esquerdo + Mdecoded)

        # ensuring no value goes beyond the limits of short int

        signal = np.clip(mix, -32767, 32766)

        # encode the data again
        encodecoded = wave.struct.pack("%dh" % (len(signal)), *list(signal))

       # print("* done recording")

        stream.stop_stream()
        stream.close()
        stream2.stop_stream()
        stream2.close()
        p.terminate()

        wf = wave.open(WAVE_OUTPUT_FILENAME, 'wb')
        wf.setnchannels(1)
        wf.setsampwidth(p.get_sample_size(FORMAT))
        wf.setframerate(RATE)
        wf.writeframes(encodecoded)
        wf.close()


    def stop_recording(self):
        self.going = False

Я сделал код очень чистым и прокомментировал каждую часть, чтобы вы понимали, что происходит. Я сделал цикл for в начале для Pyaudio, чтобы показать мне, какие интерфейсы есть в моей ОС:

0 Mapeador de som da Microsoft - Input
1 Microfone (Realtek(R) Audio)
2 Mixagem estéreo (Realtek(R) Aud
3 Mapeador de som da Microsoft - Output
4 Alto-falantes (Realtek(R) Audio
5 Alto-falantes (Realtek(R) Audio)
6 Microfone (Realtek(R) Audio)
7 Mixagem estéreo (Realtek(R) Audio)
8 Speakers 1 (Realtek HD Audio output with SST)
9 Speakers 2 (Realtek HD Audio output with SST)
10 Alto-falante (Realtek HD Audio output with SST)
11 Microfone (Realtek HD Audio Mic input)
12 Mixagem estéreo (Realtek HD Audio Stereo input)

person Gabriel    schedule 09.01.2020    source источник
comment
Поделитесь кодом, который вы используете для записи из Stereo Mix   -  person mail2subhajit    schedule 09.01.2020
comment
Это тот же код. Я просто изменил микрофон по умолчанию на стереомикс и каким-то образом услышал звук из динамиков.   -  person Gabriel    schedule 09.01.2020


Ответы (1)


Вы можете использовать 2 отдельных потока для записи с 2 разных устройств (с предоставлением отдельного индекса устройства) в отдельные файлы Wav.

Затем смешайте эти 2 файла, используя библиотеку pydub.

from pydub import AudioSegment

speakersound = AudioSegment.from_file("/path/speaker.wav")
micsound = AudioSegment.from_file("/path/mic.wav")

mixsound = speakersound.overlay(micsound)

mixsound.export("/path/mixsound.wav", format='wav')
person mail2subhajit    schedule 09.01.2020
comment
Как я могу разделить 2 потока для записи с 2 разных устройств? Спасибо за вашу помощь и время. Я не ожидал, что кто-нибудь появится. - person Gabriel; 10.01.2020
comment
Спасибо за удаление повторяющихся запросов. сначала попробуйте запустить 2 отдельных скрипта py 1. Stereo Mix 2. микрофон. Запустите эти 2 скрипта в отдельном терминале. Поделитесь результатом. - person mail2subhajit; 11.01.2020
comment
если вы хотите, чтобы люди уделяли больше внимания вашему вопросу, запустите баунти, больше людей будет готово ответить на ваш вопрос. - person mail2subhajit; 12.01.2020
comment
Привет @ mail2subhajit Не могли бы вы увидеть мой обновленный код? Думаю, я почти решил его. Большое спасибо за вашу помощь и еще раз извините за дублированные запросы. - person Gabriel; 14.01.2020
comment
Я использовал numpy для более быстрого декодирования. В этом коде я использую его для преобразования списков в векторы, также использовал функцию клипа, которая работает как ограничитель в аудио, когда вы микшируете что-то, вы просто добавляете векторы, при этом вы неизбежно экстраполировать минимальное значение short int, поэтому нам нужно, чтобы экстраполированные данные находились в пределах диапазона с помощью функции клипа. После окончания захвата я начал весь процесс декодирования звука, конвертируя звук из стерео в моно, вырезая звук и, наконец, записывая все в моно на 44100 Гц в файле .wav. Но это не работает внутри моего проекта. - person Gabriel; 14.01.2020
comment
Я надеюсь, что это имеет смысл - person Gabriel; 14.01.2020
comment
Если вы думаете, что это очень сложно, я могу заплатить вам 100 долларов, чтобы помочь мне закончить. Мне просто нужно это сделать, и все кончено. - person Gabriel; 15.01.2020
comment
Конечно, Габриэль, я отменю голосование против. Можете ли вы поделиться со мной записанным файлом с микрофона и динамика в отдельном файле (файл .wav). - person mail2subhajit; 16.01.2020
comment
Я уже решил это. Если вы хотите, мы можем нанять вас в качестве нашего консультанта для дальнейших внедрений. - person Gabriel; 16.01.2020
comment
Я хотел, чтобы пользователь ввел какую-то лицензию перед использованием приложения. Я не знаю, возможно ли это с python. - person Gabriel; 16.01.2020
comment
Хорошие новости, что вы можете решить проблему самостоятельно .. спасибо за добрые слова. - person mail2subhajit; 16.01.2020
comment
Можете ли вы дать мне более подробную информацию о лицензионной части (серийный ключ или логин), тогда я могу дать вам несколько предложений. - person mail2subhajit; 16.01.2020
comment
Это будет серийный ключ. Когда вы вводите этот лицензионный ключ, программное обеспечение должно разрешать вам использовать программное обеспечение. После того, как пользователь вводит лицензионный ключ, мое программное обеспечение должно помнить, что он ввел действительный ключ. (Потому что в следующий раз ему не нужно будет открывать диалоговое окно лицензии) - person Gabriel; 16.01.2020
comment
Прочтите: grokbase.com/t/python/ список-питонов/01c41az6bx/ - person mail2subhajit; 16.01.2020
comment
Вы можете зашифровать и сохранить лицензионный ключ в отдельном файле или поместить его в код Python, лицензионный ключ и ключ, предоставленные пользователем (закрытый и открытый ключи). вы можете написать функцию для проверки этого ключа в начале программы, проверяя закрытый и открытый ключи. Узнайте больше о криптографии: tutorialspoint.com/cryptography_with_python/ - person mail2subhajit; 16.01.2020
comment
если требуется дополнительная помощь, задайте этот вопрос отдельно на этом форуме. - person mail2subhajit; 16.01.2020
comment
Интересно. Я посмотрю на это. Еще раз спасибо. - person Gabriel; 16.01.2020