Generating Music With SageMath And Sonic Pi - Examples

We will explore some examples to make music using the libraries included in Sage and python data structures

1 Bach's Prelude in C

Here's a translation of the Mathematica code at mathematica.SE, which is quite nice. (Listen to Bach)

bwv846 = [["C", "E", "G", "C5", "E5"],
          ["C", "D", "A", "D5", "F5"],
          ["B3", "D", "G", "D5", "F5"],
          ["C", "E", "G", "C5", "E5"],
          ["C", "E", "A", "E5", "A5"],
          ["C", "D", "Fs", "A", "D5"],
          ["B3", "D", "G", "D5", "G5"],
          ["B3", "C", "E", "G", "C5"],
          ["A3", "C", "E", "G", "C5"],
          ["D3", "A3", "D", "Fs", "C5"],
          ["G3", "B3", "D", "G", "B"],
          ["G3", "Bb3", "E", "G", "Cs5"],
          ["F3", "A3", "D", "A", "D5"],
          ["F3", "Ab3", "D", "F", "B"],
          ["E3", "G3", "C", "G", "C5"],
          ["E3", "F3", "A3", "C", "F"],
          ["D3", "F3", "A3", "C", "F"],
          ["G1", "D3", "G3", "B3", "F"],
          ["C3", "E3", "G3", "C", "E"],
          ["C3", "G3", "Bb3", "C", "E"],
          ["F2", "F3", "A3", "C", "E"],
          ["Fs2", "C3", "A3", "C", "Eb"],
          ["Ab2", "F3", "B3", "C", "D"],
          ["G2", "F3", "G3", "B3", "D"],
          ["G2", "E3", "G3", "C", "E"],
          ["G2", "D3", "G3", "C", "F"],
          ["G2", "D3", "G3", "B3", "F"],
          ["G2", "Eb3", "A3", "C", "Fs"],
          ["G2", "E3", "G3", "C", "G"],
          ["G2", "D3", "G3", "C", "F"],
          ["G2", "D3", "G3", "B3", "F"],
          ["C1", "C2", "G3", "Bb3", "E"]]

finale = ["C1", "C2", "F3", "A3", "C", "F", "C", "A3", "C", "A3",
   "F3", "A3", "F3", "D3", "C1", "B1", "G", "B", "D5", "F5", "D5",
   "B", "D5", "G", "B", "D", "F", "E", "D", ["C1", "C2", "E", "G", "C5"]]

for i in bwv846:
    note = (i + i[-3:])*2
    for j in note:
        send_message("/trigger/play", j)
        sleep(0.25)

for j in finale:
    send_message("/trigger/play", j)
    sleep(0.25)

2 Sound of Cellular Automaton

Using the idea of mathematica code using Cellular Automata, we modify the Cellular Automata's interactive example (Listen to CA: 61 iterations, Rule 90)

from numpy import zeros
from random import randint

def cellular(rule, N, initial='Single-cell'):
    '''Yields a matrix showing the evolution of a Wolfram's cellular automaton

    rule:     determines how a cell's value is updated, depending on its neighbors
    N:        number of iterations
    initial:  starting condition; can be either single-cell or a random binary row
    '''
    M=zeros( (N,2*N+2), dtype=int)
    if initial=='Single-cell':
        M[0,N]=1
    else:
        M[0]=[randint(0,1) for a in range(0,2*N+2)]

    for j in range(1,N):
        for k in range(0,2*N):
            l = 4*M[j-1,k-1] + 2*M[j-1,k] + M[j-1,k+1]
            M[j,k]=rule[ l ]
    return M[:,:-1]

def num2rule(number):
    if not 0 <= number <= 255:
        raise Exception('Invalid rule number')
    binary_digits = number.digits(base=2)
    return binary_digits + [0]*(8-len(binary_digits))

@interact
def _( initial=selector(['Single-cell', 'Random'], label='Starting condition'), N=input_box(label='Number of iterations',default=31),
       rule_number=input_box(label='Rule number',default=90),
       size = slider(1, 11, label= 'Size', step_size=1, default=6 ), auto_update=False):
    rule = num2rule(rule_number)
    M = cellular(rule, N, initial)
    part = M.transpose()*range(1,N+1)*3
    plot_M = matrix_plot(M, cmap='binary')
    plot_M.show(figsize=[size,size])
    for j in part:
        k = [int(3*(N+1)-i) for i in j if i != 0]
        if len(k) != 0:
            send_message("/trigger/play", k)
            sleep(0.1)

3 HMM Emitting Notes

We may even use the notes of pleasant compositions to train Markov Models, which can later keep producing notes probabilistically. E.g. a 3 symbol model looks like: (Listen to HMM)

