← Back to list

Hello ChiPy!

This is the first in a series of blog posts tracking my experience in the ChiPy mentorship program.

ecxr · 2019-09-23 01:44 · 0 claps · 5.0 min read
#programming #python #music-theory
Open on Medium ↗
Wiki topics: 💻 · Programming 🎵 · Music & Audio

Hello ChiPy!

This is the first in a series of blog posts tracking my experience in the ChiPy mentorship program.

What?

ChiPy is the Chicago Python user group. You can tell it’s a great community because it contains enough excellent human beings interested in helping other people learn to have a full-on mentorship program, with cohorts and workshops and structure.

I am a ChiPy mentee primarily because I want to learn to build things in Python. My programming experience has largely been in school, and while I feel reasonably comfortable writing a function that returns y when you give it x, I freeze up a little at the thought of building a full thing, from start to finish. The other thing I want to learn about, vague as it is, is how to write good code. One of the beautiful things about programming is that, relatively speaking, it’s not that hard to know when your code does what you want it to do. But is it clear, and readable, and efficient? It’s hard to develop that skill without bringing in some outside judgment.

Enter Dominic Fitzgerald, my mentor. Dominic has been programming basically forever, has a masters in computer science, and works as a programmer at Bank of America. Also a fun fact: he has a background in jazz, which is helpful because for my project in this program, we are going to have some fun with music theory.

The project

[embed]

On March 21, 2019, Google’s Doodle for the day was a machine learning program that generated four-part chorales in the style of Bach. This was excellent and good and a lot of fun. The problem, of course, that these chorales were wrong. Baroque four-part harmony is a pretty strictly codified form, and the Google pieces would have parallel fifths! Augmented seconds! Voice-crossing! As a freshman music theory student, the doodle would not receive full marks.

(I say this with tongue firmly in cheek. The engine Google has built is actually pretty awesome — see this blog post here — and the doodle does not reflect its full capabilities.)

I am not a freshman music theory student, but I a) have at various points attempted to learn, and b) love telling people they are wrong on the internet. As such, I thought it would be a fun project to put together a four-part harmony checker. I want to get better at Python, so I’m going to do it in Python, and I want to learn some web development, so I’m going to stick a basic web frontend on it where people can upload music notation files and get them back marked up. Simple enough!

Where are we?

So far, I’ve coded up about half the voice-leading rules I want to implement. Using a music theory library (in my case, music21) these are fairly simple exercises in data structure manipulation, and the vast majority of work is looking up what the built-in methods are for getting the information I want out of each data structure (often chord objects or “streams” of notes). These rules are:

  • Every chord in the piece should have four voices (soprano, alto, tenor, and bass)
  • Each part in the piece stays within its range — don’t try to make a bass sing soprano notes!
  • No chord should have doubled leading tones. (Crash course: a leading tone is the seventh note of a major or, in some cases, minor scale; you can’t have two in one chord because they both tend to “lead” up to the tonic note — C in C Major, A in A Minor, etc. — , resulting in parallel octaves. Writing parallel octaves is a cardinal sin in voice leading because it weakens the independence of your four voices; you might as well just have three. I am writing in that rule later.)
  • Every chord should contain its third degree. The third degree contains a lot of information about a chord’s character — whether it’s major (“happy”) or minor (“sad”), so you don’t want to omit it. (I’m cheating a little bit here by using music21 to analyze individual chords for me and tell me what the thirds are, regardless of inversions. Thanks, MIT!)
  • Adjacent voices should be no more than an octave apart. You want your four-part texture to be nice and rich; if your soprano is singing a note too much higher than your alto, then your texture gets thin.

What I’ve learned

  • Abide by the PEP 8 style guide. Some elements of style are pretty easy to pick up just by reading code and tutorials (spacing around operators, for example); some I didn’t know about (use two line breaks between every function definition); some just require discipline (arranging hanging indents). All of them make your code easier to read. If you are lazy, you can install a code formatter like black to fix all your style mistakes. I am trying to learn, so I installed black and then only used it after trying to fix everything myself ;)
  • Use clear function names. My functions were originally named things like “no_doubled_leading_tones” and “parts_in_range”. With comments and after reading the code, it’s not hard to figure out what they’re supposed to do, but it would be noticeably quicker if they were named “are_leading_tones_doubled” and “are_parts_in_range”
  • Be clear, consistent, and efficient about the data you pass around. I was thinking about my functions as mostly intended to return boolean values: does this piece of music adhere to this rule, True/False? I quickly realized, though, that I would need to pass information about which part of the piece was breaking each rule if I wanted the functionality to annotate mistakes, so I decided that most of my functions would return a (bool, list) tuple, where the boolean shows whether the rule was adhered to and the list is a list of elements that break the rule. This is a little bit of a confusing setup, and would have been clearer if I had applied it to every similar function, rather than here and there. Also, it was unnecessary! Every time the bool was True, the list would definitionally be null; rather than return a verbose tuple, I could simply return the list and check for nullity later. (Corollary: function names should also change so as not to imply that they return booleans.)
  • Write more tests. If you spend any time wondering how a piece of code will handle a particular edge case, you should go ahead and write a test for it. I underdid it because writing bad harmony is a clunky test generation process; as a result, I had to spend some time confusing myself about whether a function worked the way I thought it did, when I could have just known. (It did not work the way I thought it did.)

Some truly terrible part-writing

Some truly terrible part-writing

What comes next?

  • First, more of the same: I have five more voice-leading rules slated for implementation, all a bit trickier than the ones I’ve discussed above. Got to catch those parallel octaves and fifths!
  • Second, implement the annotation function. This tool will be a lot more useful when it can return feedback written directly on the score.
  • Third, implement the web interface. I know zero about how web frameworks work, so I’m excited to get into some Django tutorials and build a web page!

The plan is to see how long these next three steps take. In the event there’s time to expand the project a bit, there are additional levels of musical sophistication and functionality that I can consider building in — but one step at a time.


메타데이터
post_id
19a556ee6126
slug
hello-chipy-19a556ee6126
url
https://medium.com/@ecxr/hello-chipy-19a556ee6126
canonical_url
https://medium.com/@ecxr/hello-chipy-19a556ee6126
author_url
https://medium.com/@ecxr
status
ok
fetched_at
2026-07-29 12:21:14