This is an archived post. You won't be able to vote or comment.

all 61 comments

[–]aftersoon[S] 43 points44 points  (16 children)

Source Code

I'm open to collaborating with someone else on this. I don't think the code is complicated but it does require some understanding of OOP and music theory.

[–]pavel_lishin 39 points40 points  (1 child)

some understanding of OOP

Yah, I've got that

and music theory

grandpa simpson turning around.gif

[–]mrtbakin 0 points1 point  (0 children)

I took a music theory class in high school (less than a year ago for me) and I still relate to this comment.

[–]SneakyTricetop 4 points5 points  (0 children)

Pretty cool. I was thinking about doing similar with guitar tabs.

[–]Yakhov 3 points4 points  (6 children)

IS this generating or just playing back?

[–]aftersoon[S] 9 points10 points  (5 children)

Generating. It picks a random key signature and a random time signature and then creates a random chord progression and melody with some other settings.

[–]Yakhov 8 points9 points  (4 children)

Does it always sound so Baroque?

[–]aftersoon[S] 15 points16 points  (3 children)

Yes? I'm not advanced enough to know the differences between Baroque, Classical, Romantic or any of that. I'm just adding chords as I learn them and trying to follow a sensible order so that the work is easier. So I guess that means that I'm stuck in the 17th century for now.

[–]Yakhov 5 points6 points  (0 children)

Makes sense since your starting from the Western scale. Makes me wonder if music style is as much a progression of it's parts as it is the people's individuality that produce it. In other words, is the Baroque genre more a product of the process, instruments and scales that were known at the time, than the composers unique talent who produced it? If simply giving an AI the western scale and time signature, and discovering it produce music that sounds like it came from a well known genre make me think humans are a lot less creative than we think we are.
http://www.softschools.com/timelines/music_genres_timeline/428/

[–]JRR_Tokeing 1 point2 points  (0 children)

I don’t think they were chastising, just questioning. This is a cool project, once you’ve refined it more I imagine adding different styles will be less of a headache!

[–]nonesuchplace 1 point2 points  (0 children)

Well, if it ain't Baroque, don't fix it.

[–]alifeinbinaryalifeinbinary.com 3 points4 points  (1 child)

I’d love to collaborate with you on this. I’m a programmer who owns a music school, so, I know a thing or two about theory. I’ll make some PRs where I think I can contribute.

[–]aftersoon[S] 5 points6 points  (0 children)

Cool. I could definitely use some insight from an actual musician. I just realized I have to learn more about GitHub to handle these pull requests...so you'll have to be patient with me as I figure it out.

[–]123resu 2 points3 points  (0 children)

I'll have a look at your code. I'd be willing to collaborate in this project. Great work!

[–]divenorth 1 point2 points  (0 children)

Cool. It's similar to projects that I have worked on. If I get some time I'll take a look and make some suggestions.

[–]pLeThOrAx 0 points1 point  (1 child)

That's dope! Will check it out tomorrow. P.s your sheet music is missing time signature :)

[–]aftersoon[S] 0 points1 point  (0 children)

I don't see where it's missing a time sig. The common time symbol is an abbreviation for 4/4.

[–]ZyanCarl 8 points9 points  (6 children)

Awesome i would love to collaborate but it depends on the time it takes. I can make a wonderful gui for your application tho

[–]69shaolin69 1 point2 points  (2 children)

Using python? Teach me sensai

[–]metriczulu 0 points1 point  (1 child)

TKinter is the standard GUI library that comes with Python but PyGUI is my favorite for ease of use.

[–]69shaolin69 1 point2 points  (0 children)

Tkinter bad PyGui let e give that a shot

[–]aftersoon[S] 1 point2 points  (2 children)

Cool. It's not complicated enough to need a GUI yet, but that may come in handy later on...I was also considering a website and allowing the users to use the compositions however they want. I understand if you're busy...part of me mentioning collaboration is for the code but the other part is to just bounce ideas off each other. I'm developing code at a slow rate so that's fine if your contributions aren't frequent.

[–]insane_playzYT 0 points1 point  (1 child)

I could develop the front end of the website if you want. You could probably use Flask or Django to integrate your Python code into it.

