The link to the source of this idea, code by Andrea Valle, does not work as I'm typing this so I can only thank him by saying his name. My contribution to all this is minor.
import numpy as N
import wave
def get_signal_data(frequency=440, duration=1, volume=32768, samplerate=44100):
"""Outputs a numpy array of intensities"""
samples = duration * samplerate
period = samplerate / float(frequency)
omega = N.pi * 2 / period
t = N.arange(samples, dtype=N.float)
y = volume * N.sin(t * omega)
return y
def numpy2string(y):
"""Expects a numpy vector of numbers, outputs a string"""
signal = "".join((wave.struct.pack('h', item) for item in y))
# this formats data for wave library, 'h' means data are formatted
# as short ints
return signal
class SoundFile:
def __init__(self, signal, filename, duration=1, samplerate=44100):
self.file = wave.open(filename, 'wb')
self.signal = signal
self.sr = samplerate
self.duration = duration
def write(self):
self.file.setparams((1, 2, self.sr, self.sr*self.duration, 'NONE', 'noncompressed'))
# setparams takes a tuple of:
# nchannels, sampwidth, framerate, nframes, comptype, compname
self.file.writeframes(self.signal)
self.file.close()
if __name__ == '__main__':
duration = 2
myfilename = 'test.wav'
data = get_signal_data(440, duration)
signal = numpy2string(data)
f = SoundFile(signal, myfilename, duration)
f.write()
print 'file written'
8 comments:
Thanks for the post, this got me started on writing wav files of my simulated analog synthesizer output. I found that you can write integer NumPy arrays directly to wav files using scipy.io.wavefile.write:
http://www.scipy.org/doc/api_docs/SciPy.io.wavfile.html
could you please write a code segment for reading a .wav file and to perform FFT on it using scipylab
Thanks very much for posting an update, doing so two years later is a true act of generosity of yours. I want to do some tests with siren sounds, and your script will be very helpful, so as the tip from Jason F.
FYI, I had a range problem with the sample values after changing the sampling frequency to 96k. The sample values can include 32768.0, which a signed short cannot handle.
This is a band aid fix:
# The output range includes 32768 which cannot fit int16_t.
# Clip it to 32767.
samples = map( lambda item: 32767 if item >= 32768 else item, samples )
So I'm working on a linux machine with and 32bit version of python and I keep getting the error "'module' object has no attribute 'struct'".
I have also run the code on a machine with the 64bit version and it works fine. Any thoughts on what is going on and/or how to fix it?
Anonymous: just use the struct module itself, not the wave.struct module (it's the same thing).
RL, to do the pack, I think I get better performance if I do this instead of the string join approach:
x = N.arange(samplecount, dtype = N.float) * omega
signal = (32767 * amplitude) * N.sin(x)
fmtstr = 'h' * samplecount
signalbuffer = struct.pack(fmtstr,*tuple(signal))
Thanks for the starting point help!
In the spirit of community, here's the program I wrote to let me do what I needed to do, using the OP as a starting point:
import numpy as N
import wave
import struct
def MakeSineWaveBuffer(frequency = [440], amplitude = [.5], duration = 5, Fs = 44100):
samplecount = duration * Fs
# create the signals
signals = []
for f, a in zip(frequency, amplitude):
period = Fs / float(f) # in sample points
omega = N.pi * 2 / period
x = N.arange(samplecount, dtype = N.float) * omega
signal = (a) * N.sin(x)
signals.append(signal)
# add them up
signal = signals[0]
for s in signals[1:]:
signal += s
# see if we are too loud
m = N.max(signal)
if m > 1.0:
signal /= m
# convert to 16-bit audio numerical space
signal *= 32767
fmtstr = 'h' * samplecount
signalbuffer = struct.pack(fmtstr,*tuple(signal))
return signalbuffer
class SoundFile(object):
def __init__(self, signal, Fs = 44100):
self.signal = signal
self.Fs = Fs
def write(self, fname):
numsamples = len(self.signal) / 2
fp = wave.open(fname, "wb")
fp.setparams((1, 2, self.Fs, numsamples, 'NONE', 'noncompressed'))
fp.writeframes(self.signal)
fp.close()
def ExtractSignalFromBuffer(buffer):
samplecount = len(buffer) / 2
fmtstr = 'h' * samplecount
return struct.unpack(fmtstr, buffer)
if __name__ == "__main__":
sig = MakeSineWaveBuffer(frequency = [100, 200,300,400], amplitude = [.3, .5, .7, .9], duration = 2, Fs = 44100)
sf = SoundFile(sig)
sf.write("test.wav")
reclaimedsig = ExtractSignalFromBuffer(sig)
import pylab
pylab.subplot(211)
pylab.plot(reclaimedsig)
pylab.grid()
pylab.subplot(212)
fftmag = (N.abs(N.fft.rfft(reclaimedsig)) ** 2)
m = N.max(fftmag)
fftmag /= m
fftfreq = N.fft.fftfreq(len(reclaimedsig)/2+1)
print len(fftmag), len(fftfreq)
pylab.plot(fftfreq, fftmag)
pylab.grid()
pylab.show()
your indents are gone in your comment.
anyway, on all these python 2.7 scripts, i'm getting this error on python 3.5:
struct.error: required argument is not an integer
Post a Comment