Learn to read music with Racket Scheme

Further to my previous post about using MIT Scratch to help you practice music, I thought I'd give Racket a go to see how easy it would be to get something working.

Scratch is brilliant for prototyping audio/visual ideas, but the limitations of a drag and drop language quickly become apparent once you move beyond basic programs.

I've started learning Racket using the books Realm of Racket and the Little Schemer. Racket is appealing to me because it has the audio/visual stuff built in, and because it's different from the current set of popular C-inspired programming languages.

Here's how the program looks when run...

And here's the code:
#lang racket

Show notes, then play them, see if you get them right!
Press any key if you find the note easy, you'll then
get less time to play this note next time.

Challenge: How to avoid practicing mistakes or quick
corrections, wait for the player to remember first?

- Fix display of extenders that should be hidden by
  stave lines
- Blue colouring for easy notes only considers default set
- Sort easy-notes for better display, or show them on
  the stave?

(require srfi/1)
(require 2htdp/universe 2htdp/image)
(require 2htdp/image)
(require rsound)
(require rsound/piano-tones)

;; What notes do we want to practice?
(define NOTES
  '(e2 f2 g2 a3 b3 c3 d3 e3 f3 g3 a4 b4 c4 d4 e4 f4 g4)) 
;; We need MIDI numbers to play them, these are the standard set
  '(52 53 55 57 59 60 62 64 65 67 69 71 72 74 76 77 79)) 
;; Guitar midi notes are one octave lower
(define MIDI-NOTES
  (map (λ (x) (- x 12)) PIANO-MIDI-NOTES))

;; We want to show the open string notes differently
  '(e2 a3 d3 g3 b4 e4))
;; The initial set of easy notes for *me* to play - change this
;; to suit your needs
(define EASY-NOTES
  '(c4 d4 f4 g4))

;; The canvas
(define WIDTH 400)
(define HEIGHT 300)
(define G-CLEF (bitmap "GClef.png"))

;; How many seconds between notes? Change this to suit your needs
(define TICK-RATE 3)

(define PIX-PER-NOTE 11)

;; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

(define-syntax-rule (times-repeat n fn)
  (for/list ([i (in-range n)])

(define (random-choice list)
  (list-ref list (random (length list))))

;; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

(define (note-index a-note)
  (list-index (curry equal? a-note) NOTES))

(define above/align-left
  ((curry above/align) "left"))

(define (stave)
  (apply above/align-left 
         (cons (line 300 0 "black")
               (times-repeat 4
                              (line 0 20 "black")
                              (line 300 0 "black")

(define (note-pos-relative-b4 a-note)
  ;; b4 is the middle of the stave
  ;; b4 = 0, a4 = -1, c4 = 1, etc
  (- (note-index a-note) PIX-PER-NOTE))

(define (note-y-pos a-note)
  (* PIX-PER-NOTE (note-pos-relative-b4 a-note)))

(define (extender-line)
  (line 30 0 "black"))

(define (extenders a-note-pos)
  ;; Draw extenders from b4 up or down to note
  ;; the first few will be obscured by the 5 stave lines

  ;; Use absolute value of note pos:
  (if (< a-note-pos 0) (extenders (- 0 a-note-pos))
        [(= a-note-pos 0) (extender-line)]
        ;; No lines at odd note positions
        [(odd? a-note-pos)
         (extenders (sub1 a-note-pos))]
          "left" "top"
          (extenders (sub1 a-note-pos)))])))

(define (extenders-above a-note)
  ;; Are the extenders above the stave (or below)?
  (>= (note-pos-relative-b4 a-note) 0))

(define (note-img a-note)
  (circle 10
          (if (member a-note OPEN-STRINGS) "outline" "solid")
          (if (member a-note EASY-NOTES) "blue" "black")))

(define (show-note a-note)
  ;; Show the note on the stave with extenders and the G-Clef
   (scale 0.53 G-CLEF)
   120 -6
    (extenders (note-pos-relative-b4 a-note))
    (/ WIDTH 2) (/ HEIGHT 2) "middle"
    (if (extenders-above a-note) "bottom" "top")
     (note-img a-note)
     0 (note-y-pos a-note)
      (stave) (empty-scene WIDTH HEIGHT "white"))))))

(define (play-note a-note)
  (play (piano-tone 
         (list-ref MIDI-NOTES (note-index a-note)))))

(define (play-and-show-note a-note)
  (play-note a-note)
  (show-note a-note))

;; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;; big-bang world

(struct world (note plays easy-notes) #:transparent)

(define (next-random-note last-note)
  ;; Next random note, but not last-note
  (define note (random-choice NOTES))
  (if (eq? note last-note)
      (next-random-note last-note)

(define (play-note-times a-note easy-notes)
  (if (member a-note easy-notes) 2 4))

(define (next-note w)
  ;; Play the next note, but first check if we've finished
  ;; playing this note. If we have, pick a new one.
    [(zero? (world-plays w))
     (let* ((note (next-random-note (world-note w)))
           (plays (play-note-times note (world-easy-notes w))))
       (next-note (world note plays (world-easy-notes w))))]
     (play-note (world-note w))
     (world (world-note w) (sub1 (world-plays w)) (world-easy-notes w))]))

(define (easy-note w a-key)
  ;; The user finds the current note easy - stop playing it
  ;; and add it to the set
  (let ((note (world-note w))
        (easy-notes (world-easy-notes w)))
    (world note 0
           (if (member note easy-notes)
               (cons note easy-notes)))))

(define (render-scene w)
   (above/align "left"
    (text (string-append "Easy notes: "
                         (string-join (map symbol->string (world-easy-notes w)) ", "))
          15 "black")
    (text "Press any key to add current note" 15 "black"))
   5 5 "left" "top"
   (show-note (world-note w))))

(define (go)
  (big-bang (world (random-choice NOTES) 0 EASY-NOTES)
            (on-tick next-note TICK-RATE)
            (on-key easy-note)
            (to-draw render-scene)))


You'll also need the G-Clef image:

The code is also available on GitHub:

Using autotests to explore and improve code

There's a lot of code out there on the internet, however often it's hard to understand what it does and how to change it to meet your needs. I've found writing autotests a great way to delve into others' code and really understand it, including its bugs and wrinkles. Here's how I took this approach with a new programming challenge in Racket...


I want to write a version of the Eliza psychotherapist program to explore how intelligent computers can appear, or how much work it is to make them appear intelligent. I have in mind testing it out on the local kids' computer club... how many of the 8-10 year olds would be fooled?

I've had a play with the Emacs Doctor mode (run `ESC-x doctor` in emacs to see this) and it's certainly fun. I'm also exploring the Racket programming language (a dialect of Scheme) right now so if I can get something running in that then I can extend it and also further my knowledge in this language.

So after some Googling I find this:
...it's written for Guile (GNU's version of Scheme).

