Mike Bland

Playing the Changes Hack

My programming skills still come into play at Berklee, as I wrote a Python script to download the MP3 files from the Playing the Changes website

- Boston
Tags: Berklee, music, programming, technical

Sadly, my programming skills still appear occasionally relevant to my musical studies at Berklee. Normally I like to keep the digital and analog parts of my life separate, but when you’ve got a nail staring you in the face, the natural reflex is to grab for a hammer.

My Ear Training 4 professor, Scott deOgburn, recommended that I visit the Playing the Changes website to grab some sound files as study aids.1 Each sound file produces the sound for a particular type of chord based on a specific root pitch. The idea Scott gave me is to throw these files into an iTunes playlist and play them back in random shuffle mode, sing a pitch (e.g. C3 or C4) and then try to identify the chord based on how the sung pitch sounds against it. For example, if C sounds like a #11 (Fi in movable do solfège) against a Major 7th chord, then the chord is F#Maj7.

However, while there seventy-two MP3 files on the site, there’s no bundle to download them all at once. Hence ptc_fetch.py:

#! /usr/bin/python
# https://mike-bland.com/2014/03/14/playing-the-changes-hack.html
# Grabs all the MP3 links from playingthechanges.com and downloads them into
# the current directory.
# Author:  Mike Bland (mbland@acm.org)
#          https://mike-bland.com/
# Date:    2014-03-11
# License: Creative Commons Attribution 4.0 International (CC By 4.0)
#          http://creativecommons.org/licenses/by/4.0/deed.en_US
# Written with hints from:
# http://ubuntuforums.org/showthread.php?t=1542894
# http://docs.python-requests.org/en/latest/user/quickstart/
# Requires the requests package:
# $ sudo pip install requests

import contextlib
import os.path
import re
import requests


if __name__ == '__main__':
  with contextlib.closing(requests.get('%s/' % PTC_COM)) as index_page:
    mp3_links = re.findall('downloads/.*\.mp3', index_page.text)
  for i, link in enumerate(mp3_links):
    print 'Fetching %2d of %d: %s' % (i + 1, len(mp3_links), link)
    with contextlib.closing(requests.get('%s/%s' % (PTC_COM, link))) as mp3:
      with open(os.path.basename(link), 'wb') as fd:
        for chunk in mp3.iter_content(1<<20):

To install the requests package and run the script, open a terminal (the Terminal app in OS X, in the /Applications/Utilities folder) and execute these commands:

# If you haven't installed the requests package already:
$ sudo pip install requests

$ mkdir playing-the-changes
$ cd playing-the-changes
$ cat >> ptc_fetch.py  # Copy and paste in the above code, then type Control-D
$ chmod 700 ptc_fetch.py
$ ./ptc_fetch.py

This could obviously be factored into something more generic and robust, and maybe there’s already a tool out there to do exactly this; but when searching for wget- or curl-based solutions, no other tool and no other solution quite as straightforward as rolling my own script seemed to appear. And in this format, maybe some other Playing the Changes student will have an easier time grabbing what he or she needs without having to be that hip to UNIX.

Still, the moral being, the guilty programming pleasures keep on coming, apparently, no matter how much I’m trying to avoid getting pulled back in.


  1. Turns out my Ear Training 1 professor, Paul Del Nero, is the author of the bass version and co-author of the guitar version of the book!