Pyaudio Recording audio from streaming Python - python

I would like to record using pyaudio in python, the audio streamed through a socket and save it in a *.wav file.
I want to save everything in wave after so I can process it as I want. For now I have tried to write this code, but it always fails after a while that I compile.
The error is
wf.writeframes(b''.join(data1))
TypeError: sequence item 0: expected bytes, int found
my code for client.py is:
import pyaudio, sys, socket, wave
port = 5000
ip = "192.168.1.110"
chunk = 512
FORMAT = pyaudio.paInt16
CHANNELS = 1
RATE = 49000
WAVE_OUTPUT="output.wav"
p = pyaudio.PyAudio()
stream = p.open(format = FORMAT, channels = CHANNELS, rate = RATE, input =True,output = True, frames_per_buffer = chunk)
#Create a socket connection for connecting to the server:
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client_socket.connect((ip, port))
print ("***Registrazione in corso***")
frames=[]
for i in range (0,int(RATE/chunk*20)):
data1=client_socket.recv(chunk)
frames.append(data1)
while True:
#Recieve data from the server:
#data = client_socket.recv(1024)
stream.write(data1,chunk)
wf=wave.open(WAVE_OUTPUT,"wb")
wf.setnchannels(CHANNELS)
wf.setsampwidth(p.get_sample_size(FORMAT))
wf.setframerate(RATE)
wf.writeframes(b''.join(data1))
#print data
wf.close()
socket.close()

You are getting a TypeError because the open wav file want a stream of bytes and you are passing a single int value. I think you want to write the entire frame to the file and not just the chunk in data1. Try wf.writeframes(b''.join(frames)). Also don't forget to close the stream at the end.
stream.stop_stream()
stream.close()

Related

Use the mobile phone's microphone instead of the computer's microphone

I am trying to record audio in a file and it is already working in the computer but when I connect my mobile phone to the localhost to be able to manipulate the web and check how is it seen, I realised that when I try to record an audio even if I am connected from my mobile phone it is listening to the computer's microphone instead of the phone's microphone. Is there any way to solve this?
def generate():
chunk = 1024 # Record in chunks of 1024 samples
sample_format = pyaudio.paInt16 # 16 bits per sample
channels = 1
fs = 44100 # Record at 44100 samples per second
seconds = 4
filename = "/Users/enekoiza/Desktop/easy-peasy/test2.wav"
p = pyaudio.PyAudio() # Create an interface to PortAudio
print('Recording')
stream = p.open(format=sample_format,
channels=channels,
rate=fs,
frames_per_buffer=chunk,
input=True)
frames = [] # Initialize array to store frames
# Store data in chunks for 3 seconds
for i in range(0, int(fs / chunk * seconds)):
data = stream.read(chunk)
frames.append(data)
# Stop and close the stream
stream.stop_stream()
stream.close()
# Terminate the PortAudio interface
p.terminate()
print('Finished recording')
# Save the recorded data as a WAV file
wf = wave.open(filename, 'w')
wf.setnchannels(channels)
wf.setsampwidth(p.get_sample_size(sample_format))
wf.setframerate(fs)
wf.writeframes(b''.join(frames))
wf.close()

Having issue with pyaudio and sockets

I am trying to fix a script I wrote. It's a client application and I need to figure out how to use sockets to record data from a computer running linux in my office.
I am using netcat for the server, listening on port 5555.
I know I have to convert the i to a integer, but am having trouble.
I already have a ftp script for sending the .wav file I just need to get the s.recv prompt to work.
import pyaudio
import wave
from socket import*
s = socket()
host = "127.0.0.1"
port = 5555
s.connect((host,port))
s.send("how many seconds?\n")
i = s.recv(2)
CHUNK = 1024
FORMAT = pyaudio.paInt16 #paInt8
CHANNELS = 2
RATE = 44100 #sample rate
RECORD_SECONDS = i
WAVE_OUTPUT_FILENAME = "output.wav"
p = pyaudio.PyAudio()
stream = p.open(format=FORMAT,
channels=CHANNELS,
rate=RATE,
input=True,
frames_per_buffer=CHUNK) #buffer
print("* recording")
frames = []
for i in range(0, int(RATE / CHUNK * RECORD_SECONDS)):
data = stream.read(CHUNK)
frames.append(data) # 2 bytes(16 bits) per channel
print("* done recording")
stream.stop_stream()
stream.close()
p.terminate()
wf = wave.open(WAVE_OUTPUT_FILENAME, 'wb')
wf.setnchannels(CHANNELS)
wf.setsampwidth(p.get_sample_size(FORMAT))
wf.setframerate(RATE)
wf.writeframes(b''.join(frames))
wf.close()
I recommend you rename i to USER_INPUT to prevent a name clash with the for-loop below. Then convert convert that value to an int using the in-built int(), like so:
s.send("how many seconds?\n")
USER_INPUT = s.recv(2)
RECORD_SECONDS = int(USER_INPUT) # TODO: handle invalid user input
You could confirm this value has been converted, then:
s.send("Shall record for %d seconds\n" % RECORD_SECONDS)

