Experiments with Procedural Music Generation

Demo
Source: MIT License
POP File (v1.0)
Uses FMSynth from @jpfli

I’ve been experimenting with a procedural music generator using a basic formula for creating a more relaxing piano music. Thought I’d post the process the generator uses to create each note.

First off on the screen there’s a 4x4 grid of boxes (green box is the current parameters and the smaller red box is the cursor). The rows of the grid represent the chaos level of the tune (Methodical, Semi-Methodical, Semi-Chaotic, and Semi-Chaotic) while the columns represent the pacing (Slowest, Slow, Fast, and Fastest). To the right of that you can see the parameters the generator is using. This shows the tempo, whether the chords are played on the Bass Clef or the Treble Clef, the percent chances of each type of note (1=whole, 2=half, 4=quarter, and 8=eigth) and the percent chance the generator will take a single step up/down from the previous note, skip and take two steps up/down, or leap between three and six notes up/down. The main part of the screen shows the current measure as it’s being played out.

Before I explain how the generator works it would be good to share one of the primary tutorial videos on basic music composition theory that I used to tell the generator how to decide what note/duration to play next.

The tune is pretty simple in that it plays a chord (3 notes either on the bass clef, or the treble clef) along with a melody (opposite clef as the chord). To start I define the notes available for each clef (so that the generator knows which note is a +/-1 from the previous note):

Treble Clef Notes (12 total)

image

Bass Clef Notes (12 total)

image

Chords

image

So the way the generator works is it starts by generating the melody for the measure based on the last note of the previous measure and either stepping (move one note up/down), skipping (move two notes up/down), or leaping (move three to six notes up/down) and a note duration. Since the fastest note is an eighth note that means there’s 8 slots per measure so if it decides to generate a whole note (uses 8 slots) but there aren’t enough available slots remaining then it drops down to a half note and so on filling in all remaining slots in the measure. Once the melody is generated it looks at the first note and picks a random chord that has at least one matching note on the opposite clef and the chords are always whole notes.

Faster pacing increases the tempo as well as the percent chance of playing shorter notes while an increase in tone increases the percent chance of doing more skips/leaps and fewer steps (creating a more chaotic melody that jumps around a lot).

Here’s an example of 64 measures (~2min):

7 Likes

Always interesting to see new procedural music experiments. And thanks for the video link, the channel seems to have lots of useful tutorials.

It could be a good idea to limit the number of notes per octave you use, e.g. a pentatonic scale or notes from a chord or maybe randomly select 3-5 notes per octave. Just something I’ve noticed in my own experiments. The generated melody can sound better if you don’t use all 7 white keys.

That way, when you step up and down the selected notes, you automatically have some longer skips and leaps that are always between the same notes. Maybe that is what makes it sound more pleasing?

2 Likes

Very cool, thanks for sharing :slight_smile: You’re pretty great at the theory. I also tried proc music, but not knowing as much as you do I was basically just trying to program the popular patterns and what humans do when they compose music, e.g. in pop there are verses, choruses and bridges, they use similar chords but small probabilistic changes occur, sometimes I throw in a transposition etc. The code is here, a video with some results is here.

2 Likes

One thing I haven’t figured out yet is when using FMSynth + SimpleTuneFM there’s no issues, however if instead call auto source = &Audio::play<0>(patch, note, 127); instead of using SimpleTuneFM I get an Attempt to write to flash error in the emulator though it doesn’t seem to have any issues on HW or SIM. Doing a debug on the emulator shows it happening as soon as I call Audio::play and if I comment out just that part then it works fine (though without any audio obviously).

Perhaps @jpfli or @FManga might be able to help show me the error of my ways :laughing:.

Try this instead:

Audio::FMSynthSource& source = Audio::play<0>(patch, note, 127);

Did that solve the issue?

I should clarify that inside the ProceduralMusic namespace is extern Audio::FMSynthSource *sources[4];

The exact play routine is sources[0] = &Audio::play<0>(piano, measure.melody[currentNote], 127); although

Though I’m not currently using the sources (but I will be). Still even changing them to not capture the source, or to capture the source in a temporary reference variable doesn’t change anything. I still get the weird write to flash error on the EMU.

Should it happen with the source code from your github repo? I tried it but I get no errors in the emulator.

Weird, cause the source from the git repo is giving me the write to flash error on the emulator. I’m running the latest copy of FemtoIDE from its git repo as opposed to the latest release build. Wonder if there’s a difference there.

The emulator (and all other binaries) isn’t in the repo, though. :thinking:

Ah, so it is. Downloading the latest binary release of the emulator fixed it so I’m not getting the write to flash error anymore.