Search code examples
pythonsocketsaudiotcpwav

Having issues playing a .wav file on Raspberry Pi (Python & Socket)


So my code has been built by referencing this post and this post for the wave file sending, which seem to be the most popular StackOverflow posts on the topic, but I can't seem to solve the issue. So, my code can send the audio file, but it only works half of the time when playing it on the Pi. First, some details:

I am trying to send a .wav audio file generated on my laptop to a Raspberry Pi running the most up-to-date default Raspberry Pi OS, and then playing the audio through a connected USB speaker.

I figured out that it actually is the code that I use to play the audio. Sometimes it plays the whole file, but sometimes it only plays a quarter of itor the last few seconds. How could I fix this?

I have tried multiple different audio players like pygame, pyttsx3, and the one shown below and this code works the best of them all even with its errors.

Here is my code:

Note: The server is on the Pi, and my computer acts as the client to send the .wav to the server.

Server:

def audio_reciever(): # this is the audio receiver
port = 65432                  # Reserve a port for your service.
s = socket.socket()             # Create a socket object
host = 'this is the pi ip'
chunk = 1024
# Get local machine name
s.bind((host, port))
f = open('voice.wav', 'w+b')
# Bind to the port
s.listen()                     # Now wait for client connection.
while True:
    conn, addr = s.accept()     # Establish connection with client.
    l = conn.recv(1024)
    while (l):
       f.write(l)
       l = conn.recv(1024)
    f.close()
    conn.send(b'Audio_Recieved')
    break
conn.close()
return

Audio player for the Pi:

def audio_player():
  chunk = 1024
  wf = wave.open('voice.wav', 'rb')
  # create an audio object
  p = pyaudio.PyAudio()
  # open stream based on the wave object which has been input.
  stream = p.open(format =
                  p.get_format_from_width(wf.getsampwidth()),
                  channels = wf.getnchannels(),
                  rate = wf.getframerate(),
                  output = True)
  # read data (based on the chunk size)
  data = wf.readframes(chunk)
  # play stream (looping from beginning of file to the end)
  while data != b'':
      # writing to the stream is what *actually* plays the sound.
      stream.write(data)
      data = wf.readframes(chunk)
  stream.stop_stream()    # "Stop Audio Recording
  stream.close()          # "Close Audio Recording
  p.terminate()           # "Audio System Close
  wf.close()
  return

Client:

def audio_sender(self,command):
command = "command goes here"
print(command)
engine.save_to_file(command, 'voice.wav')
engine.runAndWait()
engine.stop()

arr = array('B') # create binary array to hold the wave file
result = stat("voice.wav")  # sample file is in the same folder
f = open("voice.wav", 'rb')
arr.fromfile(f, result.st_size) # using file size as the array length
HOST = 'this is the pi ip'
PORT = 65432
print("actually sending")
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
    s.connect((HOST, PORT))
    s.send(arr)
    print('Finished sending...')
    s.close()
f.close()
os.remove('voice.wav')
print('done.')

I would greatly appreciate some help figuring out why it breaks sometimes, as I need this code to work as intended every time, as this code will need to be called repeatedly for a personal project. Also, is there any way to slow down the rate just a bit when it plays the audio file on the Pi?

Thank you for your time.


Solution

  • I eventually ended up using the PyGame module to play the audio. Here is the code:

    def playSound(filename):
      pygame.mixer.music.load(filename)
      pygame.mixer.music.play()
    def audio_player():
      pygame.init()
      playSound('voice.wav')
      while pygame.mixer.music.get_busy() == True:
          continue
      return