1
2
3
4
m = hmm.DiscreteHiddenMarkovModel([[1/3,1/2,1/6],[1/7,3/7,3/7],[1/4,1/4,1/2]], [[1,0,0],[0,1,0],[0,0,1]], [0,1,0], ["F", "Fs", "G"])
for j in m.sample(100):
    send_message("/trigger/play", j)
    sleep(0.1)

Generating Music With SageMath And Sonic Pi

In this post, we will see how to generate music with Python/SageMath and Sonic Pi. Sonic Pi itself is quite nice to make music, but python is an attractive option due to large number of math libraries written for it. So, here are the steps:

1 Install PyOSC

Open Sage subshell

sage -sh

Install PyOSC library

pip install PyOSC

2 Run Sonic Pi and sync with an OSC URL

1
2
3
4
5
6
7
use_synth :piano

live_loop :foo do
  use_real_time
  a, b = sync "/osc/trigger/play"
  play_chord [a, b]
end

3 Pass messages to the OSC URL and play notes

import OSC
from time import sleep
from math import pi, cos, sin

addr = ('localhost', 4559)
c = OSC.OSCClient()
c.connect(addr)

def send_message(url, *args):
    msg = OSC.OSCMessage()
    msg.setAddress(url)
    for l in args:
        msg.append(l)
    c.send(msg)

for x in range(1,10):
    for y in range(1,20):
        send_message("/trigger/play", float(x*10), float(y*5)) # float's not required in python, but throws error in Sage without it
        sleep(0.1)

Profiling Python Programs

Last week, when I was looking up for some info on list comprehensions, one of the pages listed a code something like this

# Access every 3rd element in a list
# filename: access.py
def main():
    a = [1]*10
    i = 0
    while i < len(a):
        print a[i]
        i = i + 3

if __name__ == "__main__":
    main()

and I thought for a moment whether the length will be calculated for each iteration, or will it be called only once? I also thought of an answer that since python code is interpreted, it may not do any peephole optimization, and hence len will be called five times. Anyway, wanted to know the command that would quantify every call in the program and found about cProfile module. We simply run the following:

python -mcProfile -sncalls access.py

which will do the profiling and order by the number of calls

1
1
1
1
         8 function calls in 0.000 seconds

   Ordered by: call count

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        5    0.000    0.000    0.000    0.000 {len}
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
        1    0.000    0.000    0.000    0.000 access.py:2(<module>)
        1    0.000    0.000    0.000    0.000 access.py:2(main)

So, will the optimization flag do anything for that?

# filename: call_access.py
import access

if __name__ == "__main__":
    access.main()

and run

python -OO -mcProfile -sncalls call_access.py

but the output doesn't change much!

1
1
1
1
         9 function calls in 0.000 seconds

   Ordered by: call count

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        5    0.000    0.000    0.000    0.000 {len}
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
        1    0.000    0.000    0.000    0.000 access.py:2(<module>)
        1    0.000    0.000    0.000    0.000 access.py:2(main)
        1    0.000    0.000    0.000    0.000 call_access.py:1(<module>)

So, even with the optimization flag, number of calls to len remains the same.

We can verify that the bytecode is executed when we run it by using strace. For instance, when I ran command the second time, the relevent part of the strace output is below

open("access.py", O_RDONLY)             = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=172, ...}) = 0
open("access.pyo", O_RDONLY)            = 4
fstat(4, {st_mode=S_IFREG|0644, st_size=389, ...}) = 0
read(4, "\3\363\r\nC\252}Xc\0\0\0\0\0\0\0\0\2\0\0\0@\0\0\0s#\0\0\0d\0"..., 4096) = 389
fstat(4, {st_mode=S_IFREG|0644, st_size=389, ...}) = 0
read(4, "", 4096)                       = 0
close(4)                                = 0
close(3)

It did not read access.py again, but it read access.pyo instead. As long as the source is not modified, python reads the bytecode to execute instead of the source (the bytecode stores the modification time of its source code)

Anyway, we need not do any of these analyses to know whether the bytecode has any code optimization in it. The python docs straight away answers with the following point

  • A program doesn't run any faster when it is read from a .pyc or .pyo file than when it is read from a .py file; the only thing that's faster about .pyc or .pyo files is the speed with which they are loaded. (docs)

Getting Started With Emacs

Emacs is an editor that lets us to edit quickly, avoid boring repetitive typing, define custom commands and bind them to desired keys etc. Plus many of its key bindings work in bash too, so, it's really worth to have some time familiarizing with this ancient editor. Let's take a look about the various key bindings and modes --

