When this runs, there is a clicking sound between pitches. I don't mind the clicking sound too much - it's pleasantly rhythmic. That said...
I have seen this thread, but haven't figured out how to apply it to my problem: How to remove pops from concatented sound data in PyAudio
Any ideas? Thanks for your time!
import numpy
import pyaudio
import math
import random
def sine(frequency, length, rate):
length = int(length * rate)
factor = float(frequency) * (math.pi * 2) / rate
waveform = numpy.sin(numpy.arange(length) * factor)
return waveform
def play_tone(stream, frequency, length, rate=44100):
chunks = []
chunks.append(sine(frequency, length, rate))
chunk = numpy.concatenate(chunks) * .25
stream.write(chunk.astype(numpy.float32).tostring())
def bassline():
frequency = 300
for i in range(1000000):
play_tone(stream, frequency, .15)
change = random.choice([-75, -75, -10, 10, 2, 3, 100, -125])
print (frequency)
if frequency < 0:
frequency = random.choice([100, 200, 250, 300])
else:
frequency = frequency + change
if __name__ == '__main__':
p = pyaudio.PyAudio()
stream = p.open(format=pyaudio.paFloat32,
channels=1, rate=44100, output=4)
bassline()
/EDIT
I've plotted the tones and it looks like the discontinuity is in the relationship between the starting and ending phase of each tone.
First tone
Second tone
Any ideas how to remedy this?
As seen in the two waveform images, you are getting a clicking noise due to the rapid change of the waveform amplitude when you switch between frequencies. To get around this you need to maintain the phase of the waveform when you change frequencies. I think the simplest way to do that is to add a variable that records the last location in waveform cycle after each sine call. The end location can be used as the start location in the next sine call.
Something like:
phase_start = phase_position
phase_end = phase_start + length
waveform = numpy.sin(numpy.arange(phase_start, phase_end) * factor)
phase_position = phase_end
Note: I think this is the simplest answer that could work, but I'd recommend using the info in the question you referenced. You should maintain the phase of the played sine wave in radians. How to remove pops from concatented sound data in PyAudio
Thank you Ehz and Matthias.
In the end, I solved this by fading in and out each tone over the course of a couple hundred milliseconds. It's also a nice way to get control of clicking sound. The closer fade
is to 0
, the louder the clicking.
import math
import numpy
import pyaudio
def sine(frequency, length, rate):
length = int(length * rate)
factor = (float(frequency) * (math.pi * 2) / rate)
return numpy.sin(numpy.arange(length) * factor)
def play_tone(stream, frequency, length, rate=44100):
chunks = [sine(frequency, length, rate)]
chunk = numpy.concatenate(chunks) * 0.25
fade = 200.
fade_in = numpy.arange(0., 1., 1/fade)
fade_out = numpy.arange(1., 0., -1/fade)
chunk[:fade] = numpy.multiply(chunk[:fade], fade_in)
chunk[-fade:] = numpy.multiply(chunk[-fade:], fade_out)
stream.write(chunk.astype(numpy.float32).tostring())
def test():
test_freqs = [50, 100, 200, 400, 800, 1200, 2000, 3200]
for i in range(2):
for freq in test_freqs:
play_tone(stream, freq, 1)
if __name__ == '__main__':
p = pyaudio.PyAudio()
stream = p.open(format=pyaudio.paFloat32,
channels=1, rate=44100, output=1)
test()
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With