Я передаю аудиоданные от двух клиентов в сети в общее серверное программное обеспечение, которое должно принимать указанные аудиоданные и объединять их в двухканальный волновой файл. И клиент, и сервер написаны мной.
Я изо всех сил пытаюсь совместить это на стороне сервера, и ключевой показатель в выходном волновом файле — это возможность воссоздать временные метки, с которыми общались пользователи. Что я хочу сделать, так это вывести каждого клиента (всего 2 на файл волны) в 2-канальный файл стереофонической волны.
Как правильно поступить в подобной ситуации? Нужно ли менять клиентов для потоковой передачи аудиоданных по-другому? Кроме того, что вы рекомендуете в качестве подхода к работе с паузами в аудиопотоке, т. е. захвата задержек между пользователями, нажимающими кнопку «нажми и говори», когда на сервер не поступают сообщения?
В настоящее время клиентское программное обеспечение использует pyaudio для записи с устройства ввода по умолчанию и отправки отдельных кадров по сети с использованием TCP/IP. Одно сообщение на кадр. Клиенты работают по принципу «нажми и говори» и отправляют аудиоданные только тогда, когда удерживается кнопка «нажми и говори», в противном случае сообщения не отправляются.
Я провел приличное исследование формата файлов WAVE и понимаю, что для этого мне нужно будет чередовать сэмплы из каждого канала для каждого записываемого кадра, откуда и возникает мой главный источник путаницы. Из-за динамического характера этой среды, а также синхронного подхода к обработке аудиоданных на стороне сервера большую часть времени у меня не будет данных от обоих клиентов одновременно, но если я это сделаю, у меня не будет хороший логический механизм, сообщающий серверу о необходимости одновременной записи обоих фреймов.
Вот что у меня есть для обработки аудио от клиентов. Для каждого клиента создается один экземпляр этого класса, и поэтому для каждого клиента создается отдельный волновой файл, а это не то, что мне нужно.
class AudioRepository(object):
def __init__(self, root_directory, test_id, player_id):
self.test_id = test_id
self.player_id = player_id
self.audio_filepath = os.path.join(root_directory, "{0}_{1}_voice_chat.wav".format(test_id, player_id))
self.audio_wave_writer = wave.open(self.audio_filepath, "wb")
self.audio_wave_writer.setnchannels(1)
self.audio_wave_writer.setframerate(44100)
self.audio_wave_writer.setsampwidth(
pyaudio.get_sample_size(pyaudio.paInt16))
self.first_audio_record = True
self.previous_audio_time = datetime.datetime.now()
def write(self, record: Record):
now = datetime.datetime.now()
time_passed_since_last = now - self.previous_audio_time
number_blank_frames = int(44100 * time_passed_since_last.total_seconds())
blank_data = b"\0\0" * number_blank_frames
if not self.first_audio_record and time_passed_since_last.total_seconds() >= 1:
self.audio_wave_writer.writeframes(blank_data)
else:
self.first_audio_record = False
self.audio_wave_writer.writeframes(
record.additional_data["audio_data"])
self.previous_audio_time = datetime.datetime.now()
def close(self):
self.audio_wave_writer.close()
Я набрал это, потому что код находится на машине без доступа к Интернету, так что извините, если форматирование и/или опечатки.
Это также демонстрирует, что я сейчас делаю, чтобы управлять временем между передачами, которое работает умеренно хорошо. Ограничение скорости — это хак и вызывает проблемы, но я думаю, что у меня есть реальное решение для этого. Клиенты отправляют сообщения, когда пользователи нажимают и отпускают кнопку разговора, поэтому я могу использовать их как флаги для приостановки вывода пустых кадров, пока пользователь отправляет мне настоящие аудиоданные (что было настоящей проблемой, когда пользователи отправляли аудиоданные, я добавлял кучу маленьких крошечных пауз, которые делали звук прерывистым).
Ожидаемое решение состоит в том, чтобы сделать приведенный выше код больше не привязанным к одному идентификатору игрока, а вместо этого запись будет вызываться с записями от обоих клиентов сервера (но все же будет по одному от каждого игрока индивидуально, а не вместе) и чередование аудиоданные от каждого в 2-канальный волновой файл с каждым проигрывателем на отдельном канале. Я просто ищу предложения о том, как справиться с деталями этого. Мои первоначальные мысли заключаются в том, что необходимо будет задействовать поток и две очереди аудиокадров от каждого клиента, но я все еще не уверен, как объединить все это в волновой файл и сделать так, чтобы он звучал правильно и был правильно рассчитан.