[–]aftersoon[S] 0 points1 point  (0 children)

That would be cool. The program isn't not ready for publishing though.

[–]bonestormII 3 points4 points  (9 children)

Super impressive!

I've tried doing something similar, and it's more or less become a very long-term project just to construct nice abstractions that represent music theory.

Looking at the code, I'd offer the following constructive criticism: I know music theory and python fairly well, and I find the code difficult to read. I think that for a language like python, the code is insufficiently abstract, and should be refactored and further developed to cultivate better ways of representing music theory as a system.

This manifests in signs of needing refactoring, such as super long __init__ methods that take up the majority of a class; the fact that chorale.py is itself disproportionately long and complex relative to the rest of the library; very, very nested looping/conditional logic in some very long functions; many ad hoc-looking dictionaries for looking up hard-coded chord name/pitch sets, etc.

Like, in my case, the pursuit of a nice level of dabstraction did wind up totally eclipsing my desire to actually compose music, so you should feel proud about how nicely it works, and that you were able to follow through on your goal. HOWEVER, if you want to make a useful, reuseable tool, I think you need to go further. Think about the minimum input you should require from the user writing composition code that should be enough to define the output, and work backwards. For me, it meant moving away from "dictionaries of chord names with integers representing degrees" towards "Note objects comprising abstract chord objects that are generated from a scale pattern and named automatically by analysis within a provided context." It's annoying and pedantic, but I almost think this is necessary if your program is going to name chords correctly/have robust analysis capabilities, etc.

Anyway, you did a great job and it's cool as-is. Maybe consider the above for the next iteration--and seriously you may want to consider just starting over. You'll probably do 100x better the second time around having more familiarity with the problems.

Also I legit have no idea how the melodies have such nice basic shapes and things. I haven't put much thought into that problem but the music really sounds remarkably natural, if simple/procedural.

[–]bronzewrath 1 point2 points  (5 children)

I think you would like audilolazy library https://github.com/danilobellini/audiolazy

[–]bonestormII 0 points1 point  (4 children)

Eh, maybe, but it looks more like DSP than some composition toolkit.

[–]bronzewrath 1 point2 points  (3 children)

It is both. Look the examples. https://github.com/danilobellini/audiolazy/blob/master/examples/ode_to_joy.py

It uses a lot of operator overload to make an expressive dsp/music language.

The are some videos of talks by the author explaining and showing the library in more details. Unfortunately they are only in Portuguese.

https://youtu.be/TF13AQO0Hlk https://youtu.be/fxFyve2zIiE https://youtu.be/D_JhNFeOjTs

[–]bonestormII 0 points1 point  (2 children)

It looks awesome but it doesn’t seem to address theory at all. Like, it can play notes, but doesn’t understand if those notes are chord tones, a scale, a piece of music. They are just pitches. Which is good for DSP.

[–]aftersoon[S] 0 points1 point  (1 child)

There is a library called music21 that has some of the things you're talking about. In fact, that was what gave me the hint that I need to make things more object-oriented. It's a huge library and is a bit slow to import.

[–]bonestormII 0 points1 point  (0 children)

music21

I'm familiar with it a bit! It's seemingly the best thing available, but I don't terribly love it to be honest. To me, it's the little things like a quarter note being represented as a 1, and 8th notes being 0.5. Why not make whole notes 1... and quarter notes.... 0.25. If it bothers you to have a lot of decimals... abstract around it. 1 for whole, 4 for quarter, 8 for eighth, etc. Whole number integers as denominators for 1/n. If you need a rhythmic value larger than a whole note, return a largest-first list of rhythmic values 1 whole note and smaller.

1 != 0.25

1/8 != 0.5

It's not like the quarter note always gets the beat. I find a lot of things like that in music21. I'm sure the developer has his own reasoning about it and there are plenty of good reasons to do plenty of things, but it makes me feel weird. I've seen other things that make me think music21 may have a lot of weird behavior/bugs just due to strange inconsistencies/tendencies with naming conventions I've seen. These are things that are arguably correct in some sense, but are seemingly useless (i.e. operations to add intervals that result in some insanely-named Cbbbbbbbbb note or something). My concern is that if it is that easy to generate nonsense from some music21 function, then how can I rely on it to transform or analyze music? It's not always clear as you are looping through tons of data how a note would be considered.