A little cheat sheet

First, some of the common commands. C means ctrl key, and M means alt key:

Key Function
C-e Go to end of line
C-a Go to beginning of line
C-t Swap adjacent characters
M-t Swap adjacent words
C-x t transpose two lines
C-x k kill current buffer
C-<space> Mark beginning of region, move cursor to highlight
M-% replace in region
C-s forward search
C-r reverse search
M-< Go to beginning of buffer
M-> Go to end
C-x h Whole buffer's selected as region
f3 Start recording macro
f4 stop recording macro (press f4 to play that macro)
M-u Convert the word after the cursor to uppercase
M-l Convert to lower case
M-c Capitalize word
M-= Count lines, words, and characters in region

There are thousands of commands, which can be invoked by M-x and entering the command name, in case we don't remember the key binding for the command. C-h can be used to find help.

Org-mode

If anybody has to be instantly impressed by Emacs, this will do it! It's more like a markup language, which can export to several formats like html, LaTeX, rst, plaintext etc., and it's much easier to use than formats like rst. We can even use it to create tables, and apply formulas on them like a spreadsheet. How cool is that, spreadsheet without any of its associated bloat?!

See org-mode's site for its excellent documentation.

Ido-mode

This provides suggestions when entering the command after M-x, which is quite helpful in discovering commands when we vaguely remember the command, or even a new command. And it searches for substrings which needn't be continuous, e.g. pressing M-x areg will highlight align-regexp

Yasnippet

This is a mode where for a given minor-mode and a keyword in that mode, on pressing tab, it expands to the code snippet as stored. E.g. Open a blank C file, type main, then hit TAB, it gets expanded to

#include <stdio.h>

int main(int argc, char *argv[])
{

    return 0;
}

Auto-complete mode

Provides suggestions to complete the words, showing frequently typed words on top.

Jedi with python mode

With python mode, jedi is useful for autocompletion and code navigation.

Restclient-mode

This is indispensable when developing web APIs. Type the method, endpoint, headers, message, and C-c C-c, and a nicely formatted response will be shown in an adjacent window! The requests can be stored in a file, separted by # as a delimiter.

Expand-region

Expands the region based on semantic units E.g. in a word "a-string", when the cursor is at 'a', when C-= is pressed once, 'a' is selected, on pressing again 'a-string' is selected, pressing again selects '"a-string"'. Very useful when we need to copy or delete a block of code.

Tiny

Tiny is not yasnippet, is another one, which can expand a given sentence with required numerical range. E.g. Construct the ascii table! Press M-x tiny-expand after entering m97\n122|%03d %(string x)

097 a
098 b
099 c
100 d
101 e
102 f
103 g
104 h
105 i
106 j
107 k
108 l
109 m
110 n
111 o
112 p
113 q
114 r
115 s
116 t
117 u
118 v
119 w
120 x
121 y
122 z

Rainbow-delimiters-mode

This is useful for languages where brackets are used to identify blocks, like lisp, C etc. This mode marks each block level's parentheses with different colors, so that it becomes easy to figure out any missing parenthesis.

Artist-mode

Want to make some ascii-art? There's a mode for that as well! Enter M-x artist-mode, and then we can easily create rectangle, ellipse, polygons etc. in the text file. If we want to draw with a mouse in the emacs GUI, press shift-<mouse-2> (middle click), which shows the menu of options to draw like rectangle, ellipse, pen, spray-can etc. Very handy if we want to draw simple block diagrams.

Calc-mode

This is a stack-based scientific calculator which performs many of the calculations. It's a mini CAS! To start it, enter M-x calc, and as an example, we can do unit conversions, by typing in the calc window:

1
u c
day
s

will return 86400, which means 86400 seconds are there in a day.

Replace-Regexp

This is a function, which can be used to replace words using regular expressions. E.g. consider a text file consisting of comma delimited values like

12,11
1,33
54,77
99,101

Now, we can use regular expressions to swap the numbers in each row using the following steps:

- Select the region
- M-x replace-regexp
- Replace regexp:\([0-9]+\),\([0-9]+\)
- Replace regexp \([0-9]+\),\([0-9]+\) with:\2,\1

And we get the result

11,12
33,1
77,54
101,99

Align-Regexp

This is another useful function to beautify our text. E.g. if we have the following snippet:

int num = 5;
int squared = num * num;
int cubed = squared * num;

Running M-x align-regexp = aligns with =

int num     = 5;
int squared = num * num;
int cubed   = squared * num;

And there are many more modes and commands, which make editing fun!

For a detailed reference:

or get help within emacs, by pressing C-h m