Thanks, Martin. Interesting idea about encoding intervals instead of pitches. Although I did ok in university aural skills courses, I never really mastered the art of hearing a melody as sequence of intervals. I can hear a pair of notes in isolation and name the interval but when it comes to melodies I tend to hear everything in conventional tonality so the 4th from do
to fa
has, to me, a different quality than the 4th from re
to sol
. Which is a long way of explaining why I didn’t give serious consideration to using intervals instead of pitches in Tbon.
Tbon is mostly just another notation language. If there’s anything that can be called innovative about it, I’d say it’s the recognition that musicians naturally think and feel beats and subdivisions of beats rather than crochets and quavers and what have you. Once I thought about it that way it was obvious that the most natural way to enter melodies from a computer keyboard is as groups of pitch symbols separated by whitespace so that
abc d--e |
represents a measure with two beats with the first beat a triplet and the second divided into two parts in dotted rhythm. Compare with, say, Lilypond where you have to write
\tuplet 3/2 {a8 b c} d8. e16 |
to get the same rhythm. (I hasten to add that Lilypond is a magnificent piece of software and infinitely more capable in terms of creating beautifully engraved output.)
Everything else about Tbon is just mechanics to take best advantage of the core insight about beats and subdivisions and support things like multiple parts and temporary polyphony that are required to represent a broad range of real music.
So my current thinking for integrating Tbon into Sonic Pi is to start as simply as possible with a construct like
melody = tbon 'abc de'
where tbon is function that returns a list of pitch/duration pairs like
[[:a3, 0.33], [:b3, 0.33], [:c4, 0.33], [:d4, 0.75], [:e4, 0.25]]
for the example above. Then you could play the melody with
m = melody.transpose #unzip to 2 lists. One with pitches, one with durations
play_pattern_timed m[0], m[1]
or use Ruby operations to permute the pitches and durations as desired.
Cheers,
Mike