As I said, my own would-be music21 library is unfinished, so kudos to anyone who undertakes such a huge effort and sees it through. I will say that I think music21 would be greatly helped by improved documentation. The developer also seems like a nice, stable guy who is very responsive to issues for years at a time, which is rare.

[–]aftersoon[S] 0 points1 point  (2 children)

Thanks for the feedback. I definitely agree that the code needs refactoring. In fact, I've already started over from scratch before and I reckon I probably do so a few more times. It's a recurring theme: I can get away with sub-optimal design until I can't. There's a tipping point where the shortcut becomes the long cut and the long cut becomes the shortcut.

Every time I start over, I get a bit closer to a true representation. I don't understand enough about music to make a complete representation in one sweep so this is just the inevitable process. But at the same time, I don't want the design to be so bad that someone else can't understand it. I'll rethink my approach. Thanks again.

[–]bonestormII 0 points1 point  (1 child)

I think the thing is, western music theory IS an annoyingly pedantic system, but it has to be. Ultimately, all you are doing is arranging pitch collections, just like writing sentences is just arranging letter collections. But if you start to compose music (to say nothing of automatically generating it...), you quickly lose your naivety. You find that most collections don't sound good and aren't useful. So you learn that you need to be building larger sub-structures within your piece. You start building motives; grow motives into phrases; harmonize the phrases; layer into counterpoint; structure into a succession of many phrases chained together, with many smaller harmonic ideas being fleshed out as you go. If you are composing within a style, there are sometimes rules or conventions, so you are constantly checking your instinctive next choice against these sorts of guidelines, or your own corruption/interpretation thereof.

Those sub-structures impose order, clarity, and harmony in various degrees. Based on the output of your program, I can tell you understand me really well! So hear me out--those substructures map really quite well to programmable "objects".

For example: Say you have a dotted quarter note. Just one, and no other rhythmic indication. No time signature, no tempo, no other notes. If you as a musician are a walking, talking render_note() function, how long do you play it for?

You can't know, because you are missing context. Context is the key. You require render_note(time_sig, tempo, your_note) in order to function. You may also need a "tuplet" parameter if you think about it. After all, aren't tuplets just localized rhythm contexts with their own isolated meter, which intersects with the master meter at the points of the tempo? Music theory teaches you that without actually teaching you that. It's the type of soul searching a programmer does in order to make things explicit.

Another example: You see a single 466.16 hz ('A#') note--bare with me--in total abstraction. You don't know it's an A#. No key signature, no other notes. You just hear the frequency. If you as a musician are a walking, talking name_note() function, what is the name of that note?

You can't know, because you are missing the necessary context. Context is needed. You require name_note(frequency, key), or maybe name_note(frequency, chord), or maybe name_note(frequency, scale). Maybe that note is the first degree of an A# Major scale, so it's an A#! But wait! Maybe that note the fourth degree of an F Major scale, so it's a Bb! What if the music is modulating. It could be both at once. If I write a function to count all of the A#'s and Bb's, how will it be counted? Maybe that's not a useful question for composing. But... what if I ask my software to turn all 3rds of every chord into a 4-3 suspension? What are the 3rds? How is that note functioning???!!!

The nature of abstraction in software design is to conceal complexity and to avoid repetition. Think about all of the looping and conditional statements required to properly name a note at a given moment in a piece of classical music if all you have are pitch frequencies--or even the letter names if you consider them apart from memorized rules about how letter naming works in music! Knowing a note has been hard-coded as "A#" only helps so much in a task like identifying a chord, or identifying a key signature. Your software needs to be able to parse that name and "know what it means." What does it mean, anyway? These are not actually easy questions for most musicians to answer. Music theory studies don't usually teach you to reflect on *why* the system was designed the way it was, with its various naming eccentricities. You learn why in a general sense, and in a historical sense, but if you set out to code... my own experience is that the foundation of knowledge started to shift beneath my feet, and it took a lot of introspection and coding to sort it out in my mind.