My first task is to get it running in Racket, it's not that hard, mostly quote escaping and a few differences when making hashes and sorting lists. You can see the code changes here:

So now it runs, cool :)

But it seems to crash a fair bit on certain inputs, and now I realise I have to figure out what the code is actually doing. Hmmm... I can see that `eliza.rkt` defines the patterns to match and `bot.rkt` does the actual work, but I'll need to dig much deeper to get the program working properly, and be able to extend it.

I start by listing the bugs I find, but this doesn't help much with my understand of the code...

Introducing autotests

Racket has a nice autotest framework `rackunit` so I give that a go. I pick out a few simple looking procedures from `bot.rkt` and write tests to see if I can prove what they do with different inputs. Here's my first version of `bot-tests.rkt`:

#lang racket

(require rackunit "bot.rkt")

(define-keyword (xnone)
   (A sentence for xnone)))

(define-keyword (sorry)
   (Please don\'t apologise.)))

 "pre-process-msg tests"
 (check-equal? (pre-process-msg "hello")
 (check-equal? (pre-process-msg "HeLlo")
 (check-equal? (pre-process-msg "apples AND oranges")
               '(apples and oranges))
 (check-equal? (pre-process-msg "maybe")

 "respond-to tests"
 (check-equal? (respond-to "apple and banana")
               "A sentence for xnone"))
 (check-equal? (respond-to "SORRY")
               "Please don\'t apologise."))

So I now know what `pre-process-msg` and `respond-to` do and I have a working set of tests. Now I can introduce failing tests to give me some debugging strategies...

Writing failing autotests

Here's my first failing tests, a keyword with a comma in it:

(define-keyword (you)
  ((* you *)
   (Oh, I (% 2) ?)))

(check-equal? (respond-to "you like noise")
               "Oh, I like noise ?")

So now I have a quick way of breaking the program, with a single click, rather than having to interact with the program by typing input. The other big advantage of these autotests is that they are way simpler than the full `eliza.rkt` file, so they are easier to understand. 

The fix to the above program was to escape the comma thus: `(Oh\, I (% 2) ?)`

To be continued...

Next: synonyms, what the (% 2) means in keywords, and how sentences are destructured...

Where I've got to so far

If you want to see how far I've got, take a look at my github repo:

Here's my current to-do list:

Racket on the Raspberry Pi

Racket is a nice programming language to experiment with -- OK there's a bit of adjustment to the lisp syntax, but once you get that you get access to a wealth of built-in libraries for graphics, animation, UIs and more.

The Racket package in the official Raspian repo doesn't work out of the box, but you can download and compile it yourself. It takes an hour or so, but is not hard to do.

I've tried this on both the newer model 2, and the older version of the Raspberry Pi. It works reasonable well on the newer model, but is frustratingly slow on the older one.

Step 1: Download the latest version from the website

Go to http://download.racket-lang.org/ and choose Unix source + built packages.

You can also download with:
wget http://mirror.racket-lang.org/installers/6.2.1/racket-6.2.1-src-builtpkgs.tgz

Step 2: Compile it from source

Check the version numbers in the commands below. At the time of writing 6.1 was available.

tar zxvf racket-6.1-src-builtpkgs.tgz
cd racket-6.1/src

Finally run:

sudo make install

Step 3: Run DrRacket to try it out

Start up DrRacket, you can find it in the bin directory:

cd ~/racket-6.1/bin

Enter the following into the top part of the window (the Definitions) and click Run.

#lang racket
(require pict)

(define (circles n)
  (if (zero? n) empty
      (cons (circle (* n 15)) (circles (sub1 n)))))

(define (squares n)
  (if (zero? n) empty
      (cons (rectangle (* n 15) (* n 15)) (squares (sub1 n)))))

Now run these in the bottom part, the Interactions:

(apply cc-superimpose (squares 8))
(shuffle (append (circles 4) (squares 4)))

DrRacket has great built in help, simply place your cursor a command and hit F1 to read the docs.

Step 4: Check out the games

There are a bunch of games in ~/racket-6.2.1/share/pkgs/realm/ -- from the book Realm of Racket.
The snake game in chapter6 is a good place to start.

Have fun...

Programming Number Squares

I want to produce number squares (10 x 10 grids of numbers up to 100) and highlight different sequences of numbers to help in my maths teaching. For example: what sequence is highlighted in the number square below?

I could make these in a spreadsheet, but why not program it so that I can quickly produce a range of number squares?

I've been playing with the Racket programming language (http://racket-lang.org/) and given that it has graphics primitives built in to the basic language it seems like a good choice.

Here's an example of what it can do with graphics, try each line in turn...
(require 2htdp/image) 
(text "21" 50 "black") 
(rectangle 70 70 "solid" "yellow") 
(overlay (text "21" 50 "black")  (rectangle 70 70 "solid" "yellow")) 
OK, so we have a yellow square with 21 in it. So from these basics we can build some number squares:
(require 2htdp/image)  
(require math/number-theory)
(define NUMBERS-PER-LINE 10)  
(define NUMBER-SIZE 30)
(define BOX-SIZE 60)
(define (number-in-box n pred)
  (overlay (text (number->string n) NUMBER-SIZE "black")
           (if (pred n)
               (rectangle BOX-SIZE BOX-SIZE "solid" "pink")
               (rectangle BOX-SIZE BOX-SIZE "outline" "black"))))
(define (number-block n pred)
  (define numbers (rest (build-list (+ n 1)
                                    (λ (x) (number-in-box x pred)))))
  (define (number-lines l)
    (cond [(= (length l) NUMBERS-PER-LINE)
           (apply beside (take l NUMBERS-PER-LINE))]
            (apply beside (take l NUMBERS-PER-LINE))
            (number-lines (drop l NUMBERS-PER-LINE)))]))
  (number-lines numbers))
The first procedure number-in-box draws one number and colours it (or not) depending on the predicate pred, which is a test for the number. Try these:

(number-in-box 5 odd?)
(number-in-box 5 even?)

The second procedure number-block draws the block of numbers, again with the predicate (which it simply passes on to number-in-box. Try these:
(number-block 100 odd?)  
(number-block 100 prime?) 
(number-block 100 square-number?)

Making times tables with curry

To make tables for times tables you need to add a predicate to test for multiples of a number, e.g.:
(define (multiple-of-3? n)
  (zero? (remainder n 3))) 
Now you can run:
(number-block 100 multiple-of-3?)
However, it's a bit of work to have to define a new procedure each time we want a new times table. What about a predicate like this?

(define (multiple-of? m n)
  (zero? (remainder n m)))

This takes two parameters like this: (multiple-of? 3 9)
in this example, is 9 a multiple of 3?

However, our predicate that we want to pass to number-block expects a single argument: the number in the number square. How can we adapt this procedure to accept a single argument? The answer: with currying.

(curry multiple-of? 3) is a new version of our procedure with the three already passed, so this version is ready to accept the number from the number square. Try these examples:

(number-block 100 (curry multiple-of? 4))
(number-block 100 (curry multiple-of? 5))
(number-block 100 (curry multiple-of? 6))

Scratch style programming in Python? - A simple Pong game

I've been attempting to make Scratch style programming in Python possible -- in response to the question: "What shall I try after Scratch". Kids eventually outgrow Scratch, but the move up to Python can be big, so I'm aiming for something that's mid way between the two.

So I'm building a suite of libraries that:
  • Makes it easy to get visual or audio effects
  • Gives immediate results (like Scratch does), type some commands, press run, see results
  • Runs easily on the Raspberry Pi
  • Has depth with plenty to explore.

I've started with a game of Pong - just a bat and ball, and some bricks to knock out. Here's an example of an early version, with just the bat and ball:

import random
from geekclub.pyscratch import *


ball_img = PhotoImage(file='geekclub/images/face.gif')
bat_img = PhotoImage(file='geekclub/images/bat.gif')

ball = Sprite(ball_img)
ball.speed_x = random.randint(-4,4) * 2
ball.speed_y = random.randint(-4,4) * 2

bat = Sprite(bat_img)

def bat_follows_mouse():
    bat.move_to(mousex(), mousey())

def bounce_ball():
    if ball.touching(bat):
        ball.speed_y = -abs(ball.speed_y)
forever(bat_follows_mouse, 20)
forever(bounce_ball, 20)

There's still a fair bit to simplify, e.g. PhotoImage, create_canvas and mainloop -- but hopefully you can see that the main body of code is easy enough to read.

The full version is on Github https://github.com/ericclack/geekclub together with more examples.

Inspired by MIT Scratch: http://scratch.mit.edu/

Building an MP3 player with the Raspberry Pi

Playing MP3s on the Raspberry Pi

You can play MP3 music on your Pi using the omxplayer command, find an MP3 file, then try something like this:

omxplayer example.mp3

But what about building a music player with real buttons to change tracks? 

Controlling the MP3 player using the Pibrella

You can hook up a Pibrella board so that you have a jukebox with no need for keyboard, mouse or monitor, just press buttons to shuffle and play the next song.

To do this you need to write a short python script to handle button presses from the Pibrella. I've tried using omxplayer to play the music but it doesn't work too well in this way (I ended up with many tracks playing at the same time!), so we need to install the more server friendly mpd and mpc instead:

First install the pibrella libraries, then mpd and mpc:
sudo apt-get install python-pip
sudo pip install pibrella
sudo apt-get install mpd mpc

Once installed edit mpd's config to tell it where your music is. Run this from the Terminal sudo nano /etc/mpd.conf then find the line with the setting for music_directory and set it to wherever you've stored your MP3s.

Once you've done this you need to restart the mpd service:
sudo service mpd restart

You can now test out mpc with the following commands:
mpc update
mpc ls
mpc ls | mpc add
mpc play
mpc next
mpc clear

Now try this program, don't forget to run as root otherwise Pibrella won't work:
   1 import os
   2 import pibrella
   4 # Show we're loaded
   5 pibrella.lights.pulse(0.2)
   7 def mpc(command):
   8     os.system('mpc %s' % command)
  10 def mpc_add_all_to_playlist():
  11     os.system('mpc ls | mpc add')
  13 def set_up_playlist():
  14     mpc('update')
  15     mpc('clear')
  16     mpc_add_all_to_playlist()
  17     mpc('random on')
  18     mpc('repeat on')
  20 def play_next_mp3(pin):
  21     pibrella.lights.fade(100, 0, 0.3)
  22     mpc('play')
  23     mpc('next')
  25 set_up_playlist()
  26 pibrella.button.released(play_next_mp3)
  27 pibrella.pause()

Make it work with no monitor or keyboard

You can get the Pi to start working as a jukebox each time you boot up, then you don't need the keyboard or mouse attached.

Edit root's crontab -- its list of tasks to run periodically:
sudo crontab -e

Add this line at the end -- this tells it to start up the jukebox each time the Pi boots.
@reboot /home/pi/path/to/your/script

More buttons

As you can see in the photo above, once I got the red button working I then added more buttons to add Pause, Random On/Off and Shutdown. You can see the code for this here: https://github.com/ericclack/geekclub/tree/master/pijukebox