Volume control in Pyaudio - python

I have pilfered some of the following code from previous stack exchange posts about
creating simple sine wave tones with pyaudio. The code is written to loop through the notes of the chromatic scale over two octaves. Each note has a frequency selected from the array named "notes". My questions: Why is the volume changing with each note? How can I keep the volume the same for each note? Please know that I am a pyaudio beginner.
import pyaudio
import numpy as np
import time
from numpy import zeros
p = pyaudio.PyAudio()
SR = 44100 # sampling rate
duration = 2.0 # seconds
durationPause = 2.0
#Frequency of pitches
notes = [220.00, 233.08, 246.94, 261.63, 277.18, 293.66, 311.13, 329.63, 349.23, 369.99, 392.00, 415.30, 440.00, 466.16, 493.88, 523.25, 554.37, 587.33, 622.25, 659.25, 698.46, 739.99, 783.99, 830.61, 880.00]
stream = p.open(format=pyaudio.paFloat32,
channels=1,
rate=SR,
output=True)
count = 0
while count < 23:
count += 1
f = notes[count]
#create sine wave of frequency f
samples = (np.sin(2.0*np.pi*np.arange(int(SR*duration))*(f/SR))).astype(np.float32)
#create pause
pause = zeros(int(SR*durationPause)).astype(np.float32)
#concatenate pitch and pause
samples = np.concatenate((samples, pause))
# Play sound.
stream.write((samples).tobytes())
stream.stop_stream()
stream.close()
p.terminate()

Related

How do i create an animated (moving from left to right) waveform in Python?

I need to create moving waveform using Python and pyaudio. I thought about creating a buffer and filling it with data from audio stream. It sorta works but it's too slow. I tried changing the size of chunk to something smaller, but i always get an "Input overflowed" error.
This is the code i came up with
import numpy as np
import pyaudio as pa
import matplotlib.pyplot as plt
CHUNK = 1024
FORMAT = pa.paInt16
CHANNELS = 1
RATE = 44100
p = pa.PyAudio()
stream = p.open(
format = FORMAT,
channels = CHANNELS,
rate = RATE,
input=True,
output=True,
frames_per_buffer=CHUNK
)
buffer = [0]*CHUNK
fig, ax = plt.subplots()
x = np.arange(0,2*CHUNK,2)
line, = ax.plot(x, np.random.rand(CHUNK),'r')
ax.set_ylim(-32000,32000)
ax.ser_xlim = (0,CHUNK)
fig.show()
while True:
data = stream.read(CHUNK)
dataInt = np.frombuffer(data, dtype=np.int16)
for i in range(len(dataInt)):
buffer.insert(0, dataInt[i])
del buffer[-1]
line.set_ydata(buffer)
fig.canvas.draw()
fig.canvas.flush_events()
The main problem is that the update in the for loop is too frequent and we know each update in the data and redrawing the canvas is time-consuming. I suggest making the update once every few datapoints. You can control update frequency using a variable like UPDATE_SIZE:
UPDATE_SIZE = 32
while True:
data = stream.read(CHUNK)
dataInt = np.frombuffer(data, dtype=np.int16)
for i in range(0, len(dataInt), UPDATE_SIZE):
buffer = dataInt[i:i+UPDATE_SIZE].tolist()[::-1] + buffer[:-UPDATE_SIZE]
line.set_ydata(buffer)
fig.canvas.draw()
fig.canvas.flush_events()

playing tone with pyaudio

im trying to create an audio stream player that will receive audio through TCP, play it and save into file. This is just the player block part. For some reason pyaudio doesnt play the sine wave (sounddevice does, so its fine in the source format at least). Ive tried to play the converted format with sounddevice but without success, so that's probably where the issue lies. Can anyone point me to the right direction? thanks.
import numpy as np
import pyaudio
import sounddevice as sd
from array import array
import wave
sample_rate = 44100
Fs = 50000
f = 5000
sample = 150000
def play_stream(ob):
"converting object to pyauido required format"
tone = ob.astype(np.int16)
bytestream = tone.tobytes()
"playing data with pyaudio"
p = pyaudio.PyAudio()
stream = p.open(format=pyaudio.paInt16,
channels=1,
rate=sample_rate,
output=True)
if len(bytestream)>0:
stream.write(bytestream)
else:
print("no data!")
stream.stop_stream()
stream.close()
print("* stream finished")
p.terminate()
"testing data in alternate method"
sd.play(ob, 44100)
print ("play finished!")
"generating sine wave"
x = np.arange(sample)
ob = np.sin(2 * np.pi * f * x /Fs)
"playing sine wave"
play_stream(ob)
Edit: If i swap the format and data to float 32 it works. No idea why.
np.sin' returns a value in the range of -1 to 1. The 'astype' call is just casting it to a 16-bit integer which will give possible outputs of -1, 0, and 1. To use the full 16-bit range you need to multiply by 32767 before quantization.
scaled = ob * 32767
tone = scaled.totype(np.int16)

Why is this just playing a quarter of a sine wave in pyaudio?

