Showing posts with label wave. Show all posts
Showing posts with label wave. Show all posts

Sunday, February 21, 2010

How to make a .wav file with Python, revisited

I've noticed that my two-year old post about making a .wav file with Python is being read a lot so I forced myself to modify the code a little. Now it should be nicer to read and modify further according to your needs/wishes. I've also incorporated the comments of helpful readers Mike Axiak and Fingon. Thanks guys ;-)
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'

Friday, July 4, 2008

How to make a simple .wav file with Python

EDIT: I've edited this code a little and incorporated reader's comments, here's the new version

I've recently had an idea to test some vibration-resonance problems by playing a set of sounds and detecting the vibrations in question. The glitch is, how to create a sound of desired frequency? I believe there is a lot of ways to solve this but having only a hammer (read Python) as my tool it looked like a nail ;-)

The idea is then to create a simple sine wave and play it back with a computer. My first try was with pygame module but the sound quality was poor and unsatisfactory.
If not playing sound directly one can write the wave to the file and then play the file with whatever means are suitable. This turned out to be the better way after all.
A search of the internet turned up this discussion of creating .wav files with Python. I've adopted the "minimal example" from there (written by Andrea Valle) to be usable with modern modules (it used obsolete Numeric and RandomArray modules). The code is not neat but it works well.

import numpy as N
import wave

class SoundFile:
def __init__(self, signal):
self.file = wave.open('test.wav', 'wb')
self.signal = signal
self.sr = 44100

def write(self):
self.file.setparams((1, 2, self.sr, 44100*4, 'NONE', 'noncompressed'))
self.file.writeframes(self.signal)
self.file.close()

# let's prepare signal
duration = 4 # seconds
samplerate = 44100 # Hz
samples = duration*samplerate
frequency = 440 # Hz
period = samplerate / float(frequency) # in sample points
omega = N.pi * 2 / period

xaxis = N.arange(int(period),dtype = N.float) * omega
ydata = 16384 * N.sin(xaxis)

signal = N.resize(ydata, (samples,))

ssignal = ''
for i in range(len(signal)):
ssignal += wave.struct.pack('h',signal[i]) # transform to binary

f = SoundFile(ssignal)
f.write()
print 'file written'
This code should work as intended and produce .wav files of given length and frequency. A few comments:
a) if you need different sampling frequencies, just replace all 44100 numbers with desired sampling frequency;
b) the setparams command is relatively well described in wave doc in Python help, its fourth parameter (nframes) may probably be whatever number you want, the procedure is designed to write all data you sent to it;
c) the 16384 number seems to be a volume setting for .wav file, this value is near the maximum so only lowering the volume is possible from here;
d) wave.struct.pack('h', #) - this was tough to find but without this command the resulting file will not be correct (at least on Windows machines), I do not yet understand what it does...