Skip to content

Instantly share code, notes, and snippets.

@ViennaMike
Last active October 10, 2023 08:29
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save ViennaMike/70c6a47ad5309a06f03faed047b1df11 to your computer and use it in GitHub Desktop.
Save ViennaMike/70c6a47ad5309a06f03faed047b1df11 to your computer and use it in GitHub Desktop.
Simple wireless microphone with sound effects using UDP, python, and PyAudio
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()
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()
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()
@ViennaMike
Copy link
Author

ViennaMike commented May 2, 2021

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/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment