diff options
author | Galen Guyer <galen@galenguyer.com> | 2022-11-22 01:10:50 -0500 |
---|---|---|
committer | Galen Guyer <galen@galenguyer.com> | 2022-11-22 01:10:50 -0500 |
commit | 95e6b7489c1f937921061a1872705e456546fc83 (patch) | |
tree | 4cd4466d5551758b945a8f1b0de87cda48cef535 |
Python UDP audio streaming in real time
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | udp-audio.py | 88 |
2 files changed, 89 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f7275bb --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +venv/ diff --git a/udp-audio.py b/udp-audio.py new file mode 100644 index 0000000..d6d86a0 --- /dev/null +++ b/udp-audio.py @@ -0,0 +1,88 @@ +import socket +import struct +import pyaudio +import time + +from threading import Thread +import numpy as np +import scipy.signal +from matplotlib import pyplot as plt + +TGID_in_stream = True # When set to True, we expect a 4 byte long int with the TGID prior to the audio in each packet +TGID_to_play = 0 # When TGID_in_stream is set to True, we'll only play audio if the received TGID matches this value +UDP_PORT = 9123 # UDP port to listen on +AUDIO_OUTPUT_DEVICE_INDEX = 4 # Audio device to play received audio on + +# Set up a UDP server +UDPSock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) +# UDPSock.settimeout(1) + +listen_addr = ("", UDP_PORT) +UDPSock.bind(listen_addr) + +p = pyaudio.PyAudio() + +for i in range(p.get_device_count()): + print(p.get_device_info_by_index(i)) + +CHUNK=1024 +FORMAT = pyaudio.paInt16 +CHANNELS = 1 +IN_RATE = 8_000 +OUT_RATE = 48_000 +stream = p.open( + format=FORMAT, + channels=CHANNELS, + rate=OUT_RATE, + input=False, + output=True, + frames_per_buffer=CHUNK, + output_device_index=AUDIO_OUTPUT_DEVICE_INDEX, +) + +FRAMES = [] + +def receive_audio(): + global FRAMES + global TGID_in_stream + global TGID_to_play + while True: + try: + data, addr = UDPSock.recvfrom((CHUNK * CHANNELS * 2) + (4 if TGID_in_stream else 0)) + if TGID_in_stream: + tgid = int.from_bytes(data[0:4], "little") + if TGID_to_play == 0: + TGID_to_play = [tgid] + if TGID_to_play == 0 or tgid in TGID_to_play: + audio_data = data[4:] + if len(audio_data) < 128: + continue + print(f"TGID: {tgid}, audio_data: {len(audio_data)}") + signal = np.frombuffer(audio_data, dtype=np.int16) + res = scipy.signal.resample(signal, (len(signal) * OUT_RATE) // IN_RATE).astype(np.int16) + FRAMES.append(res) + else: + stream.write(data) + except socket.timeout: + pass + +def play_audio(): + global FRAMES + while True: + while len(FRAMES) == 0: + time.sleep(1) + frames = FRAMES[:] + FRAMES.clear() + print(f"Playing {len(frames)} frames") + for frame in frames: + stream.write(frame.tobytes()) + + +stream_t = Thread(target=receive_audio) +play_t = Thread(target=play_audio) +stream_t.daemon = True +play_t.daemon = True +stream_t.start() +play_t.start() +stream_t.join() +play_t.join() |