This is one of the 100 recipes of the IPython Cookbook, the definitive guide to high-performance scientific computing and data science in Python.

# 11.7. Creating a sound synthesizer in the notebook¶

1. We import NumPy, matplotlib, and various IPython packages and objects.
In [ ]:
import numpy as np
import matplotlib.pyplot as plt
from IPython.display import Audio, display, clear_output
from IPython.html import widgets
from functools import partial
import matplotlib as mpl
%matplotlib inline

1. We define the sampling rate and the duration of the notes.
In [ ]:
rate = 16000.
duration = .5
t = np.linspace(0., duration, rate * duration)

1. We create a function that generates and plays the sound of a note (sine function) at a given frequency, using NumPy and IPython's Audio class.
In [ ]:
def synth(f):
x = np.sin(f * 2. * np.pi * t)
display(Audio(x, rate=rate, autoplay=True))

1. Here is the fundamental 440 Hz note.
In [ ]:
synth(440)

1. Now, we generate the note frequencies of our piano. The chromatic scale is obtained by a geometric progression with common ratio $2^{1/12}$.
In [ ]:
keys = 'C,C#,D,D#,E,F,F#,G,G#,A,A#,B,C'.split(',')
notes = zip(keys, 440. * 2 ** (np.arange(3, 3 + len(keys)) / 12.))

1. Finally, we create the piano with the notebook widgets. Each note is a button, and all buttons are contained in an horizontal box container. Clicking on one note plays a sound at the corresponding frequency.
In [ ]:
container = widgets.ContainerWidget()
buttons = []
for note, f in notes:
button = widgets.ButtonWidget(description=note)
def on_button_clicked(f, b):
clear_output()
synth(f)
button.on_click(partial(on_button_clicked, f))
button.set_css({'width': '30px',
'height': '60px',
'color': ('black', 'white')['#' in note],
'background': ('white', 'black')['#' in note],
'border': '1px solid black',
'float': 'left'})
buttons.append(button)
container.children = buttons
display(container)
container.remove_class('vbox')