Last active
October 10, 2023 08:29
-
-
Save ViennaMike/70c6a47ad5309a06f03faed047b1df11 to your computer and use it in GitHub Desktop.
Simple wireless microphone with sound effects using UDP, python, and PyAudio
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import socket | |
import sys | |
import pyaudio | |
import wave # do we need? | |
import time | |
import numpy as np | |
import sox | |
HOST = '' # Symbolic name meaning all available interfaces | |
PORT = 50007 # Arbitrary non-privileged port | |
CHUNK = 4096 | |
FORMAT = pyaudio.paInt16 | |
CHANNELS = 1 | |
RATE = 44100 | |
SHIFT = -6 | |
REV = 60 | |
EFFECT = True | |
# Instantiate PyAudio and a sox transformer | |
p = pyaudio.PyAudio() | |
tfm = sox.Transformer() | |
tfm.pitch(SHIFT) | |
tfm2 = sox.Transformer() | |
tfm2.reverb(reverberance = REV) | |
# Datagram (udp) socket | |
try : | |
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) | |
print('Socket created') | |
except socket.error as e: | |
print('Failed to create socket. Error Code : ', e.message, e.args) | |
sys.exit() | |
# Bind socket to local host and port | |
try: | |
s.bind((HOST, PORT)) | |
except socket.error as e: | |
print('Bind failed. Error Code : ', e.message, e.args) | |
sys.exit() | |
print('Socket bind complete') | |
# Open stream using blocking | |
stream = p.open(format=FORMAT, | |
channels=CHANNELS, | |
rate=RATE, | |
frames_per_buffer=CHUNK, | |
output=True) | |
#now keep talking with the client | |
while True: | |
try: | |
# receive data from client (data, addr) | |
d = s.recvfrom(8192) | |
data = np.frombuffer(d[0], dtype=np.int16) | |
if EFFECT: | |
data = tfm.build_array(input_array=data, sample_rate_in=RATE) | |
data = tfm2.build_array(input_array = data, sample_rate_in=RATE) | |
data = (data.astype(np.int16).tostring()) | |
stream.write(data) | |
except KeyboardInterrupt: | |
print("Stream off.") | |
stream.stop_stream() | |
stream.close() | |
p.terminate() | |
s.close() | |
s.close() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import socket | |
import sys | |
import pyaudio | |
import wave # do we need? | |
import time | |
import numpy as np | |
import sox | |
HOST = '' # Symbolic name meaning all available interfaces | |
PORT = 50007 # Arbitrary non-privileged port | |
CHUNK = 4096 | |
FORMAT = pyaudio.paInt16 | |
CHANNELS = 1 | |
RATE = 44100 | |
SHIFT = -6 | |
REV = 60 | |
EFFECT = True | |
# Instantiate PyAudio and a sox transformer | |
p = pyaudio.PyAudio() | |
tfm = sox.Transformer() | |
tfm.pitch(SHIFT) | |
tfm2 = sox.Transformer() | |
tfm2.reverb(reverberance = REV) | |
# Datagram (udp) socket | |
try : | |
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) | |
print('Socket created') | |
except socket.error as e: | |
print('Failed to create socket. Error Code : ', e.message, e.args) | |
sys.exit() | |
# Bind socket to local host and port | |
try: | |
s.bind((HOST, PORT)) | |
except socket.error as e: | |
print('Bind failed. Error Code : ', e.message, e.args) | |
sys.exit() | |
print('Socket bind complete') | |
# Define callback | |
def callback(in_data, frame_count, time_info, status): | |
d = s.recvfrom(16384) | |
data = np.frombuffer(d[0], dtype=np.int16) | |
if EFFECT: | |
data = tfm.build_array(input_array=data, sample_rate_in=RATE) | |
data = tfm2.build_array(input_array = data, sample_rate_in=RATE) | |
data = (data.astype(np.int16).tostring()) | |
return(data, pyaudio.paContinue) | |
# Open stream using callback | |
stream = p.open(format=FORMAT, | |
channels=CHANNELS, | |
rate=RATE, | |
frames_per_buffer=CHUNK, | |
output=True, | |
stream_callback=callback) | |
#now keep talking with the client | |
while True: | |
try: | |
time.sleep(0.1) | |
except KeyboardInterrupt: | |
print("Stream off.") | |
stream.stop_stream() | |
stream.close() | |
p.terminate() | |
s.close() | |
sys.exit() | |
s.close() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import socket | |
import sys | |
import pyaudio | |
import time | |
HOST = '192.168.88.49' | |
PORT = 50007 | |
CHUNK = 1024 | |
FORMAT = pyaudio.paInt16 | |
CHANNELS = 1 | |
RATE = 44100 | |
def callback(in_data, frame_count, time_info, status): | |
s.sendto(in_data, (HOST, PORT)) | |
return (in_data, pyaudio.paContinue) | |
# Instantiate PyAudio | |
p = pyaudio.PyAudio() | |
# create dgram udp socket | |
try: | |
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) | |
except socket.error: | |
print('Failed to create socket') | |
sys.exit() | |
# Open stream using callback | |
stream = p.open(format=FORMAT, | |
channels=CHANNELS, | |
rate=RATE, | |
frames_per_buffer=CHUNK, | |
input=True, | |
stream_callback=callback) | |
stream.start_stream() | |
while stream.is_active(): | |
try : | |
# data = stream.read(CHUNK) | |
# s.sendto(data, (HOST, PORT)) | |
time.sleep(1) | |
except socket.error as e: | |
print('Error Code : ', e.message, e.args) | |
sys.exit() | |
except KeyboardInterrupt: | |
print("Stream off.") | |
stream.stop_stream() | |
stream.close() | |
p.terminate() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
For Raspberry Pi (and likely other Linux devices). Simple, no frills transmission of input stream (e.g., a microphone) to xmit.py running on one Pi to play out over audio output on a second Pi. The receiver, if the Effect Boolean, if set to True, uses Sox to lower the pitch and add a bit of reverb.
PyAudio has two modes, a blocking mode, where each call to pyaudio.Stream.write() or pyaudio.Stream.read() blocks until all the given/requested frames have been played/recorded and a non-blocking mode where a callback function is launched in a separate thread, so that processing can continue in the program calling it, and the thread ends when the current chunk of audio is processed. xmit.py uses the non-blocking mode using the callback function, as does rcvpitchcallback.py for the receiver. rcvpitch.py, on the other hand, uses the blocking approach. You need to be careful when using the non-blocking mode that the callback function does not include anything really time consuming, like file reading and writing. If it does, it can’t finish before the next chunk of audio is ready and you get clipping or worse problems.
Uses UDP to send and receive the audio packets and PyAudio to process the audio on both ends.
Note: audio out is very low volume on a Pi if you use the audio jack. I found that when I used a USB speaker the sound was much better and louder, but I had to use the ALSAMixer, NOT the speaker icon on the GUI, to control the volume. If I used the speaker icon, I got no sound unless it was at maximum volume. This might be an issue with the particular speaker I'm using or might be more general.
Also, there's a pretty much constant stream of ALSA buffer underrun warnings. I don't know just why or how to elliminate them. They don't interfere with operations.
I've seen other similar implementations using TCP. If someone knows why TCP or UDP makes more sense for audio streaming, please comment. Similarly, if anyone has more insight into the volume control issue or the buffer underrun warnings, please share. I'd love to hear it.
See a bit more on this project on my blog at https://www.mcgurrin.info/robots/754/