{ "metadata": { "gist_id": "5166891", "name": "Music" }, "nbformat": 3, "nbformat_minor": 0, "worksheets": [ { "cells": [ { "cell_type": "heading", "level": 1, "metadata": {}, "source": [ "Playing Music on the Phoenix" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "For this notebook, you will need a [Phoenix](http://www.iuac.res.in/~elab/phoenix/) box with a speaker\n", "connected to PWG / GND,\n", "and optionally an LED at Digital Output D3 / GND for some pointless flashing lights." ] }, { "cell_type": "code", "collapsed": false, "input": [ "import os, sys, time" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "code", "collapsed": false, "input": [ "from IPython.display import display, Javascript, HTML" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "code", "collapsed": false, "input": [ "import phm\n", "p = phm.phm()" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Notes table from [here](http://www.phy.mtu.edu/~suits/notefreqs.html)" ] }, { "cell_type": "code", "collapsed": false, "input": [ "import pandas\n", "notes_csv = pandas.read_csv('notes.csv', sep='\\t')\n", "notes = {}\n", "for note, freq in zip(notes_csv.name, notes_csv.frequency):\n", " notes[note] = freq" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "code", "collapsed": false, "input": [ "notes_csv[:4]" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A global variable setting the tempo in bpm" ] }, { "cell_type": "code", "collapsed": false, "input": [ "tempo = 160" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Some utility functions for playing notes / rests on the Phoenix" ] }, { "cell_type": "code", "collapsed": false, "input": [ "shutup = lambda : p.set_frequency(0)\n", "\n", "def play_note(name, beats=1):\n", " display(Javascript(\"$('div#%s').addClass('playing');\" % name))\n", " play_frequency(notes[name], beats)\n", " display(Javascript(\"$('div#%s').removeClass('playing');\" % name))\n", "\n", "def play_frequency(freq, beats=1):\n", " mask = int(freq) % 15\n", " p.write_outputs(mask)\n", " p.set_frequency(freq)\n", " time.sleep(beats * 60./tempo)\n", " p.set_frequency(0)\n", " p.write_outputs(0b0000)\n", "\n", "def rest(beats=1):\n", " time.sleep(beats * 60. / tempo)" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "code", "collapsed": false, "input": [ "def shutup_handler(self, etype, value, tb, tb_offset=None):\n", " shutup()\n", " print 'KeyboardInterrupt'\n", "\n", "get_ipython().set_custom_exc((KeyboardInterrupt,), shutup_handler)" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now we can play simple notes by name, or specific frequencies" ] }, { "cell_type": "code", "collapsed": false, "input": [ "play_note('C4')" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "code", "collapsed": false, "input": [ "play_frequency(400)" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Here is a class for representing a note.\n", "This is just a simple wrapper for a name and a frequency,\n", "which will play its note when you call it." ] }, { "cell_type": "code", "collapsed": false, "input": [ "class Note(object):\n", " name = None\n", " frequency = 0\n", " def __init__(self, name, freq):\n", " self.name = name\n", " self.frequency = freq\n", " \n", " def __hash__(self):\n", " return self.freq\n", " \n", " def __call__(self, beats=1):\n", " play_frequency(self.frequency, beats)" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now assign all of these notes to variables,\n", "so that our global namespace is filled with callable notes" ] }, { "cell_type": "code", "collapsed": false, "input": [ "g = globals()\n", "for name, freq in notes.items():\n", " g[name] = Note(name, freq)\n" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "So we can just call note names to play notes" ] }, { "cell_type": "code", "collapsed": false, "input": [ "C4()\n", "C4()\n", "D4()\n", "E4()\n", "E4()" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now a function that parses a string into a sequence of notes and their durations." ] }, { "cell_type": "code", "collapsed": false, "input": [ "def play_tune(tune_s, octave=4):\n", " notelist = tune_s.split()\n", " for note in notelist:\n", " if ':' in note:\n", " note, beats = note.split(':')\n", " beats = float(beats)\n", " else:\n", " beats = 1\n", " if note == 'r':\n", " rest(beats)\n", " continue\n", " if note[-1] == '-':\n", " note = note[:-1] + str(octave-1)\n", " elif note[-1] == '+':\n", " note = note[:-1] + str(octave+1)\n", " elif not note[-1].isdigit():\n", " note = note + str(octave)\n", " play_note(note, beats)" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "With this, we can write a simple version of Ode to Joy\n", "in a few lines of Python." ] }, { "cell_type": "code", "collapsed": false, "input": [ "tempo = 160\n", "bar1 = 'E E F G G F E D C C D E E:1.5 D:.5 D:2'\n", "bar2 = 'E E F G G F E D C C D E D:1.5 C:.5 C:2'\n", "bar3 = 'D D E C D E:.5 F:.5 E C D E:.5 F:.5 E D C D G-:2'\n", "\n", "play_tune(bar1)\n", "\n", "play_tune(bar2)\n", "\n", "play_tune(bar3)" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "heading", "level": 2, "metadata": {}, "source": [ "Keyboard in the Notebook" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now that we have written simple *Python* to play some music,\n", "we can actually hack a little HTML and Javascript to build an actual playable keyboard in the notebook.\n", "\n", "The notes are played on the phoenix by the same mechanism as before,\n", "just by registering javascript callbacks to the Python kernel when you click on the HTML elements." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Build the keyboard in HTML with divs and ids" ] }, { "cell_type": "code", "collapsed": false, "input": [ "key_t = \"
{0}
\"\n", "black_key_t = \"
\"\n", "\n", "names = []\n", "for o in range(8):\n", " names.extend([ '%s%i' % (k,o) for k in 'CDEFGAB'])\n", "\n", "keys = []\n", "for name in names:\n", " keys.append(key_t.format(name))\n", " if name.startswith(tuple('ACDFG')):\n", " sharpname = name[0] + 's' + name[1:]\n", " keys.append(black_key_t.format(sharpname, sharpname[:2]))\n" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "code", "collapsed": false, "input": [ "print '\\n'.join(keys[:10])" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Some CSS for the keyboard (edit this cell to see it)\n", "\n", "" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "And let's publish that HTML to the notebook, to see how we've done." ] }, { "cell_type": "code", "collapsed": false, "input": [ "display(HTML(\"\"\"
\n", "
\n", "%s\n", "
\n", "
\"\"\" % '\\n'.join(keys)))" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Finally, register mouse events on the keys that will make the appropriate Python calls when you click on each key." ] }, { "cell_type": "code", "collapsed": false, "input": [ "%%javascript\n", "\n", "var myState = {};\n", "myState.mousedown = false;\n", "$(document).on(\"mousedown\", function () { myState.mousedown = true; }\n", ").on(\"mouseup\", function () { myState.mousedown = false; });\n", "\n", "\n", "var mousedown = function(e) {\n", " $(this).addClass(\"playing\");\n", " IPython.notebook.kernel.execute(\"p.set_frequency(notes['\" + $(this)[0].id + \"'])\");\n", "}\n", "var mouseenter = function(e) {\n", " if ( !myState.mousedown ) return;\n", " $(this).addClass(\"playing\");\n", " IPython.notebook.kernel.execute(\"p.set_frequency(notes['\" + $(this)[0].id + \"'])\");\n", "}\n", "var shutup = function(e) {\n", " if ( ! $(this).hasClass(\"playing\") ) return ;\n", " \n", " $(this).removeClass(\"playing\");\n", " IPython.notebook.kernel.execute(\"p.set_frequency(0)\");\n", "\n", "}\n", "\n", "$(\"div.key\").on(\"mousedown\", mousedown\n", ").on(\"mouseenter\", mouseenter\n", ").on(\"selectstart\", false\n", ").on(\"mouseup\", shutup\n", ").on(\"mouseout\", shutup);\n" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "code", "collapsed": false, "input": [ "import numpy as np" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "code", "collapsed": false, "input": [], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "heading", "level": 1, "metadata": {}, "source": [ "More familiar tunes" ] }, { "cell_type": "code", "collapsed": false, "input": [ "tempo = 680\n", "\n", "play_tune('E E r E r C E r G r:4 G- r:4', octave=4)\n", "for i in range(2):\n", " for i in range(2):\n", " play_tune('C r:2 G- r:2 E- r:3 A- r B- r As- A- r')\n", " play_tune('C E r G A r F G r E r C D B- r:4')\n", " \n", " \n", " for i in range(2):\n", " play_tune('G Fs F Ds r E r Gs- A- C r A- C D r:2')\n", " play_tune('G Fs F Ds r E r C+ r C+ C+ r:4')\n", " play_tune('G Fs F Ds r E r Gs- A- C r A- C D r:2 Eb r:2 D r:2 C r:4')\n", " \n", " play_tune('C C r C r C D r E C r A- G- r:4')\n", " play_tune('C C r C r C D r E r:8')\n", " play_tune('C C r C r C D r E C r A- G- r:4')\n", " play_tune('E E r E r C E r G r:4 G-:1.5 r:4')\n", " \n", " play_tune('C r:2 G- r:2 E- r:3 A- r B- r As- A- r')\n", " play_tune('C E r G A r F G r E r C D B- r:4')\n", " for i in range(2):\n", " play_tune('E C r G- r:2 Gs- r A- F r F A- r:4')\n", " play_tune('D A:1.5 A A G F r:0.5 E C r:.5 A- G- r:4')\n", " play_tune('E C r G- r:2 Gs- r A- F r F A- r:4')\n", " play_tune('B- F F F E D C C- r C- D2 r:4')\n" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "code", "collapsed": false, "input": [], "language": "python", "metadata": {}, "outputs": [] } ], "metadata": {} } ] }