I currently have a 2 channel wav file. I would like to have the ability to play one channel at a time. Using the examples from the PyAudio website where you read in a chunk of data through the wave module and then write to stream, I am unsure whether I need to somehow read in one channel of data and write to a one channel stream, or if there is some other solution. Thanks for the help.
I propose you alter the stream and overwrite the channel that you do not want to hear, with data from the channel you want to hear. Normally, channels are interleaved, so the following should do the trick.
import pyaudio
import wave
import sys
chunk = 1024
def convert(data, sampleSize = 4, channel = 0):
for i in range(0, len(data), 2*sampleSize):
for j in range(0, sampleSize):
data[i + j + sampleSize * channel] = data[i + j + sampleSize * (1 - channel)]
if len(sys.argv) < 2:
print "Plays a wave file.\n\n" +\
"Usage: %s filename.wav" % sys.argv[0]
sys.exit(-1)
wf = wave.open(sys.argv[1], 'rb')
p = pyaudio.PyAudio()
# open stream
stream = p.open(format =
p.get_format_from_width(wf.getsampwidth()),
channels = wf.getnchannels(),
rate = wf.getframerate(),
output = True)
# read data
data = wf.readframes(chunk)
convert(data)
# play stream
while data != '':
stream.write(data)
data = wf.readframes(chunk)
convert(data)
stream.close()
p.terminate()
Related
I have a normal stream in Pyaudio that plays a wav file but I can not find any way to change the volume of the stream. I tried multiplying the data by some number but that did not work.
import pyaudio
import wave
audio = pyaudio.PyAudio()
chunk = 1024
af = wave.open("filename.wav", 'rb')
pa = pyaudio.PyAudio()
stream = pa.open(format =
pa.get_format_from_width(af.getsampwidth()),
channels = af.getnchannels(),
rate = af.getframerate(),
output_device_index=9,
output = True)
rd_data = af.readframes(chunk)
while rd_data != '':
#do some majic to change the volume
stream.write(rd_data)
rd_data = af.readframes(chunk)
You can use numpy for this:
import numpy
def audio_datalist_set_volume(datalist, volume):
""" Change value of list of audio chunks """
sound_level = (volume / 100.)
for i in range(len(datalist)):
chunk = numpy.fromstring(datalist[i], numpy.int16)
chunk = chunk * sound_level
datalist[i] = chunk.astype(numpy.int16)
With the Python script shown below I try to play a wav file from the internet but I'm getting the error message OSError: [Errno 22] Invalid argument: 'https://file-examples-com.github.io/uploads/2017/11/file_example_WAV_1MG.wav'.
How can I play a wav file from the internet?
import pyaudio
import wave
chunk = 1024
f = wave.open("https://file-examples-com.github.io/uploads/2017/11/file_example_WAV_1MG.wav","rb")
p = pyaudio.PyAudio()
stream = p.open(format = p.get_format_from_width(f.getsampwidth()),
channels = f.getnchannels(),
rate = f.getframerate(),
output = True)
data = f.readframes(chunk)
while data:
stream.write(data)
data = f.readframes(chunk)
stream.stop_stream()
stream.close()
p.terminate()
You can also get the content of website, store it in a variable, and play it. There is no need to store it on the disk for a short file like this. Here is an example of how to do this:
import logging
import requests
import simpleaudio
sample_rate = 8000
num_channels = 2
bytes_per_sample = 2
total = sample_rate * num_channels * bytes_per_sample
logging.basicConfig(level=logging.INFO)
audio_url = "https://file-examples-com.github.io/uploads/2017/11/file_example_WAV_1MG.wav"
logging.info(f"Downloading audio file from: {audio_url}")
content = requests.get(audio_url).content
# Just to ensure that the file does not have extra bytes
blocks = len(content) // total
content = content[:total * blocks]
wave = simpleaudio.WaveObject(audio_data=content,
sample_rate=sample_rate,
num_channels=num_channels,
bytes_per_sample=bytes_per_sample)
control = wave.play()
control.wait_done()
I'm demonstrating what #larsks suggests.
import requests
with open(audio_file, 'wb') as a:
resp = requests.get("https://file-examples-com.github.io/uploads/2017/11/file_example_WAV_1MG.wav")
if resp.status_code == 200:
a.write(resp.content)
print('downloaded')
else:
print(resp.reason)
exit(1)
f = wave.open(audio_file, "rb")
# the remaining lines are the same
And I also suggest another great python library python-mpv which is based on mpv, this library can handle much more codecs and also online streaming play.
For a project I need to
read WAV file
filter
play WAV file
The code below is "working" except that the sound crackles, and that is because I can't guarantee a continue data stream to the audio output. Filtering takes some time and that's where the sound freezes for a very short time.
In the future I'd like to do some extra calculations on the raw audio data. I'd like to know which method is the best to keep the sound smooth.
import pyaudio
import wave
from scipy import signal
from struct import *
chunk = 1024
f = wave.open("sample.wav","rb")
p = pyaudio.PyAudio()
stream = p.open(format = p.get_format_from_width(f.getsampwidth()),
channels = f.getnchannels(),
rate = f.getframerate(),
output = True)
data = f.readframes(chunk)
b, a = signal.butter(2, 0.01)
tmp = len(data)/2
s = '<'
for i in range(0,tmp):
s = s + 'h'
while data != '':
sig = signal.filtfilt(b, a, unpack(s,data))
output_signal = pack(s,*sig)
stream.write(output_signal)
data = f.readframes(chunk)
stream.stop_stream()
stream.close()
p.terminate()
Thanks in advance!
You can try splitting the producer of the data and the consumer (which is pyaudio) in two programs, with a regular Unix pipe connecting them. The Unix pipe plays the role of buffering. Example:
in producer.py:
while data != '':
sig = signal.filtfilt(b, a, unpack(s,data))
output_signal = pack(s,*sig)
sys.stdout.write(output_signal) # <===
data = f.readframes(chunk)
in consumer.py:
while 1:
data = sys.stdin.read(1024)
if not data: break
stream.write(data)
Command-line invocation:
python producer.py | python consumer.py
If it works, then you can also run python consumer.py from inside the other program with os.popen(), for example.
I want to record and play my voice using pyaudio and wave lib but I don't know how to do it because wave lib requires a path to a file and even if I'm trying to set it as a variable with list of bytes recorded a few second ago, still doesn't work beacuse I can't use 'read' for a list. Does someone have some idea? I want to make a looper like KORG stuff, etc
I want to play it immediately after stopped recording, like real looper, without saving record as file.
There is my code (Python 3.4):
def record(self): #recording a voice
#var for bytes from recording
self.stream = self.player.open(format = self.FORMAT,
channels = self.CHANNELS,
rate = self.RATE,
input = True,
frames_per_buffer = self.CHUNK)
print("Recording")
self.frames = [] #byte list
#recoring for a few seconds (5sec at this moment)
for i in range(0, int(self.RATE / self.CHUNK * self.RECORD_SECONDS)):
self.data = self.stream.read(self.CHUNK) #sing stream do data var
self.frames.append(self.data) #add bytes to the end of a list
print("Stop recording")
self.stopRecording()
def stopRecording(self):
self.stream.stop_stream()
self.stream.close()
print("Recording has been stopped")
self.play()
def play(self): #playing a record
print("Playing")
f = wave.open(self.frames,"rb")
#read data
data = f.readframes(CHUNK)
#play stream
while data != '':
self.stream.write(data)
data = f.readframes(CHUNK)
self.stopPlaying()
After stop your record you need join your appended data, use data = ''.join(self.frames), and at the end build a loop (for, while) to stream all your byte list, here is how i did:
import pyaudio
chunk = 1024
FORMAT = pyaudio.paInt16
CHANNELS = 1
RATE = 41000
RECORD_SECONDS = 5
p = pyaudio.PyAudio()
stream = p.open(format = FORMAT,
channels = CHANNELS,
rate = RATE,
input = True,
output = 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("***Stop recording***")
print ("***START PLAY***")
data = ''.join(all)
for i in range(0, len(data), chunk):
stream.write(data[i:i+chunk])
I am looking to have a python script run in the background and use pyaudio to record sound files when the threshold of the microphone has reached a certain point. This is for a monitor on a two way radio network. So hence we only want to record transmitted audio.
Tasks in mind:
Record audio input on a n% gate threshold
stop recording after so many seconds of silence
keep recording for so many seconds after audio
Phase 2: input data into MySQL database to search the recordings
I am looking at a file structure of the similar
/home/Recodings/2013/8/23/12-33.wav would be a recording of the transmision on 23/08/2013 # 12:33.wav
I have used the code from
Detect and record a sound with python
I am at a bit of a loss where to go from here now and a little guidance would be greatly appreciated
thank you
The current top answer is a bit outdated and only works for python 2. Here is a version updated for python 3. It wraps the functions into classes and packages everything into one simple easy-to-use version. Note that there is one key difference between the top answer and my script:
The script at the top records for one file and then stops, while my script keeps recording whenever noise is detected and dumps the recordings into a directory as it goes.
The main idea for both scripts are pretty similar:
Step 1: 'Listen' until rms becomes greater than the threshold
Step 2: Start recording, set a timer for when to stop recording, == TIMEOUT_LENGTH
Step 3: If the rms breaks threshold again before the timer times out reset the timer
Step 4: Now that the timer is expired, write the recording to a directory and go back to step 1
import pyaudio
import math
import struct
import wave
import time
import os
Threshold = 10
SHORT_NORMALIZE = (1.0/32768.0)
chunk = 1024
FORMAT = pyaudio.paInt16
CHANNELS = 1
RATE = 16000
swidth = 2
TIMEOUT_LENGTH = 5
f_name_directory = r'C:\Users\Jason\PyCharmProjects\AutoRecorder\records'
class Recorder:
#staticmethod
def rms(frame):
count = len(frame) / swidth
format = "%dh" % (count)
shorts = struct.unpack(format, frame)
sum_squares = 0.0
for sample in shorts:
n = sample * SHORT_NORMALIZE
sum_squares += n * n
rms = math.pow(sum_squares / count, 0.5)
return rms * 1000
def __init__(self):
self.p = pyaudio.PyAudio()
self.stream = self.p.open(format=FORMAT,
channels=CHANNELS,
rate=RATE,
input=True,
output=True,
frames_per_buffer=chunk)
def record(self):
print('Noise detected, recording beginning')
rec = []
current = time.time()
end = time.time() + TIMEOUT_LENGTH
while current <= end:
data = self.stream.read(chunk)
if self.rms(data) >= Threshold: end = time.time() + TIMEOUT_LENGTH
current = time.time()
rec.append(data)
self.write(b''.join(rec))
def write(self, recording):
n_files = len(os.listdir(f_name_directory))
filename = os.path.join(f_name_directory, '{}.wav'.format(n_files))
wf = wave.open(filename, 'wb')
wf.setnchannels(CHANNELS)
wf.setsampwidth(self.p.get_sample_size(FORMAT))
wf.setframerate(RATE)
wf.writeframes(recording)
wf.close()
print('Written to file: {}'.format(filename))
print('Returning to listening')
def listen(self):
print('Listening beginning')
while True:
input = self.stream.read(chunk)
rms_val = self.rms(input)
if rms_val > Threshold:
self.record()
a = Recorder()
a.listen()
Some time ago I wrote some of the steps
Record audio input on a n% gate threshold
A: Start a Boolean variable type for "Silence" and you can calculate RMS to decide if Silence is true or False, Set one RMS Threshold
stop recording after so many seconds of silence
A: Do you need calculate one timeout, for it get the Frame Rate, Chunk Size and how many seconds do you want, to calculate your timeout make (FrameRate / chunk * Max_Seconds)
keep recording for so many seconds after audio
A: If Silence is false == (RMS > Threshold) get the last chunk of data of audio (LastBlock) and just keep record :-)
Phase 2: input data into MySQL database to search the recordings
A: This step is up to you
Source code:
import pyaudio
import math
import struct
import wave
#Assuming Energy threshold upper than 30 dB
Threshold = 30
SHORT_NORMALIZE = (1.0/32768.0)
chunk = 1024
FORMAT = pyaudio.paInt16
CHANNELS = 1
RATE = 16000
swidth = 2
Max_Seconds = 10
TimeoutSignal=((RATE / chunk * Max_Seconds) + 2)
silence = True
FileNameTmp = '/home/Recodings/2013/8/23/12-33.wav'
Time=0
all =[]
def GetStream(chunk):
return stream.read(chunk)
def rms(frame):
count = len(frame)/swidth
format = "%dh"%(count)
# short is 16 bit int
shorts = struct.unpack( format, frame )
sum_squares = 0.0
for sample in shorts:
n = sample * SHORT_NORMALIZE
sum_squares += n*n
# compute the rms
rms = math.pow(sum_squares/count,0.5);
return rms * 1000
def WriteSpeech(WriteData):
stream.stop_stream()
stream.close()
p.terminate()
wf = wave.open(FileNameTmp, 'wb')
wf.setnchannels(CHANNELS)
wf.setsampwidth(p.get_sample_size(FORMAT))
wf.setframerate(RATE)
wf.writeframes(WriteData)
wf.close()
def KeepRecord(TimeoutSignal, LastBlock):
all.append(LastBlock)
for i in range(0, TimeoutSignal):
try:
data = GetStream(chunk)
except:
continue
#I chage here (new Ident)
all.append(data)
print "end record after timeout";
data = ''.join(all)
print "write to File";
WriteSpeech(data)
silence = True
Time=0
listen(silence,Time)
def listen(silence,Time):
print "waiting for Speech"
while silence:
try:
input = GetStream(chunk)
except:
continue
rms_value = rms(input)
if (rms_value > Threshold):
silence=False
LastBlock=input
print "hello ederwander I'm Recording...."
KeepRecord(TimeoutSignal, LastBlock)
Time = Time + 1
if (Time > TimeoutSignal):
print "Time Out No Speech Detected"
sys.exit()
p = pyaudio.PyAudio()
stream = p.open(format = FORMAT,
channels = CHANNELS,
rate = RATE,
input = True,
output = True,
frames_per_buffer = chunk)
listen(silence,Time)
So you just need the getLevel(data) function?
A quick hack would be:
def getLevel(data):
sqrsum = 0
for b in data:
b = ord(b)
sqrsum+=b*b
return sqrsum
That should increase with volume. Set your threshold appropriately through trial and error.
For those who have problems installing pyaudio because of the missing portaudio.h, you can do that:
sudo apt-get install portaudio19-dev python-pyaudio python3-pyaudio
the answer is from: portaudio.h: No such file or directory