Turning up the randomness to 11 for MAXIMUM MIDI MADNESS
midiblender
fun
Mangling with all the polyphony, will it blend?
Author
Matt Crump
Published
February 9, 2024
Code
from diffusers import DiffusionPipelinefrom transformers import set_seedfrom PIL import Imageimport torchimport randomimport sslimport osssl._create_default_https_context = ssl._create_unverified_context#locate library#model_id = "./stable-diffusion-v1-5"model_id ="dreamshaper-xl-turbo"pipeline = DiffusionPipeline.from_pretrained( pretrained_model_name_or_path ="../../../../bigFiles/huggingface/dreamshaper-xl-turbo/")pipeline = pipeline.to("mps")# Recommended if your computer has < 64 GB of RAMpipeline.enable_attention_slicing("max")prompt ="Random MIDI randomness. Explosive torrent of musical notes. Exploding the universe of music. Music everywhere. Neon super colorful sonic cartoon."for s inrange(30):for n in [5,10]: seed = s+21 num_steps = n+1 set_seed(seed) image = pipeline(prompt,height =1024,width =1024,num_images_per_prompt =1,num_inference_steps=num_steps) image_name ="images/synth_{}_{}.jpeg" image_save = image.images[0].save(image_name.format(seed,num_steps))
Random MIDI randomness. Explosive torrent of musical notes. Exploding the universe of music. Music everywhere. Neon super colorful sonic cartoon. - dreamshaper
Warning
Some of the audio is loud, be careful with volume. MIDI was mangled.
If I’m going to mangle MIDI, I should at least stuff it with as many notes from a uniform distribution as I care to.
I will now attempt to randomize the pancakes out of a MIDI file until they splatter into a polyphonic volcano of mangled bliss (that’s the dream scenario).
According to some quick research, it seems that fluid synth (that I’m using to quickly render MIDI files with premium cheese factor) has at least 128 voices of polyphony, which is enough to play all of the midi notes at the same time…potentially all in randomized voices.
Here’s the plan. Will it blend? I don’t know. I’m pretty sure the first part of the plan will blend quickly.
Create a piano roll matrix with 128 rows and enough columns of midi ticks for 30 seconds.
Completely randomize note occurrence across the whole matrix, such that any note could occur in any time point.
Increase the note density so that there is a lot of notes being randomly generated…maybe not all the way to 128 notes per tick, that seems too extreme (but what does that sound like !?!).
Bump this super fat rando matrix out to a midi file.
For the easy win, render it via fluid synth and listen to it.
Maybe this will be easy too, but I’d like to randomize the synth voice for each note in the matrix. I’m not sure how straight forward this part is…will find out. Update, I didn’t do that part, saving for another day.
Let the abomination begin.
Code
library(midiblender)#import midi#using mario to get the midi headersmario <-midi_to_object("all_overworld.mid")list2env(mario, .GlobalEnv) # send objects to global environment# convert midi to matrixn_notes <-128n_ticks <-96*4*15#15 bars at 120BPM of this should be enoughfeature_vector <-rep(1,n_notes*n_ticks)prob_vector <- feature_vector/sum(feature_vector)# ensures uniform sampling of notes into tick intervals# Number of notes is approximately = density# my note density goes to 1100rando_vector <- midiblender::new_features_from_probs(prob_vector,density =1100)# convert back piano roll matrixrando_matrix <-feature_vector_to_matrix(rando_vector, num_notes=128)################ turn it back into midi and render to mp3track_0 <-copy_midi_df_track(midi_df,track_num =0)# transform back to midimidi_time_df <-matrix_to_midi_time(midi_matrix = rando_matrix,smallest_time_unit =1,note_off_length =8)meta_messages_df <-get_midi_meta_df(track_0)meta_messages_df <-set_midi_tempo_meta(meta_messages_df,update_tempo =500000)split_meta_messages_df <-split_meta_df(meta_messages_df)new_midi_df <-matrix_to_midi_track(midi_time_df = midi_time_df,split_meta_list = split_meta_messages_df,channel =0,velocity =100)#### bounce# update miditapyr dfmiditapyr_object$midi_frame_unnested$update_unnested_mf(new_midi_df)#write midi file to diskmiditapyr_object$write_file("rando_1100.mid")########## bounce to mp3 with fluid synthtrack_name <-"rando_1100"wav_name <-paste0(track_name,".wav")midi_name <-paste0(track_name,".mid")mp3_name <-paste0(track_name,".mp3")# synthesize midi file to wav with fluid synthsystem_command <- glue::glue('fluidsynth -F {wav_name} ~/Library/Audio/Sounds/Banks/nintendo_soundfont.sf2 {midi_name}')system(system_command)# convert wav to mp3av::av_audio_convert(wav_name,mp3_name)# clean up and delete wavif(file.exists(wav_name)){file.remove(wav_name)}
I used a nintendo sound font sound here. It’s nice and random, too pretty though. Not nearly enough notes.
There are 128 possible notes and 15 bars. Each bar has four beats of 96 ticks. In total, there is 128 x 96 x 4 x 15 = 737280 cells where a note could be. I sampled about 1100 notes uniformly into those cells. That’s just not enough.
It should at least go to 11 percent, or about 81000 notes in 30 seconds. That seems like so many…oooooeeee.
Code
#import midi#using mario to get the midi headersmario <-midi_to_object("all_overworld.mid")list2env(mario, .GlobalEnv) # send objects to global environment# convert midi to matrixn_notes <-128n_ticks <-96*4*15#15 bars at 120BPM of this should be enoughfeature_vector <-rep(1,n_notes*n_ticks)prob_vector <- feature_vector/sum(feature_vector)# ensures uniform sampling of notes into tick intervals# Number of notes is approximately = density# my note density goes to 11%rando_vector <- midiblender::new_features_from_probs(prob_vector,density =81100)# convert back piano roll matrixrando_matrix <-feature_vector_to_matrix(rando_vector, num_notes=128)################ turn it back into midi and render to mp3track_0 <-copy_midi_df_track(midi_df,track_num =0)# transform back to midimidi_time_df <-matrix_to_midi_time(midi_matrix = rando_matrix,smallest_time_unit =1,note_off_length =8)meta_messages_df <-get_midi_meta_df(track_0)meta_messages_df <-set_midi_tempo_meta(meta_messages_df,update_tempo =500000)split_meta_messages_df <-split_meta_df(meta_messages_df)new_midi_df <-matrix_to_midi_track(midi_time_df = midi_time_df,split_meta_list = split_meta_messages_df,channel =0,velocity =100)#### bounce# update miditapyr dfmiditapyr_object$midi_frame_unnested$update_unnested_mf(new_midi_df)#write midi file to diskmiditapyr_object$write_file("rando_B.mid")########## bounce to mp3 with fluid synthtrack_name <-"rando_B"wav_name <-paste0(track_name,".wav")midi_name <-paste0(track_name,".mid")mp3_name <-paste0(track_name,".mp3")# synthesize midi file to wav with fluid synthsystem_command <- glue::glue('fluidsynth -F {wav_name} ~/Library/Audio/Sounds/Banks/nintendo_soundfont.sf2 {midi_name}')system(system_command)# convert wav to mp3av::av_audio_convert(wav_name,mp3_name)# clean up and delete wavif(file.exists(wav_name)){file.remove(wav_name)}
HAHAHHAHAHA. Oh MY. Giving my eurorack a run on thick noise.
MuseScore4 would not render the sheet music for this. But, fluid synth didn’t even blink.
It’s very loud, so beware.
Chunky compared to white noise, and whiffs of crystal rain.
Notes
That was fun. Had to try it. Even though this is basically ridiculous, I might return here one day with some FX pedals.
I couldn’t stay away. Too many questions. And, it seems like I could add program change notes to change the synth voice, so I’m going to try that. And, I wondered about ramping the note density up and down over time, and will try that too. Maybe some other stuff.
Randomizing notes and program changes
Based ona few checks of MIDI files, it seems I could insert a program change message anywhere in a track, and this ought to change the synth voice. Gotta try it out.
Notes:
how many patches are in the nintendo sound font? need to find out.
Maybe run this? Seems to work and shows a long enough list to get me started.
Need to keep the program change between 0-127. I guess there would be other stuff to switch banks. Leaving that for now.
Code
library(midiblender)#import midi#using mario to get the midi headersmario <-midi_to_object("all_overworld.mid")list2env(mario, .GlobalEnv) # send objects to global environment# convert midi to matrixn_notes <-128n_ticks <-96*4*15#15 bars at 120BPM of this should be enoughfeature_vector <-rep(1,n_notes*n_ticks)prob_vector <- feature_vector/sum(feature_vector)# ensures uniform sampling of notes into tick intervals# Number of notes is approximately = density# my note density goes to 1100rando_vector <- midiblender::new_features_from_probs(prob_vector,density =1100)# convert back piano roll matrixrando_matrix <-feature_vector_to_matrix(rando_vector, num_notes=128)################ turn it back into midi and render to mp3track_0 <-copy_midi_df_track(midi_df,track_num =0)# transform back to midimidi_time_df <-matrix_to_midi_time(midi_matrix = rando_matrix,smallest_time_unit =1,note_off_length =16)meta_messages_df <-get_midi_meta_df(track_0)meta_messages_df <-set_midi_tempo_meta(meta_messages_df,update_tempo =500000)split_meta_messages_df <-split_meta_df(meta_messages_df)new_midi_df <-matrix_to_midi_track(midi_time_df = midi_time_df,split_meta_list = split_meta_messages_df,channel =0,velocity =100)#################### add random program changes before each note oni <-0while(i <dim(new_midi_df)[1]){ i <- i+1if(new_midi_df$type[i] =="note_on"){ new_midi_df <- new_midi_df %>% dplyr::add_row(i_track =0,meta =FALSE,time =0,program =sample(0:127,1),channel =0,type ="program_change",.before = i) i <- i+1 }}#### bounce# update miditapyr dfmiditapyr_object$midi_frame_unnested$update_unnested_mf(new_midi_df)#write midi file to diskmiditapyr_object$write_file("rando_1100_PC_B.mid")########## bounce to mp3 with fluid synthtrack_name <-"rando_1100_PC"wav_name <-paste0(track_name,".wav")midi_name <-paste0(track_name,".mid")mp3_name <-paste0(track_name,".mp3")# synthesize midi file to wav with fluid synthsystem_command <- glue::glue('fluidsynth -F {wav_name} ~/Library/Audio/Sounds/Banks/nintendo_soundfont.sf2 {midi_name}')system(system_command)#system_command <- glue::glue('fluidsynth -F {wav_name} ~/Library/Audio/Sounds/Banks/FluidR3_GM.sf2 {midi_name}')#system(system_command)# convert wav to mp3av::av_audio_convert(wav_name,mp3_name)# clean up and delete wavif(file.exists(wav_name)){file.remove(wav_name)}
I got a lot of fluidsynth warnings for instrument not found when the PC value was 120 or greater. Oh well. This sounds like a breathy R2D2.
Here’s another one using the FluidR3_GM.sf2 soundfont, which is a General MIDI kind of thing. I set the note length to a bit longer.
Ha, I like it.
Mario with every note played by a random instrument
I gotta one more thing with this. What does the Mario music sound like when every note is played by a different instrument?
Code
library(midiblender)#import midi#using mario to get the midi headersmario <-midi_to_object("all_overworld.mid")list2env(mario, .GlobalEnv) # send objects to global environmentnew_midi_df <- midi_df#################### add random program changes before each note oni <-0while(i <dim(new_midi_df)[1]){ i <- i+1if(new_midi_df$type[i] =="note_on"){ new_midi_df <- new_midi_df %>% dplyr::add_row(i_track =0,meta =FALSE,time =0,program =sample(0:127,1),channel =0,type ="program_change",.before = i) i <- i+1 }}#### bounce# update miditapyr dfmiditapyr_object$midi_frame_unnested$update_unnested_mf(new_midi_df)#write midi file to diskmiditapyr_object$write_file("mario_rand_voice.mid")########## bounce to mp3 with fluid synthtrack_name <-"mario_rand_voice"wav_name <-paste0(track_name,".wav")midi_name <-paste0(track_name,".mid")mp3_name <-paste0(track_name,".mp3")# synthesize midi file to wav with fluid synth#system_command <- glue::glue('fluidsynth -F {wav_name} ~/Library/Audio/Sounds/Banks/nintendo_soundfont.sf2 {midi_name}')#system(system_command)system_command <- glue::glue('fluidsynth -F {wav_name} ~/Library/Audio/Sounds/Banks/FluidR3_GM.sf2 {midi_name}')system(system_command)# convert wav to mp3av::av_audio_convert(wav_name,mp3_name)# clean up and delete wavif(file.exists(wav_name)){file.remove(wav_name)}
Meh…If I had a better handle on which patches I was randomizing over I bet that could sound more interesting.
Maybe I should write a utility function to play through the sounds really fast and check them out.
Sweeping randomness up and down
If this was a eurorack module there would be a whole bunch of knobs to turn. I would turn a knob for note density, so let’s see how easy it would be to have the randomness start with few notes, go up to ludicrous speed, and then come back down again.
And, program change all the notes, because why not.
Code
library(midiblender)#import midi#using mario to get the midi headersmario <-midi_to_object("all_overworld.mid")list2env(mario, .GlobalEnv) # send objects to global environment# convert midi to matrixn_notes <-128n_ticks <-96*4*15#15 bars at 120BPM of this should be enoughpoints <- n_notes*n_ticksmid_point <-round(points /2)ramp_up <-round(seq(1, 100, length.out = mid_point))ramp_down <-round(seq(100, 1, length.out = (points - mid_point)))feature_vector <-c(ramp_up,ramp_down)prob_vector <- feature_vector/sum(feature_vector)# ensures uniform sampling of notes into tick intervals# Number of notes is approximately = density# my note density goes to 1100rando_vector <- midiblender::new_features_from_probs(prob_vector,density =4000)# convert back piano roll matrixrando_matrix <-feature_vector_to_matrix(rando_vector, num_notes=128)################ turn it back into midi and render to mp3track_0 <-copy_midi_df_track(midi_df,track_num =0)# transform back to midimidi_time_df <-matrix_to_midi_time(midi_matrix = rando_matrix,smallest_time_unit =1,note_off_length =8)meta_messages_df <-get_midi_meta_df(track_0)meta_messages_df <-set_midi_tempo_meta(meta_messages_df,update_tempo =500000)split_meta_messages_df <-split_meta_df(meta_messages_df)new_midi_df <-matrix_to_midi_track(midi_time_df = midi_time_df,split_meta_list = split_meta_messages_df,channel =0,velocity =100)#################### add random program changes before each note oni <-0while(i <dim(new_midi_df)[1]){ i <- i+1if(new_midi_df$type[i] =="note_on"){ new_midi_df <- new_midi_df %>% dplyr::add_row(i_track =0,meta =FALSE,time =0,program =sample(0:127,1),channel =0,type ="program_change",.before = i) i <- i+1 }}#### bounce# update miditapyr dfmiditapyr_object$midi_frame_unnested$update_unnested_mf(new_midi_df)#write midi file to diskmiditapyr_object$write_file("rando_ramp.mid")########## bounce to mp3 with fluid synthtrack_name <-"rando_ramp"wav_name <-paste0(track_name,".wav")midi_name <-paste0(track_name,".mid")mp3_name <-paste0(track_name,".mp3")# synthesize midi file to wav with fluid synth#system_command <- glue::glue('fluidsynth -F {wav_name} ~/Library/Audio/Sounds/Banks/nintendo_soundfont.sf2 {midi_name}')#system(system_command)system_command <- glue::glue('fluidsynth -F {wav_name} ~/Library/Audio/Sounds/Banks/FluidR3_GM.sf2 {midi_name}')system(system_command)# convert wav to mp3av::av_audio_convert(wav_name,mp3_name)# clean up and delete wavif(file.exists(wav_name)){file.remove(wav_name)}
A ramp up and down of sorts was added. Didn’t achieve ludicrous levels so much in the middle. Clocking out on this.