UDP sound transfer : played sound have big noise

I have no idea how to solve this problem. Please help me :)
I would like to send sound data, recorded by one PC, to the other PC and play it. (by UDP)
The program might work correctly, but the sound contain(?) uncomfortable noise.
when I tried to record & play sound in one program sequence, it worked correctly. There was no noise.
In case of using UDP even in one PC, use IP 127.0.0.1, the noise appeared.
At first, I thought the factor is because played sound is out in the other PC and I fixed it by making buffer.
It solved little noise, but almost all the noise is still remaining.
the following code is it
Client
import pyaudio
import socket
from threading import Thread
frames = []
def udpStream():
udp = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
while True:
if len(frames) > 0:
udp.sendto(frames.pop(0), ("127.0.0.1", 12345))
udp.close()
def record(stream, CHUNK):
while True:
frames.append(stream.read(CHUNK))
if __name__ == "__main__":
CHUNK = 1024
FORMAT = pyaudio.paInt16
CHANNELS = 2
RATE = 44100
p = pyaudio.PyAudio()
stream = p.open(format = FORMAT,
channels = CHANNELS,
rate = RATE,
input = True,
frames_per_buffer = CHUNK,
)
Tr = Thread(target = record, args = (stream, CHUNK,))
Ts = Thread(target = udpStream)
Tr.setDaemon(True)
Ts.setDaemon(True)
Tr.start()
Ts.start()
Tr.join()
Ts.join()
Server
import pyaudio
import socket
from threading import Thread
frames = []
def udpStream(CHUNK):
udp = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
udp.bind(("127.0.0.1", 12345))
while True:
soundData, addr = udp.recvfrom(CHUNK)
frames.append(soundData)
udp.close()
def play(stream, CHUNK):
BUFFER = 10
while True:
if len(frames) == BUFFER:
while True:
stream.write(frames.pop(0), CHUNK)
if __name__ == "__main__":
FORMAT = pyaudio.paInt16
CHUNK = 1024
CHANNELS = 2
RATE = 44100
p = pyaudio.PyAudio()
stream = p.open(format=FORMAT,
channels = CHANNELS,
rate = RATE,
output = True,
frames_per_buffer = CHUNK,
)
Ts = Thread(target = udpStream, args=(CHUNK,))
Tp = Thread(target = play, args=(stream, CHUNK,))
Ts.setDaemon(True)
Tp.setDaemon(True)
Ts.start()
Tp.start()
Ts.join()
Tp.join()
sorry for long source code. Feel free to play this program.
I have searched for the reason of this noise. Finally I could detect why this happened.
Actually, This program UDP transfer did not cause packet loss.
Even if it did, the sound do not have such a serious noise.
This program sent data correctly, and there are almost no packet loss, but the "receive" method could not receive data correctly.
In server program
def udpStream(CHUNK):
udp = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
udp.bind(("127.0.0.1", 12345))
while True:
soundData, addr = udp.recvfrom(CHUNK)
frames.append(soundData)
udp.close()
This program could data only "25%". (I checked the amount of data)
So, I tried to receive the data multiply (CHANNELS * 2)
soundData, addr = udp.recvfrom(CHUNK * CHANNELS * 2)
This results in the sound data can be received 100% completely.
Finally, the sound recorded by one PC is played in the other PC without noise.
I've run into the same problem, but your solution didn't help me. What I discovered was that using
stream.write(frames.pop(0))
instead of
stream.write(frames.pop(0), CHUNK)
Clears all the noise in the received signal.

PyAudio Input overflowed

