Reputation: 3385
Good day. I have a small problem that may be partly math.
The thing is I want to play Sine wave without fixed frequency. Therefore, not to make the sound cracky between transitions or during fixed frequency i need the sine wave to start and to end with amplitude zero. Mathematicly I understand what has to be done.
I chosed a way, where I adapt 'time' of the sine wave so it has time to finish all cycles. Basicly y=sin(2*pift) where f*t must be whole number.
The problem is that it actually works but not fully. All waves end up very near to zero, but not exactly there. Sound is quite ok while changing frequency but not perfect. I cant figure out why the last element cant land on zero.
If you would go through it and check i would be really greatful. Thx
import pyaudio
import numpy as np
import matplotlib.pyplot as plt
p = pyaudio.PyAudio()
volume = 0.5 # range [0.0, 1.0]
fs = 44100*4 # sampling rate, Hz, must be integer
time = 0.1 # in seconds, may be float
f = 400 # sine frequency, Hz, may be float
k = np.arange(int(time*fs))
t=np.arange(0,time,1/fs)
start=0
end=time
stream = p.open(format=pyaudio.paFloat32,
channels=1,
rate=fs,
output=True)
# generate samples, note conversion to float32 array
for i in range(1000):
start = 0
end = 40 / f #time to acomplish whole whole cycles according to the give frequency - must be whole number
print(len(t))
t = np.arange(start, end, 1 / fs)
samples = (np.sin(2*np.pi*f*t)).astype(np.float32)
print(samples[0],samples[-1]) # The main problem. I need first and last elements in the sample to be zero.
# Problem is that last element is only close to zero, which make the sound not so smooth
#print(start+i,end+i)
#print(samples) # # # # # Shows first and last element
f+=1
# for paFloat32 sample values must be in range [-1.0, 1.0]
# play. May repeat with different volume values (if done interactively)
stream.write(volume*samples)
stream.stop_stream()
stream.close()
p.terminate()
Upvotes: 1
Views: 574
Reputation: 9341
The sine function repeats itself every multiple of 2*pi*N
where N is a whole number. IOW, sin(2*pi) == sin(2*pi*2) == sin(2*pi*3)
and so on.
The typical method for generating samples of a particular frequency is sin(2*pi*i*freq/sampleRate)
where i
is the sample number.
What follows is that the sine will only repeat at values of i
such that i*freq/sampleRate
is exactly equal to a whole number (I'm disregarding phase offsets).
The net result is that some frequency/sampleRate combinations may repeat after only a single cycle (1kHz @ 48kSr) whereas others may take a very long time to repeat (997Hz @ 48kSr).
It is not necessary that you change frequencies at exact zero crossings in order to avoid glitches. A better approach is this:
phaseInc = 2*pi*freq/sampleRate
y = sin(phase)
phase += phaseInc
If you are insistent on changing at a zero crossing, just do it at the nearest sample where the phase crosses a multiple of 2*pi.
Upvotes: 1