is there a way in python to generate a continuous series of beeps in increasing amplitude and export it into a WAV file?
I've based this on the answer to the previous question and added a lot of comments. Hopefully this makes it clear. You'll probably want to introduce a for loop to control the number of beeps and the increasing volume.
#!/usr/bin/python
# based on : www.daniweb.com/code/snippet263775.html
import math
import wave
import struct
# Audio will contain a long list of samples (i.e. floating point numbers describing the
# waveform). If you were working with a very long sound you'd want to stream this to
# disk instead of buffering it all in memory list this. But most sounds will fit in
# memory.
audio = []
sample_rate = 44100.0
def append_silence(duration_milliseconds=500):
"""
Adding silence is easy - we add zeros to the end of our array
"""
num_samples = duration_milliseconds * (sample_rate / 1000.0)
for x in range(int(num_samples)):
audio.append(0.0)
return
def append_sinewave(
freq=440.0,
duration_milliseconds=500,
volume=1.0):
"""
The sine wave generated here is the standard beep. If you want something
more aggresive you could try a square or saw tooth waveform. Though there
are some rather complicated issues with making high quality square and
sawtooth waves... which we won't address here :)
"""
global audio # using global variables isn't cool.
num_samples = duration_milliseconds * (sample_rate / 1000.0)
for x in range(int(num_samples)):
audio.append(volume * math.sin(2 * math.pi * freq * ( x / sample_rate )))
return
def save_wav(file_name):
# Open up a wav file
wav_file=wave.open(file_name,"w")
# wav params
nchannels = 1
sampwidth = 2
# 44100 is the industry standard sample rate - CD quality. If you need to
# save on file size you can adjust it downwards. The stanard for low quality
# is 8000 or 8kHz.
nframes = len(audio)
comptype = "NONE"
compname = "not compressed"
wav_file.setparams((nchannels, sampwidth, sample_rate, nframes, comptype, compname))
# WAV files here are using short, 16 bit, signed integers for the
# sample size. So we multiply the floating point data we have by 32767, the
# maximum value for a short integer. NOTE: It is theortically possible to
# use the floating point -1.0 to 1.0 data directly in a WAV file but not
# obvious how to do that using the wave module in python.
for sample in audio:
wav_file.writeframes(struct.pack('h', int( sample * 32767.0 )))
wav_file.close()
return
append_sinewave(volume=0.25)
append_silence()
append_sinewave(volume=0.5)
append_silence()
append_sinewave()
save_wav("output.wav")
I added minor improvements to the JCx code above. As author said, its not cool to use global variables. So I wrapped his solution into class, and it works just fine:
import math
import wave
import struct
class BeepGenerator:
def __init__(self):
# Audio will contain a long list of samples (i.e. floating point numbers describing the
# waveform). If you were working with a very long sound you'd want to stream this to
# disk instead of buffering it all in memory list this. But most sounds will fit in
# memory.
self.audio = []
self.sample_rate = 44100.0
def append_silence(self, duration_milliseconds=500):
"""
Adding silence is easy - we add zeros to the end of our array
"""
num_samples = duration_milliseconds * (self.sample_rate / 1000.0)
for x in range(int(num_samples)):
self.audio.append(0.0)
return
def append_sinewave(
self,
freq=440.0,
duration_milliseconds=500,
volume=1.0):
"""
The sine wave generated here is the standard beep. If you want something
more aggresive you could try a square or saw tooth waveform. Though there
are some rather complicated issues with making high quality square and
sawtooth waves... which we won't address here :)
"""
num_samples = duration_milliseconds * (self.sample_rate / 1000.0)
for x in range(int(num_samples)):
self.audio.append(volume * math.sin(2 * math.pi * freq * ( x / self.sample_rate )))
return
def save_wav(self, file_name):
# Open up a wav file
wav_file=wave.open(file_name,"w")
# wav params
nchannels = 1
sampwidth = 2
# 44100 is the industry standard sample rate - CD quality. If you need to
# save on file size you can adjust it downwards. The stanard for low quality
# is 8000 or 8kHz.
nframes = len(self.audio)
comptype = "NONE"
compname = "not compressed"
wav_file.setparams((nchannels, sampwidth, self.sample_rate, nframes, comptype, compname))
# WAV files here are using short, 16 bit, signed integers for the
# sample size. So we multiply the floating point data we have by 32767, the
# maximum value for a short integer. NOTE: It is theortically possible to
# use the floating point -1.0 to 1.0 data directly in a WAV file but not
# obvious how to do that using the wave module in python.
for sample in self.audio:
wav_file.writeframes(struct.pack('h', int( sample * 32767.0 )))
wav_file.close()
return
if __name__ == "__main__":
bg = BeepGenerator()
bg.append_sinewave(volume=0.25, duration_milliseconds=100)
bg.append_silence()
bg.append_sinewave(volume=0.5, duration_milliseconds=700)
bg.append_silence()
bg.save_wav("output.wav")
I adjusted it a bit further, now it should be a lot faster, and I added a function for playing multiple tones at the same time.
import numpy as np
import scipy.io.wavfile
class BeepGenerator:
def __init__(self):
# Audio will contain a long list of samples (i.e. floating point numbers describing the
# waveform). If you were working with a very long sound you'd want to stream this to
# disk instead of buffering it all in memory list this. But most sounds will fit in
# memory.
self.audio = []
self.sample_rate = 44100.0
def append_silence(self, duration_milliseconds=500):
"""
Adding silence is easy - we add zeros to the end of our array
"""
num_samples = duration_milliseconds * (self.sample_rate / 1000.0)
for x in range(int(num_samples)):
self.audio.append(0.0)
return
def append_sinewave(
self,
freq=440.0,
duration_milliseconds=500,
volume=1.0):
"""
The sine wave generated here is the standard beep. If you want something
more aggressive you could try a square or saw tooth waveform. Though there
are some rather complicated issues with making high quality square and
sawtooth waves... which we won't address here :)
"""
num_samples = duration_milliseconds * (self.sample_rate / 1000.0)
x = np.array([i for i in range(int(num_samples))])
sine_wave = volume * np.sin(2 * np.pi * freq * (x / self.sample_rate))
self.audio.extend(list(sine_wave))
return
def append_sinewaves(
self,
freqs=[440.0],
duration_milliseconds=500,
volumes=[1.0]):
"""
The sine wave generated here is the standard beep. If you want something
more aggressive you could try a square or saw tooth waveform. Though there
are some rather complicated issues with making high quality square and
sawtooth waves... which we won't address here :)
len(freqs) must be the same as len(volumes)
"""
volumes = list(np.array(volumes)/sum(volumes))
num_samples = duration_milliseconds * (self.sample_rate / 1000.0)
x = np.array([i for i in range(int(num_samples))])
first_it = True
for volume, freq in zip(volumes, freqs):
print(freq)
if first_it:
sine_wave = volume * np.sin(2 * np.pi * freq * (x / self.sample_rate))
first_it = False
else:
sine_wave += volume * np.sin(2 * np.pi * freq * (x / self.sample_rate))
self.audio.extend(list(sine_wave))
return
def save_wav(self, file_name):
# Open up a wav file
# wav params
# 44100 is the industry standard sample rate - CD quality. If you need to
# save on file size you can adjust it downwards. The standard for low quality
# is 8000 or 8kHz.
# WAV files here are using short, 16 bit, signed integers for the
# sample size. So we multiply the floating point data we have by 32767, the
# maximum value for a short integer. NOTE: It is theoretically possible to
# use the floating point -1.0 to 1.0 data directly in a WAV file but not
# obvious how to do that using the wave module in python.
self.audio = np.array(self.audio).astype(np.float32)
scipy.io.wavfile.write(file_name, int(self.sample_rate), np.array(self.audio))
return
if __name__ == "__main__":
bg = BeepGenerator()
bg.append_sinewave(volume=1, duration_milliseconds=100)
bg.append_silence()
bg.append_sinewave(volume=0.5, duration_milliseconds=700)
bg.append_silence()
bg.append_sinewaves(volumes=[1, 1], duration_milliseconds=700, freqs=[880, 660])
bg.append_silence()
bg.save_wav("output.wav")
Related
For a given audio signal I want it to be spitted in to 50ms chunks to perform Fourier Transform. The problem is when I use the usual split method in Numpy it adds some high frequency components due to sudden split.
So to solve this I heard we have to use a proper window.
The splitting using the numpy function is equalent to using a rectangular window which in signal processing, a not-so-clever method!
So I need help how I can use Blackman window to make my audio signal spliced in to chunks.
So here is what I have used for a similar application in audio signal processing. To be exact I was making a spectrogram finder for songs. For that I also wanted to find the Fourier Transform of small chunks.
spacing = 0.1 # seconds
window_dura = 0.2 #seconds
signal_N = len(data) #length of whole audio
window_N = int(sample_rate * window_dura)
spacing_N = int(spacing * sample_rate)
#blackman nuttel window
a0,a1,a2,a3 = 0.3635819,0.4891775,0.1365995,0.0106411
xWindow = np.arange(0,window_N,1)
yWindow = a0 - a1 * np.cos(2 * np.pi * xWindow / window_N) + a2 * np.cos(4 * np.pi * xWindow / window_N) -a3 * np.cos(6 * np.pi * xWindow / window_N)
n = int((signal_N - window_N) / spacing_N) # number of chunks targetable (window touches the starting point
# ... total does not exceed signal length)
window_Array = np.zeros(shape=(n,siganl_N)) # all windows horizontally stacked
print("r3:shape",window_Array.shape)
for i in range(n):
window_Array[i,i*spacing_N:i*spacing_N+window_N] = yWindow
Also see how these windows are aligned (only first 10 windows are displayed)
#See how the window_Array's each "window with full duration" looks like
max_plot_N = 10
fig,ax = plt.subplots(min(max_plot_N,n),1,figsize=(30,4*min(max_plot_N,n)))
for i in range(n)[:max_plot_N]:
ax[i].plot(window_Array[i])
plt.show()
I've started to write a script, which is to generate sound of any frequency. I found some examples and tried to run them. Then I accidentally put 16000 Hz and somehow my computer played it, although it shouldn’t. Does anyone know why I can hear such high fr. sound? I mean there must be a mistake in the code.
SAMPLE_RATE = 44100
S_16BIT = 2 ** 16
def generate_sample(freq, duration, volume):
amplitude = np.round(S_16BIT * volume)
total_samples = np.round(SAMPLE_RATE * duration)
w = 2.0 * np.pi * freq / SAMPLE_RATE
k = np.arange(0, total_samples)
return np.round(amplitude * np.sin(k * w))
freq_array = np.array([260.00, 290.00, 329.63, 350.00, 392.00, 440.00, 800.00, 16000.00])
tones = []
for freq in freq_array:
tone = np.array(generate_sample(freq, 1.0, 1.0), dtype=np.int16)
tones.append(tone)
def fmain():
p = pa.PyAudio()
stream = p.open(format=p.get_format_from_width(width=2), channels=2, rate=SAMPLE_RATE, output=True)
stream.write(tones[7])
stream.stop_stream()
stream.close()
p.terminate()
fmain()
UPD: 30kHz also can be heard for some reason.
No mistake, the upper limit of the human ear is 20000 Hertz or 20 kHz, 16 kHz is well within the limit.
https://en.wikipedia.org/wiki/Hearing_range
How can I write this same code from MATLAB in Python? what modules should I use?
player = audioplayer(y, Fs);
play(player);
% y = Audio signal represented by a vector or two-dimensional array containing
%single, double, int8, uint8, or int16 values.
%Fs = Sampling rate in Hz
The simpleaudio package allows audio playback of numpy vectors with time-domain signals.
Here is a simple usage example taken from the simpleaudio package's documentation:
import numpy as np
import simpleaudio as sa
# calculate note frequencies
A_freq = 440
Csh_freq = A_freq * 2 ** (4 / 12)
E_freq = A_freq * 2 ** (7 / 12)
# get timesteps for each sample, T is note duration in seconds
sample_rate = 44100
T = 0.25
t = np.linspace(0, T, T * sample_rate, False)
# generate sine wave notes
A_note = np.sin(A_freq * t * 2 * np.pi)
Csh_note = np.sin(Csh_freq * t * 2 * np.pi)
E_note = np.sin(E_freq * t * 2 * np.pi)
# concatenate notes
audio = np.hstack((A_note, Csh_note, E_note))
# normalize to 16-bit range
audio *= 32767 / np.max(np.abs(audio))
# convert to 16-bit data
audio = audio.astype(np.int16)
# start playback
play_obj = sa.play_buffer(audio, 1, 2, sample_rate)
# wait for playback to finish before exiting
play_obj.wait_done()
I just started learning about isochronic tones and started writing a basic python script to generate the same. Following is a script that I have written for doing the following:
Generate a trapezoidal wave of beat frequency
Modulate a sine wave of base frequency, with the trapezoidal wave of beat frequency
#!/usr/bin/env python
# encoding: utf-8
"""
Small program for creating Isochronic Tones of desired base frequency, beat frequency and ramp frequencies
"""
import math
import wave
import struct
import array
import sys
def make_isochronic_wave(beat_freq, beat_ramp_percent, beat_sampl_rate, beat_time, base_freq, amplitude):
#
# The time for which the trapezoidal beat frequency wave is present in the entire cycle
#
up_time = 1 / beat_freq
#
# Gap between consequtive trapezoidal beats
#
gap_percent = up_time * 0.15
#
# To accomodate gaps
#
up_time = up_time * 0.85
#
# Total number of samples
#
data_size = beat_sampl_rate * beat_time
#
# No. of gaps per sec = No. of beats per sec
#
no_of_gaps = beat_freq
#
# Samples per gap = percentage of total time allocated for gaps * No. of samples per sec
#
sampls_per_gap = gap_percent * beat_sampl_rate
#
# Total number of samples in all the gaps
#
gap_sampls = no_of_gaps * sampls_per_gap
#
# Change the beat sample rate to accomodate gaps
#
beat_sampl_rate = beat_sampl_rate - gap_sampls
#
# nsps = Number of Samples Per Second
#
# NOTE: Please see the image at:
#
beat_nsps_defined = beat_sampl_rate * up_time
beat_nsps_inc = beat_nsps_defined * beat_ramp_percent
beat_nsps_dec = beat_nsps_defined * beat_ramp_percent
beat_nsps_stable = beat_nsps_defined - (beat_nsps_inc + beat_nsps_dec)
beat_nsps_undefined = beat_sampl_rate - beat_nsps_defined
#
# Trapezoidal values
#
values = []
#
# Trapezoidal * sine == Isochronic values
#
isoch_values = []
#
# Samples constructed
#
sampls_const = 0
#
# Iterate till all the samples in data_size are constructed
#
while sampls_const < data_size:
prev_sampl_value_inc = 0.0
prev_sampl_value_dec = 1.0
#
# Construct one trapezoidal beat wave (remember this is not the entire sample)
#
for sampl_itr in range(0, int(beat_sampl_rate)):
if sampl_itr < beat_nsps_inc:
value = prev_sampl_value_inc + (1 / beat_nsps_inc)
prev_sampl_value_inc = value
values.append(value)
if sampl_itr > beat_nsps_inc:
if sampl_itr < (beat_nsps_inc + beat_nsps_stable):
value = 1
values.append(value)
elif (sampl_itr > (beat_nsps_inc + beat_nsps_stable)) and (sampl_itr < (beat_nsps_inc + beat_nsps_stable + beat_nsps_dec)):
value = prev_sampl_value_dec - (1 / beat_nsps_dec)
prev_sampl_value_dec = value
values.append(value)
#
# Add the gap cycles
#
for gap_iter in range(0, int(sampls_per_gap)):
values.append(0)
#
# Increment the number of samples constructed to reflect the values
#
sampls_const = sampls_const + beat_nsps_defined + gap_sampls
#
# Open the wave file
#
wav_file = wave.open("beat_wave_%s_%s.wav" % (base_freq, beat_freq), "w")
#
# Define parameters
#
nchannels = 2
sampwidth = 2
framerate = beat_sampl_rate
nframes = data_size
comptype = "NONE"
compname = "not compressed"
wav_file.setparams((nchannels, sampwidth, framerate, nframes, comptype, compname))
#
# Calculate isochronic wave point values
#
value_iter = 0
for value in values:
isoch_value = value * math.sin(2 * math.pi * base_freq * (value_iter / beat_sampl_rate))
value_iter = value_iter + 1
isoch_values.append(isoch_value)
#
# Create the wave file (in .wav format)
#
for value in isoch_values:
data = array.array('h')
data.append(int(value * amplitude / 2)) # left channel
data.append(int(value * amplitude / 2)) # right channel
wav_file.writeframes(data.tostring())
wav_file.close()
try:
base_freq = int(sys.argv[1], 10)
beat_freq = int(sys.argv[2], 10)
sample_rate = int(sys.argv[3], 10)
output_time = int(sys.argv[4], 10)
ramp_percent = float(sys.argv[5])
amplitude = float(sys.argv[6])
make_isochronic_wave(beat_freq, ramp_percent, sample_rate, output_time, base_freq, amplitude)
except:
msg = """
<program> <base freqency> <beat frequency> <sample rate> <output time> <ramp percent> <amplitude>
"""
print (msg)
The above code works fine, and I get a wave of the below format:
The above format being similar to what is generated using audacity using IsoMod plugin. However, I would like to generate a tone which has the beat frequency reduce as a ramp. For this, I have enhanced the above script to call the trapezoidal wave generation in a loop, once every second. However, setting the parameters for wav file for writing multiple times (with changes in data_size due to change in beat_freq across the ramp), I am getting the following error
G:\>python gen_isochronic_tones.py 70 10 5 11025 5 0.15 8000
gen_isochronic_tones.py:195: DeprecationWarning: tostring() is deprecated. Use tobytes() instead.
make_isochronic_wave(beat_freq_start, beat_freq_end, ramp_percent, sample_rate, output_time, base_freq, amplitude)
Traceback (most recent call last):
File "gen_isochronic_tones.py", line 195, in <module>
make_isochronic_wave(beat_freq_start, beat_freq_end, ramp_percent, sample_rate, output_time, base_freq, amplitude)
File "gen_isochronic_tones.py", line 156, in make_isochronic_wave
wav_file.setparams((nchannels, sampwidth, framerate, data_size, comptype, compname))
File "G:\Python37-32\lib\wave.py", line 399, in setparams
raise Error('cannot change parameters after starting to write')
wave.Error: cannot change parameters after starting to write
Looks like wave module allows the paramters (namely data_size above) to be changed only once. Any idea how to make this work for changing data_size?
I am trying to get the power at a particular frequency, with an RTL-SDR. I'm adapting FFT examples I've found online. Abbreviated code here (removed superfluous stuff):
import matplotlib.mlab as mlab
import rtlsdr
NFFT=1024
dwell = 0.016
sample_rate = 2.4e6
offset = 200e3
freq = 100e6
sdr = rtlsdr.RtlSdr(0)
sdr.set_sample_rate(sample_rate)
sdr.set_manual_gain_enabled(1)
sdr.set_gain(22.9)
sdr.freq_correction = 0
numsamples = next_2_to_pow(int(dwell * sample_rate))
freq = freq - offset # avoid dc spike
sdr.set_center_freq(freq)
samples = sdr.read_samples(numsamples)
powers, freqs = mlab.psd(samples, NFFT=NFFT, Fs=sample_rate/1e6, window=hamming(NFFT))
bin_offset = int(offset / (sample_rate / NFFT))
freq = int(freq + offset)
pwr = float( "{:.2f}".format(10 * math.log10( powers[ int(len(powers)/2) + bin_offset ] )) )
This simply doesn't work. The power at the frequency stays about the same (noise floor level), even when I inject a signal.
I have two theories about why this doesn't work. 1) RTL-SDRs return I/Q data, and this method doesn't account for that (?) 2) My understanding of the FFT is simply not sound enough to perform it right.
Which is it? How can I fix it?
Have you tried using rtl_power then just picking the right bin in the output? It's a relatively recent addition to the rtl_sdr suite. In C, not Python. https://github.com/osmocom/rtl-sdr