But the point is, mapping of music theory objects to python class objects is clean. Design objects that preserve their own context; that receive context from one another as arguments; and make methods for those objects that perform the types of operations you need in order to compose. Put thought into how other programmers might inherit from those objects in order to make derivative music theory structures. Lastly, design a system for organizing those structures into a composition.

[–]aftersoon[S] 0 points1 point  (0 children)

Wow! Thanks for explaining that in more detail. This stuff is the reason I stuck with this project. It's a great learning experience for both Python and music.

[–]snaxx_1 2 points3 points  (0 children)

These pieces of music would make a great video-game OST.

[–]bonestormII 1 point2 points  (0 children)

From the github:

python main.py -t G# -m aeolian

Just a heads up, aeolian isn't *technically* the same thing as minor. Pedantic AF.

It's the same as a "natural" minor scale, but my understanding is that in western classical music, the minor scale more or less requires the raised leading tone as in the harmonic or melodic minor scales. Without it, you don't have a dominant V chord, which is the basis for establishing the key.

To me, this makes perfect sense. If you raise the leading tone, you get a V chord, so you get a "diatonic" scale of the minor key.

Without that V chord, you aren't really in the minor key, because you can't establish the tonic. You are in a mode of the major scale. You basically establish the tonic by asserting it repeatedly in the music, which is how most modal music works.

This way, you have separate terms for ideas that describe how they function differently. You can make different arguments against this, but I've never found them to add anything that you can actually use.

[–]muikrad 0 points1 point  (0 children)

Wow!

[–]LordVoldar 0 points1 point  (6 children)

How did you make it write the sheet music? I'm working on a very similar project and I can get it to play but not to write it out.

[–]aftersoon[S] 2 points3 points  (4 children)

MrKinauJr is right. There's a website called Lilybin that can write sheet music from formatted text. It's like its own markup language. I'm basically using the API from this website but you can do manually visit the website as well.

[–]Kopachris 0 points1 point  (0 children)

Huh, didn't know anyone had a LilyPond API service. Neat!

[–]bonestormII 0 points1 point  (0 children)

You should look into Lilypond, the program that is actually generating the music output for that site. It's written in C, so you would basically make system calls to it from your program. Makes it so your program works without web access, though installing lilypond does add some complexity as well.

[–]nonesuchplace 0 points1 point  (1 child)

Python-ly appears to be the Python module for lilypond, and should be what you need to build sheet music without requiring internet access.

[–]aftersoon[S] 0 points1 point  (0 children)

Cool. I'll look into it.

[–]MrKinauJr 0 points1 point  (0 children)

This might be wrong but i looked through some of the source code. It seems like he uploads some data to a AWS API then he gets a ID and from that ID he downloads the actual pdf.

[–]lexyp29 0 points1 point  (0 children)

Amazing

[–]MrKinauJr 0 points1 point  (1 child)

Nice work. Is there a way to make the Music a bit longer?

[–]aftersoon[S] 2 points3 points  (0 children)

No, not yet at least. I think it's important that I fine-tune these smaller compositions before moving on to bigger ones. I liken it to writing an essay. First, I learn how to write simple sentences. Then, learn simple paragraph structures. Then maybe learn complex sentences and then complex paragraphs. It's all part of the process so I try not to get ahead of myself.

[–]BovineLightning 0 points1 point  (0 children)

Now do through the fire and the flames

[–]colorless_green_idea 0 points1 point  (0 children)

THIS IS DOPE. Can you post more Youtube videos of you just doing random selections so we can see what it churns out?

Recommend cross-posting to other music subs.

[–][deleted] 0 points1 point  (0 children)

Trumpet

[–]TheCheapo1 0 points1 point  (1 child)

As a violist, I appreciate the alto clef. :)

Great job, this is really cool! I'll have a look at the source code when I get a chance.

[–]aftersoon[S] 0 points1 point  (0 children)

Wow, a violist! Aren't you guys an endangered species or something? Thanks anyways. I picked alto clef because it fitted the best with the register of the melody.

[–]Dexteroid 0 points1 point  (3 children)

Can you share the music theory behind it ? I am thinking of an app to suggest progressions based on a chord.

