[Tutor] Musical note on python

eryksun eryksun at gmail.com
Wed Sep 12 10:18:10 CEST 2012

```On Wed, Sep 12, 2012 at 1:17 AM, D.V.N.Sarma డి.వి.ఎన్.శర్మ
<dvnsarma at gmail.com> wrote:
> How to produce  a musical note of given frequency,volume and duration in
> Python.

Do you just want a sinusoidal, pure tone, or do you need a more
complex waveform?

For a pure tone you can use math.sin(2 * math.pi * f * dt * n). The
sin is a function of radians. 2*math.pi is the constant number of
radians/cycle, f is the frequency in cycles/second (Hz), dt is your
sampling step size in seconds/sample, and n is the index in samples.
Multiply all those units together and you get an argument in radians.
The sin function just samples the y value of the (x,y) coordinate as
you step around a unit circle in the given radians/step. You can scale
the amplitude and iterate for however many steps you want.

The discrete frequency is f*dt in cycles/sample, and the discrete
period is 1/(f*dt) in samples/cycle. You need this to be at least 2
samples/cycle to uniquely define a sinusoid. In other words, 1/dt >=
2*f.  The term 1/dt is the sampling rate fs. Two times the highest
frequency of interest (2*f) is called the Nyquist rate. Sampling a a
sinusoid at a sub-Nyquist rate will alias to a different frequency
(the spectrum of a sampled signal goes from 0 to fs/2; higher
frequencies fold back around). Search the web for animations that show
rate is 48000 Hz, you can create tones up to 24000 Hz, which is beyond
the spectrum of human hearing (20Hz - 20kHz).

The example below creates a generator for 1 cycle of a tone at a
sample rate of 48 ksps. Next it packs the floating point values as
bytes using struct.pack(). The resulting byte string is written to a
PyAudio stream. PyAudio is a C extension wrapper around the
cross-platform PortAudio library:

http://people.csail.mit.edu/hubert/pyaudio

import math
import struct
import pyaudio

def play_tone(frequency, amplitude, duration, fs, stream):
N = int(fs / frequency)
T = int(frequency * duration)  # repeat for T cycles
dt = 1.0 / fs
# 1 cycle
tone = (amplitude * math.sin(2 * math.pi * frequency * n * dt)
for n in xrange(N))
# todo: get the format from the stream; this assumes Float32
data = ''.join(struct.pack('f', samp) for samp in tone)
for n in xrange(T):
stream.write(data)

fs = 48000
p = pyaudio.PyAudio()
stream = p.open(
format=pyaudio.paFloat32,
channels=1,
rate=fs,
output=True)

# play the C major scale
scale = [130.8, 146.8, 164.8, 174.6, 195.0, 220.0, 246.9, 261.6]
for tone in scale:
play_tone(tone, 0.5, 0.75, fs, stream)

# up an octave
for tone in scale[1:]:
play_tone(2*tone, 0.5, 0.75, fs, stream)

stream.close()
p.terminate()
```