I am trying to dynamically modify the volume of a song by messing with numpy arrays. And I did it, but I can hear some strange noises in addition to the main song. I think the problem is "numpy.int8".
import pyaudio
import wave
import numpy
def audio_datalist_set_volume(datalist, volume):
""" Change value of list of audio chunks """
sound_level = (volume / 100.)
chunk = numpy.frombuffer(datalist, numpy.int8)
chunk = chunk * sound_level
return chunk.astype(numpy.int8)
CHUNK = 1024
# if len(sys.argv) < 2:
# print("Plays a wave file.\n\nUsage: %s filename.wav" % sys.argv[0])
# sys.exit(-1)
wf = wave.open("your_audio.wav", 'rb')
# instantiate PyAudio (1)
p = pyaudio.PyAudio()
# open stream (2)
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)
# play stream (3)
while len(data) > 0:
nda = audio_datalist_set_volume(data, 20)
stream.write(nda)
data = wf.readframes(CHUNK)
# stop stream (4)
stream.stop_stream()
stream.close()
# close PyAudio (5)
p.terminate()
Update: I think I have found the problem: numpy.int8 should be changed for "b" or "i1". Is it a proper decision?
If you want to handle arbitrary wav files, you should make sure the numpy data type matches the format of the stream:
def audio_datalist_set_volume(datalist, volume, numpy_type):
""" Change value of list of audio chunks """
sound_level = (volume / 100.)
chunk = numpy.frombuffer(datalist, numpy_type)
chunk = chunk * sound_level
return chunk
# ...
numpy_type = {
pyaudio.paFloat32: np.float32,
pyaudio.paInt32: np.int32,
# numpy can't do 24-bit ints :(
pyaudio.paInt16: np.int16,
pyaudio.paInt8: np.int8,
pyaudio.paUInt8: np.uint8,
}[stream._format]
Related
I'm writing a simple player in python using the pyaudio Library, with some basic functionalities, such start play, pause and start position.
I started working on the first example of the Documentation:
import pyaudio
import wave
import sys
CHUNK = 1024
if len(sys.argv) < 2:
print("Plays a wave file.\n\nUsage: %s filename.wav" % sys.argv[0])
sys.exit(-1)
wf = wave.open(sys.argv[1], 'rb')
# instantiate PyAudio (1)
p = pyaudio.PyAudio()
# open stream (2)
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)
# play stream (3)
while len(data) > 0:
stream.write(data)
data = wf.readframes(CHUNK)
# stop stream (4)
stream.stop_stream()
stream.close()
# close PyAudio (5)
p.terminate()
It works perfectly but I really wouldn't know where to add a frame offset to start the playback at a specific frame.
I saw that there are different libraries available, but PyAudio allows me to read the raw data from the file in real time, and I need this functionality.
Do you have any suggestions?
You just have to count how many bytes to move in the audio.
nbytes = wf.getsampwidth() # Gives the number of bytes per sample for 1 channel
nchannels = wf.getnchannels() # Number of channels
sample_rate = wf.getframerate() # Number of samples per second 44100 is 44100 samples per second
nbytes_per_sample_per_channel = nbytes * nchannels
nbytes_per_second = nbytes_per_sample_per_channel * sample_rate
skip_seconds = 5 # Skip 5 seconds
wf.readframes(int(skip_seconds * nbytes_per_second)) # Read data that you want to skip
Start playing the file after the offset was read
# read data
data = wf.readframes(CHUNK)
# play stream (3)
while len(data) > 0:
stream.write(data)
data = wf.readframes(CHUNK)
# stop stream (4)
stream.stop_stream()
stream.close()
# close PyAudio (5)
p.terminate()
I'm trying to play the ".wav" file infinitely to use in my experiment.
I'm using the script of the pyaudio website (http://people.csail.mit.edu/hubert/pyaudio/), however it plays for only 5 seconds.
I tried to use the code below, but it plays for some seconds.
import pyaudio
import wave
while True:
CHUNK = 20*100
wf =
wave.open('Metano_Ref_Lockin=SR830_mod=0.460V_freq=3936_PP=20_NP=100.wav', 'rb')
data = wf.readframes(CHUNK)
p = pyaudio.PyAudio()
stream = p.open(format=pyaudio.paInt16,
channels=wf.getnchannels(),
rate=wf.getframerate(),
output_device_index=4,
output=True)
while data != '':
stream.write(data)
data = wf.readframes(CHUNK)
stream.stop_stream()
stream.close()
p.terminate()
On the other hand, this code works, nevertheless, the signal is not uniform (some noises appear).
import pyaudio
import wave
CHUNK = 20*100
wf = wave.open('Metano_Ref_Lockin=SR830_mod=0.460V_freq=3936_PP=20_NP=100.wav', 'rb')
data = wf.readframes(CHUNK)
p = pyaudio.PyAudio()
stream = p.open(format=pyaudio.paInt16,
channels=wf.getnchannels(),
rate=wf.getframerate(),
output_device_index=4,
output=True)
while data != '':
stream.write(data)
stream.stop_stream()
stream.close()
p.terminate()
I expect a uniform signal to be reproduced infinitely.
Thanks.
Your data comparison should be
while data != b'':
...
or the simpler variant (empty strings cast to False):
while data:
...
Also, you really should reuse wf, stream, and p inbetween loops:
import pyaudio
import wave
CHUNK = 2 ** 11
wf = wave.open('Metano_Ref_Lockin=SR830_mod=0.460V_freq=3936_PP=20_NP=100.wav', 'rb')
p = pyaudio.PyAudio()
stream = p.open(format=pyaudio.paInt16,
channels=wf.getnchannels(),
rate=wf.getframerate(),
output=True)
while True:
wf.rewind()
data = wf.readframes(CHUNK)
while data:
stream.write(data)
data = wf.readframes(CHUNK)
wf.close()
stream.stop_stream()
stream.close()
p.terminate()
I have been searching for this since last week. Tried pyaudio also and when i used its another fork the system audio was mixed with microphone audio. I was not able to find any other module for this and thus finally asked the question.
Edit:
import pyaudio
import wave
CHUNK = 1024
FORMAT = pyaudio.paInt16
CHANNELS = 2
RATE = 44100
RECORD_SECONDS = 5
WAVE_OUTPUT_FILENAME = "output.wav"
p = pyaudio.PyAudio()
SPEAKERS = p.get_default_output_device_info()["hostApi"] #The modified part
stream = p.open(format=FORMAT,
channels=CHANNELS,
rate=RATE,
input=True,
frames_per_buffer=CHUNK,
input_host_api_specific_stream_info=SPEAKERS,
as_loopback = True) #The part I have modified
print("* recording")
frames = []
for i in range(0, int(RATE / CHUNK * RECORD_SECONDS) + 1):
data = stream.read(CHUNK)
frames.append(data)
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()
This code was taken from stack overflow. It records the speaker output but the output is mixed with the microphone Input.
Also the pyaudio module used was from the fork : https://github.com/intxcc/pyaudio_portaudio.
using https://github.com/intxcc/pyaudio_portaudio
This only records the audio of the device specified by "device_id"
import pyaudio
import wave
chunk = 1024 # Record in chunks of 1024 samples
sample_format = pyaudio.paInt16 # 16 bits per sample
channels = 2
fs = 44100 # Record at 44100 samples per second
seconds = 3
filename = "output.wav"
p = pyaudio.PyAudio() # Create an interface to PortAudio
#Select Device
print ( "Available devices:\n")
for i in range(0, p.get_device_count()):
info = p.get_device_info_by_index(i)
print ( str(info["index"]) + ": \t %s \n \t %s \n" % (info["name"], p.get_host_api_info_by_index(info["hostApi"])["name"]))
pass
#ToDo change to your device ID
device_id = 7
device_info = p.get_device_info_by_index(device_id)
channels = device_info["maxInputChannels"] if (device_info["maxOutputChannels"] < device_info["maxInputChannels"]) else device_info["maxOutputChannels"]
# https://people.csail.mit.edu/hubert/pyaudio/docs/#pyaudio.Stream.__init__
stream = p.open(format=sample_format,
channels=channels,
rate=int(device_info["defaultSampleRate"]),
input=True,
frames_per_buffer=chunk,
input_device_index=device_info["index"],
as_loopback=True
)
frames = [] # Initialize array to store frames
print('\nRecording', device_id, '...\n')
# 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, 'wb')
wf.setnchannels(channels)
wf.setsampwidth(p.get_sample_size(sample_format))
wf.setframerate(fs)
wf.writeframes(b''.join(frames))
wf.close()
P.S. check out https://github.com/intxcc/pyaudio_portaudio/tree/master/example
This can be done with soundcard. You will have to figure out which device index to use for your loopback. This code prints out the ones you will have to choose from. I found the correct one by looping over all of them and seeing which produced non zeros when speakers were playing.
pip install soundcard
import soundcard as sc
import time
# get a list of all speakers:
speakers = sc.all_speakers()
# get the current default speaker on your system:
default_speaker = sc.default_speaker()
# get a list of all microphones:v
mics = sc.all_microphones(include_loopback=True)
# get the current default microphone on your system:
default_mic = mics[index of your speaker loopback here]
for i in range(len(mics)):
try:
print(f"{i}: {mics[i].name}")
except Exception as e:
print(e)
with default_mic.recorder(samplerate=148000) as mic, \
default_speaker.player(samplerate=148000) as sp:
print("Recording...")
data = mic.record(numframes=1000000)
print("Done...Stop your sound so you can hear playback")
time.sleep(5)
sp.play(data)
I install a virtul soundcard(blackhole) on mac to record the system audio, and is worked.
I only record system audio without microphone audio, as I don't need it
On Ubuntu, you can use 'pavucontrol' to change the recording source. An example of recording audio directly from the speakers (without using a microphone):
First you run a script like the one below:
import pyaudio
mic = pyaudio.PyAudio()
stream = mic.open(format=pyaudio.paInt16, channels=1, rate=44100, input=True, output=True, frames_per_buffer=2048)
stream.start_stream()
if __name__ == '__main__':
while True:
data = stream.read(1024)
# Do something with sound
Then you can change the recording source (recording tab) from 'Built-in=Audio Analog Stereo' to 'Monitor of Built-in=Audio Analog Stereo'.
With this approach, you can analyze the sound from the speakers during the video call.
I looked at this question: pyaudio help play a file
While this question did get answered I never got a clear answer of where to actually put the song file.
This is the code for playing a WAVE file:
""" Play a WAVE file. """
import pyaudio
import wave
import sys
chunk = 1024
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)
# play stream
while data != '':
stream.write(data)
data = wf.readframes(chunk)
stream.close()
p.terminate()
I've looked through the code but I can't find anything in the code where I actually insert the music file itself. When I press the "Play" button in my program (I use wxform with this program) nothing is played.
The magic line is:
wf = wave.open(sys.argv[1], 'rb')
This seems to say that the first argument to the script (sys.argv[1]) is used as the input for waves.
I don't know anything of pyaudio but it seems pretty clear that the song file is the first argument that is passed to the program when you execute it. Look att this line: wf = wave.open(sys.argv[1], 'rb') Just change to sys.arg[1] to 'c:/filename.wav' or something.
And the program won't run as it is written now if you don't pass any argument to it. Because of the if len(sys.argv) < 2 block
Just know a few things of python, pyaudio but it seems that the song file is the first argument that is passed to the program when you execute it.
Just change insert an argument like this :
python your-python_file.py sound_file.wav
regards.
Here is a solution :
Comment the If Statment and directly add the file name to play
import pyaudio
import wave
import sys
CHUNK = 1024
#if len(sys.argv) < 2:
# print("Plays a wave file.\n\nUsage: %s output.wav" % sys.argv[0])
# sys.exit(-1)
wf = wave.open("output.wav", 'rb')
p = pyaudio.PyAudio()
stream = p.open(format=p.get_format_from_width(wf.getsampwidth()),
channels=wf.getnchannels(),
rate=wf.getframerate(),
output=True)
data = wf.readframes(CHUNK)
while data != '':
stream.write(data)
data = wf.readframes(CHUNK)
stream.stop_stream()
stream.close()
p.terminate()
I do not understand the example material for pyaudio. It seems they had written an entire small program and it threw me off.
How do I just play a single audio file?
Format is not an issue, I just want to know the bare minimum code I need to play an audio file.
May be this small wrapper (warning: created on knees) of their example will help you to understand the meaning of code they wrote.
import pyaudio
import wave
import sys
class AudioFile:
chunk = 1024
def __init__(self, file):
""" Init audio stream """
self.wf = wave.open(file, 'rb')
self.p = pyaudio.PyAudio()
self.stream = self.p.open(
format = self.p.get_format_from_width(self.wf.getsampwidth()),
channels = self.wf.getnchannels(),
rate = self.wf.getframerate(),
output = True
)
def play(self):
""" Play entire file """
data = self.wf.readframes(self.chunk)
while data != b'':
self.stream.write(data)
data = self.wf.readframes(self.chunk)
def close(self):
""" Graceful shutdown """
self.stream.close()
self.p.terminate()
# Usage example for pyaudio
a = AudioFile("1.wav")
a.play()
a.close()
The example seems pretty clear to me. You simply save the example as playwav.py call:
python playwav.py my_fav_wav.wav
The wave example with some extra comments:
import pyaudio
import wave
import sys
# length of data to read.
chunk = 1024
# validation. If a wave file hasn't been specified, exit.
if len(sys.argv) < 2:
print "Plays a wave file.\n\n" +\
"Usage: %s filename.wav" % sys.argv[0]
sys.exit(-1)
'''
************************************************************************
This is the start of the "minimum needed to read a wave"
************************************************************************
'''
# open the file for reading.
wf = wave.open(sys.argv[1], '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:
# writing to the stream is what *actually* plays the sound.
stream.write(data)
data = wf.readframes(chunk)
# cleanup stuff.
wf.close()
stream.close()
p.terminate()
This way requires ffmpeg for pydub, but can play not only wave files:
import pyaudio
import sys
from pydub import AudioSegment
if len(sys.argv) <= 1:
print('No File Name!')
sys.exit(1)
chunk = 1024
fn = ' '.join(sys.argv[1:])
pd = AudioSegment.from_file(fn)
p = pyaudio.PyAudio()
stream = p.open(format =
p.get_format_from_width(pd.sample_width),
channels = pd.channels,
rate = pd.frame_rate,
output = True)
i = 0
data = pd[:chunk]._data
while data:
stream.write(data)
i += chunk
data = pd[i:i + chunk]._data
stream.close()
p.terminate()
sys.exit(0)