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...

Thursday, July 3, 2008

Introductory and testing first post

Hello there, whoever reads this!
I've started this blog to collect my dilluted and unorganized programming and general computer knowledge. I'm not a full time programmer and all of my coding skills were self-taught. There are long periods of time when I'm not coding and having bad memory this causes me to forget anything I've ever known. After a year without programming I usually even forget the basic language commands and syntax.
This site is dedicated to preserving some of my present knowledge for future reference by anybody who may be interested in it.
I've a long history of programming languages including Basic, Pascal, C/C++, Matlab and, more recently, Python.
I'm currently a scientist-in-training and I've found Matlab to be a very fine and powerful tool. Unfortunately the price is quite prohibitive for occasionally used software and for random playing. I believe Python is a very good replacement of Matlab (at least for most of my purposes) and recommend it to anyone who might appreciate simple syntax, fast development and high portability. In connection with some add-on modules like SciPy I believe it can compete with Matlab on even grounds.
As a sideline I also intend to use this blog to learn some web-creating skills.
That'll be all for now.
R.L.