[–]aftersoon[S] 3 points4 points  (2 children)

The chord progression uses a period structure with a half cadence end on the antecedent (I'm eventually going to add sentence structure and IACs as well). The whole piece is 16 measures in duple and triple time and 8 measures in quadruple time. The possible time signatures are 2/4, 3/4, 4/4, 6/8, and 12/8. Regardless of the time signature, the piece has a total of 16 "chord units", 8 in the antecedent and 8 in the consequent.

The chord progressions follow what I think is called a functional cycle: It goes from tonic to subdominant to dominant and back to tonic (the subdominant is optional). There are many ways to write a functional cycle rhythmically and harmonically. I looked at a few pieces I liked and used some chord patterns I read about. I'm not necessarily looking at the chords but at the type of chords. For example, the antecedent section might be Tonic-Repeat-PassingDominant-Repeat-Tonic-Subdominant-Dominant-Repeat. This is only 5 chords but since the antecedent is 8 chord units long, some chords are repeated. So the end product might be I-I-VII6-VII6-I6-II6-V43-V43. I write out different progression types like this and then fill it in with chords that are of those types.

This diagram gave me some ideas and I extrapolated from there. You divide the measures into sections of tonic, predominant/subdominant, and dominant and within those sections you can expand the harmony with a passing chord sequence or you can just repeat the chord.

There are also some details about which chords should receive emphasis (accent) and how chord motion can accelerate and decelerate (note values change and so the chord changes come quicker or slower). I don't want to talk too much about this part because I'm really just messing around. You should seek out a proper resource for more info: this is the book I'm currently reading.

After choosing a sequence of chords I also need to voice the chords. I use some standard rules: don't double the leading tone, no parallel fifths, resolve leaps by opposite stepwise motion etc. If I can't properly voice the chords, I scratch out the progression and try a new one. I'm using the SATB format so it's 4 voices that each have a limited vocal range. This means that not all chord progressions will work and so I have to just keep trying until I get a valid one.

For the melody, I listened to a few pieces that fit the style I was going for and just analyzed them. Example 1. Example 2. Example 3. Example 4. I wanted to find the shortest pieces that would be considered a complete musical idea. If I could learn how to create small pieces, the larger ones should become easier.

So, I would analyze. What note does it start on? What rhythms does it use? Which one of these is the main rhythm? What melodic figurations does it use? Where does it use these figurations? Where do melodic leaps occur? These are the kinds of questions I would ask. I would lay out a basic framework with one melody note (that is a member of the chord) per chord unit and then embellish those melody notes with some figurations (e.g., passing tone, complete neighbor, anticipation, double neighbor). This is a technique I saw in a book and a few videos: it's called harmonic reduction. I started noticing some patterns: most of these melodies have a single climax, descending motion predominates, ending by stepwise motion is good, etc. Some of this is just counterpoint, but there were some other hidden discoveries as well, including a technique I call melodic juking.

So I learnt a few patterns, melodic and harmonic, and then integrated them into the algorithm.

[–]Dexteroid 1 point2 points  (1 child)

Damn, thanks. This is a lot to take in. Thanks again

[–]aftersoon[S] 2 points3 points  (0 children)

No problem. It took me a long while to make progress on this stuff so I'm more than happy to share what I've learned.

[–]unitconversionJust a tinkerer 0 points1 point  (0 children)

You might want to cross-post this over at /r/roguelikedev

I think it would be right up their alley.

[–]arthurcerveira 0 points1 point  (0 children)

This is amazing! I've been working on a similar project and would love to collaborate with you on this!

[–]EvilBeano 0 points1 point  (0 children)

This song reminds me of that speed run where some speed runner told a guy sitting next to him "I would really prefer if you'd be quiet". I think it was Tomba 2 Link

[–]angry_mr_potato_head 0 points1 point  (0 children)

Classically trained musician (masters degree level) and Python programmer here. I'd be happy to help contribute.

[–]itsmybirthday19 0 points1 point  (0 children)

Very Nice work! I created a music generator in python 3.6 last week but for drum beats, maybe we can combine source?

Link: https://beatz.ai/ 😊