I'm trying to create a sine wave with pyaudio, and it seems like this code should create an entire waveform, repeated 1000 times:
import pyaudio
import numpy as np
p = pyaudio.PyAudio()
fs = 44100 # sampling rate, Hz, must be integer
f = 440.0 # sine frequency, Hz, may be float
duration = 1/f # in seconds, may be float
def sine_sampler(length, frequency, sampling_rate=44100):
"""Generate sine samples of volume 1"""
sine_samples = (np.sin(2 * np.pi * np.arange(length) * frequency / sampling_rate))
return sine_samples.astype(np.float32)
stream = p.open(format=pyaudio.paFloat32, channels=1, rate=fs, output=True)
sound = sine_sampler(duration * fs, f)
print(sound)
for i in range(1000):
stream.write(sound)
stream.stop_stream()
stream.close()
p.terminate()
When I print the sound after generating it, I get the expected behavior: an array that cycles up and down, as one cycle of a sine wave:
[ 0. 0.06264833 0.12505053 0.18696144 0.24813785 0.30833942
0.3673296 0.42487666 0.48075455 0.53474367 0.586632 0.6362156
0.6832998 0.72769946 0.76924026 0.807759 0.84310424 0.87513727
0.9037321 0.92877656 0.9501721 0.9678348 0.98169506 0.9916987
0.9978062 0.9999937 0.99825245 0.9925895 0.983027 0.9696024
0.9523687 0.9313933 0.90675884 0.87856203 0.84691364 0.811938
0.77377254 0.73256713 0.6884838 0.64169556 0.5923863 0.5407498
0.4869888 0.43131465 0.37394598 0.3151082 0.25503248 0.19395483
0.13211517 0.06975647 0.00712373 -0.055537 -0.11797953 -0.17995858
-0.24123062 -0.30155495 -0.36069456 -0.41841713 -0.47449586 -0.5287105
-0.5808479 -0.63070345 -0.67808115 -0.7227949 -0.76466894 -0.8035389
-0.83925205 -0.87166804 -0.90065956 -0.92611265 -0.94792736 -0.96601796
-0.9803134 -0.9907575 -0.99730927 -0.9999429 -0.9986481 -0.99342996
-0.98430896 -0.9713209 -0.9545169 -0.9339628 -0.90973955 -0.8819423
-0.85068005 -0.81607586 -0.77826554 -0.7373977 -0.6936328 -0.6471429
-0.59811056 -0.54672843 -0.4931984 -0.43773076 -0.3805434 -0.32186103
-0.2619142 -0.20093836 -0.1391731 -0.07686108 -0.0142471 ]
However, the resulting sine wave is only the first quarter of a sine wave, repeated again and again, as seen in this screenshot from Audacity:
It's only when I change duration to 4/f, getting four cycles, that it actually produces a smooth, complete sine wave.
Experiments with other durations support this same pattern of only playing part of the sound. Why is only the first fourth of each cycle being played?

PyAudio - multiple channel management \ Demixing

I want to calculate the single channel data (in order to calculate the audio cross correlation between the channel 1 and channel 4) of this code:
import time
import numpy as np
import pyaudio
import scipy
from scipy import signal, fftpack
pyaud = pyaudio.PyAudio()
#open the stream
stream = pyaud.open(
format = pyaudio.paInt16,
channels = 4,
rate = 16000,
input_device_index = 4,
output = False,
input = True,
frames_per_buffer=2048,)
while True:
rawsamps = stream.read(2048)
samps = np.fromstring(rawsamps, dtype=np.int16)
frames_per_buffer_length = len(samps) / 4 #(channels)
assert frames_per_buffer_length == int(frames_per_buffer_length)
samps = np.reshape(samps, (frames_per_buffer_length, 4)) #4 channels
Assuming that the raw data is interleaved.
This is the function i need to use :
signal.correlate(n1, n2, mode='full')
how can I create an array of data for each channel in order to use the correlate function? are the last lines of the code correct?
Thank you
I found the answer, using print loudness(samps[:,0]), loudness(samps[:,3]). It print in the shell " mic 1 loudness , mic 4 loudness"

How to change audio speed without changing pitch?

I need to apply audio to video at certain time with certain duration, but some audio duration is bigger(or smaller) then needed. How to change speed of audio without changing pitch? I tried to change fps(by multiplying to division of needed duration to audio duration) but it is not work as I want.
original = VideoFileClip("orig.mp4")
clips = [orig.audio.volumex(0.3)]
subs = [] #some array
i = 0
for sub in subs:
clip = AudioFileClip("\\temp{}.mp3")
mult = clip.duration / (sub.end - sub.start) + 0.00001
clip = AudioArrayClip(clip.to_soundarray(buffersize=500, fps=24000/mult), fps=24000).set_start(sub.start).set_end(sub.end)
clips.append(clip)
i += 1
final = CompositeAudioClip(clips)
final.write_audiofile("final.mp3")
you can use librosa module:
from scipy.io import wavfile
import librosa, numpy as np
song, fs = librosa.load("song.wav")
song_2_times_faster = librosa.effects.time_stretch(song, 2)
scipy.io.wavfile.write("song_2_times_faster.wav", fs, song_2_times_faster) # save the song
Using wave: Change the sampling rate
import wave
CHANNELS = 1
swidth = 2
Change_RATE = 2
spf = wave.open('VOZ.wav', 'rb')
RATE=spf.getframerate()
signal = spf.readframes(-1)
wf = wave.open('changed.wav', 'wb')
wf.setnchannels(CHANNELS)
wf.setsampwidth(swidth)
wf.setframerate(RATE*Change_RATE)
wf.writeframes(signal)
wf.close()

Categories