I'm trying to make real-time plotting sound in python. I need to get chunks from my microphone.
Using PyAudio, try to use
import pyaudio
import wave
import sys
chunk = 1024
FORMAT = pyaudio.paInt16
CHANNELS = 1
RATE = 44100
RECORD_SECONDS = 5
WAVE_OUTPUT_FILENAME = "output.wav"
p = pyaudio.PyAudio()
stream = p.open(format = FORMAT,
channels = CHANNELS,
rate = RATE,
input = True,
frames_per_buffer = chunk)
print "* recording"
all = []
for i in range(0, RATE / chunk * RECORD_SECONDS):
data = stream.read(chunk)
all.append(data)
print "* done recording"
stream.close()
p.terminate()
After, I getting the followin error:
* recording
Traceback (most recent call last):
File "gg.py", line 23, in <module>
data = stream.read(chunk)
File "/usr/lib64/python2.7/site-packages/pyaudio.py", line 564, in read
return pa.read_stream(self._stream, num_frames)
IOError: [Errno Input overflowed] -9981
I can't understand this buffer. I want, to use blocking IO mode, so if chunks not available, i want to wait for those chunks. But when I creating try except segment or sleep(0.1), i hear clicks, so this is not what i want.
Please suggest the best solution for my ploblem?
pyaudio.Stream.read() has a keyword parameter exception_on_overflow, set this to False.
For your sample code that would look like:
import pyaudio
import wave
import sys
chunk = 1024
FORMAT = pyaudio.paInt16
CHANNELS = 1
RATE = 44100
RECORD_SECONDS = 5
WAVE_OUTPUT_FILENAME = "output.wav"
p = pyaudio.PyAudio()
stream = p.open(format = FORMAT,
channels = CHANNELS,
rate = RATE,
input = True,
frames_per_buffer = chunk)
print "* recording"
all = []
for i in range(0, RATE / chunk * RECORD_SECONDS):
data = stream.read(chunk, exception_on_overflow = False)
all.append(data)
print "* done recording"
stream.close()
p.terminate()
See the PyAudio documentation for more details.
It seems like a lot of people are encountering this issue. I dug a bit into it and I think it means that between the previous call to stream.read() and this current call, data from the stream was lost (i.e. the buffer filled up faster than you cleared it).
From the doc for Pa_ReadStream() (the PortAudio function that stream.read() eventually ends up calling):
#return On success PaNoError will be returned, or PaInputOverflowed if
input data was discarded by PortAudio after the previous call and
before this call.
(PaInputOverflowed then causes an IOError in the pyaudio wrapper).
If it's OK for you to not capture every single frame, then you may ignore this error. If it's absolutely critical for you to have every frame, then you'll need to find a way to increase the priority of your application. I'm not familiar enough with Python to know a pythonic way to do this, but it's worth trying a simple nice command, or changing the scheduling policy to SCHED_DEADLINE.
Edit:
One issue right now is that when IOError is thrown, you lose all the frames collected in that call. To instead ignore the overflow and just return what we have, you can apply the patch below, which will cause stream.read() to ignore output underrun and input overflow errors from PortAudio (but still throw something if a different error occurred). A better way would be to make this behaviour (throw/no throw) customizable depending on your needs.
diff --git a/src/_portaudiomodule.c b/src/_portaudiomodule.c
index a8f053d..0878e74 100644
--- a/src/_portaudiomodule.c
+++ b/src/_portaudiomodule.c
## -2484,15 +2484,15 ## pa_read_stream(PyObject *self, PyObject *args)
} else {
/* clean up */
_cleanup_Stream_object(streamObject);
+
+ /* free the string buffer */
+ Py_XDECREF(rv);
+
+ PyErr_SetObject(PyExc_IOError,
+ Py_BuildValue("(s,i)",
+ Pa_GetErrorText(err), err));
+ return NULL;
}
-
- /* free the string buffer */
- Py_XDECREF(rv);
-
- PyErr_SetObject(PyExc_IOError,
- Py_BuildValue("(s,i)",
- Pa_GetErrorText(err), err));
- return NULL;
}
return rv;
I got the same error when I ran your code. I looked at the default sample rate of my default audio device, my macbook's internal microphone, it was 48000Hz not 44100Hz.
p.get_device_info_by_index(0)['defaultSampleRate']
Out[12]: 48000.0
When I changed RATE to this value, it worked.
I worked this on OS X 10.10, Got the same error while trying to get audio from the microphone in a SYBA USB card (C Media chipset), and process it in real time with fft's and more:
IOError: [Errno Input overflowed] -9981
The overflow was completely solved when using a Callback Mode, instead of the Blocking Mode, as written by libbkmz.(https://www.python.org/dev/peps/pep-0263/)
Based on that, the bit of the working code looked like this:
"""
Creating the audio stream from our mic
"""
rate=48000
self.chunk=2**12
width = 2
p = pyaudio.PyAudio()
# callback function to stream audio, another thread.
def callback(in_data,frame_count, time_info, status):
self.audio = numpy.fromstring(in_data,dtype=numpy.int16)
return (self.audio, pyaudio.paContinue)
#create a pyaudio object
self.inStream = p.open(format = p.get_format_from_width(width, unsigned=False),
channels=1,
rate=rate,
input=True,
frames_per_buffer=self.chunk,
stream_callback = callback)
"""
Setting up the array that will handle the timeseries of audio data from our input
"""
self.audio = numpy.empty((self.buffersize),dtype="int16")
self.inStream.start_stream()
while True:
try:
self.ANY_FUNCTION() #any function to run parallel to the audio thread, running forever, until ctrl+C is pressed.
except KeyboardInterrupt:
self.inStream.stop_stream()
self.inStream.close()
p.terminate()
print("* Killed Process")
quit()
This code will create a callback function, then create a stream object, start it and then loop in any function. A separate thread streams audio, and that stream is closed when the main loop is stopped. self.audio is used in any function. I also had problems with the thread running forever if not terminated.
Since Pyaudio runs this stream in a separate thread, and this made the audio stream stable, the Blocking mode might have been saturating depending on the speed or timing of the rest of the processes in the script.
Note that the chunk size is 2^12, but smaller chunks work just as well. There are other parameters I considered and played around with to make sure they all made sense:
Chunk size larger or smaller(no effect)
Number and format of bits for the words in the buffer, signed 16 bit in this case.
signedness of variables(tried with unsigned and got saturation patterns)
Nature of mic input, and selection as default in the system, gain etc.
Hope that works for someone!
My other answer solved the problem in most cases. However sometimes the error still occurs.
That was the reason why I scrapped pyaudio and switched to pyalsaaudio. My Raspy now smoothly records any sound.
import alsaaudio
import numpy as np
import array
# constants
CHANNELS = 1
INFORMAT = alsaaudio.PCM_FORMAT_FLOAT_LE
RATE = 44100
FRAMESIZE = 1024
# set up audio input
recorder=alsaaudio.PCM(type=alsaaudio.PCM_CAPTURE)
recorder.setchannels(CHANNELS)
recorder.setrate(RATE)
recorder.setformat(INFORMAT)
recorder.setperiodsize(FRAMESIZE)
buffer = array.array('f')
while <some condition>:
buffer.fromstring(recorder.read()[1])
data = np.array(buffer, dtype='f')
FORMAT = pyaudio.paInt16
Make sure to set the correct format, my internal microphone was set to 24 Bit (see Audio-Midi-Setup application).
I had the same issue on the really slow raspberry pi, but I was able to solve it (for most cases) by using the faster array module for storing the data.
import array
import pyaudio
FORMAT = pyaudio.paInt16
CHANNELS = 1
INPUT_CHANNEL=2
RATE = 48000
CHUNK = 512
p = pyaudio.PyAudio()
stream = p.open(format=FORMAT,
channels=CHANNELS,
rate=RATE,
input=INPUT_CHANNEL,
frames_per_buffer =CHUNK)
print("* recording")
try:
data = array.array('h')
for i in range(0, int(RATE / CHUNK * RECORD_SECONDS)):
data.fromstring(stream.read(CHUNK))
finally:
stream.stop_stream()
stream.close()
p.terminate()
print("* done recording")
The content of data is rather binary afterwards.
But you can use numpy.array(data, dtype='i') to get a numpy array of intergers.
Instead of
chunk = 1024
use:
chunk = 4096
It worked for me on a USB microphone.
This was helpful for me:
input_ = stream.read(chunk, exception_on_overflow=False)
exception_on_overflow = False
For me this helped: https://stackoverflow.com/a/46787874/5047984
I used multiprocessing to write the file in parallel to recording audio. This is my code:
recordAudioSamples.py
import pyaudio
import wave
import datetime
import signal
import ftplib
import sys
import os
# configuration for assos_listen
import config
# run the audio capture and send sound sample processes
# in parallel
from multiprocessing import Process
# CONFIG
CHUNK = config.chunkSize
FORMAT = pyaudio.paInt16
CHANNELS = 1
RATE = config.samplingRate
RECORD_SECONDS = config.sampleLength
# HELPER FUNCTIONS
# write to ftp
def uploadFile(filename):
print("start uploading file: " + filename)
# connect to container
ftp = ftplib.FTP(config.ftp_server_ip, config.username, config.password)
# write file
ftp.storbinary('STOR '+filename, open(filename, 'rb'))
# close connection
ftp.quit()
print("finished uploading: " +filename)
# write to sd-card
def storeFile(filename,frames):
print("start writing file: " + filename)
wf = wave.open(filename, 'wb')
wf.setnchannels(CHANNELS)
wf.setsampwidth(p.get_sample_size(FORMAT))
wf.setframerate(RATE)
wf.writeframes(b''.join(frames))
wf.close()
print(filename + " written")
# abort the sampling process
def signal_handler(signal, frame):
print('You pressed Ctrl+C!')
# close stream and pyAudio
stream.stop_stream()
stream.close()
p.terminate()
sys.exit(0)
# MAIN FUNCTION
def recordAudio(p, stream):
sampleNumber = 0
while (True):
print("* recording")
sampleNumber = sampleNumber +1
frames = []
startDateTimeStr = datetime.datetime.now().strftime("%Y_%m_%d_%I_%M_%S_%f")
for i in range(0, int(RATE / CHUNK * RECORD_SECONDS)):
data = stream.read(CHUNK)
frames.append(data)
fileName = str(config.sensorID) + "_" + startDateTimeStr + ".wav"
# create a store process to write the file in parallel
storeProcess = Process(target=storeFile, args=(fileName,frames))
storeProcess.start()
if (config.upload == True):
# since waiting for the upload to finish will take some time
# and we do not want to have gaps in our sample
# we start the upload process in parallel
print("start uploading...")
uploadProcess = Process(target=uploadFile, args=(fileName,))
uploadProcess.start()
# ENTRYPOINT FROM CONSOLE
if __name__ == '__main__':
p = pyaudio.PyAudio()
stream = p.open(format=FORMAT,
channels=CHANNELS,
rate=RATE,
input=True,
frames_per_buffer=CHUNK)
# directory to write and read files from
os.chdir(config.storagePath)
# abort by pressing C
signal.signal(signal.SIGINT, signal_handler)
print('\n\n--------------------------\npress Ctrl+C to stop the recording')
# start recording
recordAudio(p, stream)
config.py
### configuration file for assos_listen
# upload
upload = False
# config for this sensor
sensorID = "al_01"
# sampling rate & chunk size
chunkSize = 8192
samplingRate = 44100 # 44100 needed for Aves sampling
# choices=[4000, 8000, 16000, 32000, 44100] :: default 16000
# sample length in seconds
sampleLength = 10
# configuration for assos_store container
ftp_server_ip = "192.168.0.157"
username = "sensor"
password = "sensor"
# storage on assos_listen device
storagePath = "/home/pi/assos_listen_pi/storage/"

Python Voice Communication

Hello I am trying to figure out some code which is suppose to send voice over the network. I am having problems with the audio it sends but its just a series of loud beeps and not the audio I input
After the beeps are finished I get an EOFError
I have spent the last 48 hours trying to figure this out any ideas are greatly appreciated
The relevant code
import pyaudio
import speex
import sys
chunk = 320
FORMAT = pyaudio.paInt16
CHANNELS = 1
RATE = 44100
RECORD_SECONDS = 5
### Server function ###
def server():
### Initialize socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((HOST, PORT))
s.listen(5)
### Start recieve loop
while True:
...
elif cmd == CMD_AUDIO:
d = speex.Decoder()
d.initialize(speex.SPEEX_MODEID_WB)
p = pyaudio.PyAudio()
stream = p.open(format = FORMAT,
channels = CHANNELS,
rate = RATE,
input = True,
output = True,
frames_per_buffer = chunk)
#voice = cPickle.loads(decrypt_my_message(msg))
voice = cPickle.loads(msg)
print voice
for i in range(len(voice)):
decdata = d.decode(voice[i])#DECODE my data. (YaY)#DECODE my data. (YaY)
stream.write(str(voice), chunk) #Write the data back out to the speakers
stream.stop_stream()
stream.close()
p.terminate()
d.destroy()
if not msg: break
conn.close()
### READ DATA FROM THE MIC ###
def sendAudio():
chunklist = []
init_my_audio = speex.Encoder()
init_my_audio.initialize(speex.SPEEX_MODEID_WB)
p = pyaudio.PyAudio()
stream = p.open(format = FORMAT,
channels = CHANNELS,
rate = RATE,
input = True,
output = True,
frames_per_buffer = chunk)
for i in range(0, 44100 / chunk * RECORD_SECONDS):
try:
data = stream.read(chunk)
except IOError:
pass
encdata = init_my_audio.encode(data)
chunklist.append(encdata)
client(chr(CMD_AUDIO), cPickle.dumps((chunklist), 1))
stream.stop_stream()
stream.close()
p.terminate()
init_my_audio.destroy()

Categories

Resources