#!/usr/bin/python """Demonstrate harmonic synthesis in Python.

Python itself is slow enough that if we had to execute multiple Python bytecodes per sample, we'd probably not keep up, even on modern hardware. But with Numerical Python ("Numeric") we can run the inner loops of harmonic synthesis in C and have "NSL" --- no stinking loops! And PyGame is awesome in that (a) it's easy to get stuff up and running quickly and (b) it comes with an interface to Numeric. """ import pygame, time, random, Numeric, pygame, pygame.sndarray sample_rate = 44100 def sine_array_onecycle(hz, peak): "Compute one cycle of an N-Hz sine wave with given peak amplitude." length = sample_rate / float(hz) omega = Numeric.pi * 2 / length xvalues = Numeric.arange(int(length)) * omega return (peak * Numeric.sin(xvalues)).astype(Numeric.Int16) def sine_array(hz, peak, n_samples = sample_rate): """Compute N samples of a sine wave with given frequency and peak amplitude. Defaults to one second.""" return Numeric.resize(sine_array_onecycle(hz, peak), (n_samples,)) def second_harmonic(hz): "Compute a wave with a strong second harmonic." return sine_array(hz, 16384) + sine_array(hz * 2, 16384) def brass(hz): "Compute a sound with some odd harmonics. Doesn't really sound brassy." return sum([sine_array(hz, 4096), sine_array(hz * 3, 4096), sine_array(hz * 5, 4096)]) def play_for(sample_array, ms): "Play given samples, as a sound, for N ms." sound = pygame.sndarray.make_sound(sample_array) sound.play(-1) pygame.time.delay(ms) sound.stop() def main(): "Play some funky sounds, as a demo." pygame.mixer.pre_init(sample_rate, -16, 1) # 44.1kHz, 16-bit signed, mono pygame.init() play_for(sine_array(440, 4096), 500) play_for(second_harmonic(440), 500) play_for(brass(440), 500) if __name__ == '__main__': main()