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()
Related
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]
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.
So I'm just wondering how I can go about playing two beeps that last for 1 second each, with a 1 second delay between them. It doesn't seem to work when I try to import time and then add a time.sleep(1) my two function calls
`play_beep()`
`time.sleep(1)`
`play_beep()`
#Function plays a 1 second beep
def play_beep():
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')
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()
play_beep()
Help would be much appreciated. Anyway of fixing this would be great!
I am using python to process the audio signal. I would like to record a signal and keep its SPL level. I am using pyaudio Library to do that. Here is the example I found. However, I find the signal is around 18db larger then it should be. Is there a way to change the recorded signal amplitude in the file wav? Second question is for the wav file, data are coded in signed 16 bits. I have the impression that the negative values are coded using Two's complement can anyone confirm this please?
Thank you very much!
enter code here def record(output_file_name, rec_device_id=None):
# Sampling = Data is recorded per batches of 1024 bits
CHUNK = 1024
FORMAT = pyaudio.paInt16
CHANNELS = 1
# Sampling = 48kHz
RATE = 48000
RECORD_SECONDS = 5
WAVE_OUTPUT_FILENAME = output_file_name
p = pyaudio.PyAudio()
stream = p.open(format=FORMAT,
channels=CHANNELS,
rate=RATE,
input=True,
frames_per_buffer=CHUNK,
input_device_index=rec_device_id)
# Sampling = 48kHz
print("*** recording start ***")
frames = []
rms=np.zeros(RATE / CHUNK * RECORD_SECONDS+1)
for i in range(0, int(RATE / CHUNK * RECORD_SECONDS)):
data = stream.read(CHUNK)
#print("*** data ***",data)
#decoded = struct.unpack('h',data)
print("*** decoded ***",decoded)
frames.append(data)
rms[i] = audioop.rms(data, 2)
print("*** recording done ***")
print("*** rms ***",rms)
stream.stop_stream()
stream.close()
p.terminate()
wf = wave.open(output_file_name, 'wb')
wf.setnchannels(CHANNELS)
wf.setsampwidth(p.get_sample_size(FORMAT))
wf.setframerate(RATE)
wf.writeframes(b''.join(frames))
wf.close()