Skip to content

Commit

Permalink
Release Version 1.0. Enjoy :)
Browse files Browse the repository at this point in the history
  • Loading branch information
Alex authored Jan 27, 2021
1 parent f56e2f8 commit c835ed6
Showing 1 changed file with 324 additions and 0 deletions.
324 changes: 324 additions & 0 deletions Examples/markovify_piano.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,324 @@
# -*- coding: utf-8 -*-
"""Markovify_Piano.ipynb
Automatically generated by Colaboratory.
Original file is located at
https://colab.research.google.com/drive/17SGUaA8r6nZyHHH3huOmgcsVUDH6HBWE
# Markovify Piano
***
## Based upon absolutely amazing markovify package of @jsvine: https://github.com/jsvine/markovify
## Powered by tegridy-tools TMIDI 1.4 Processors
***
### Project Los Angeles
### Tegridy Code 2021
***
# Setup environment
"""

#@title Install dependencies
!git clone https://github.com/asigalov61/tegridy-tools
!pip install unidecode
!pip install tqdm
!apt install fluidsynth #Pip does not work for some reason. Only apt works
!pip install midi2audio

#@title Load needed modules
print('Loading needed modules. Please wait...')

import sys
import os
import json

os.chdir('/content/tegridy-tools/tegridy-tools/')
import TMIDI
import markovify
os.chdir('/content/')

from pprint import pprint

import tqdm.auto

from midi2audio import FluidSynth
from IPython.display import display, Javascript, HTML, Audio

from google.colab import output, drive

print('Creating Dataset dir...')
if not os.path.exists('/content/Dataset'):
os.makedirs('/content/Dataset')

os.chdir('/content/')
print('Loading complete. Enjoy! :)')

"""# Download/upload desired MIDI dataset
## NOTE: Dataset must be sufficiently large and homogenous for Markov chain to train/perform properly.
"""

# Commented out IPython magic to ensure Python compatibility.
#@title Download World Melody-ABC-Tunes MIDI dataset

#@markdown Works best stand-alone/as-is for the optimal results
# %cd /content/Dataset/

!wget --no-check-certificate -O Melody-ABC-Tunes-Dataset-CC-BY-NC-SA.zip "https://onedrive.live.com/download?cid=8A0D502FC99C608F&resid=8A0D502FC99C608F%2118458&authkey=AOXe4AaDtageB-k"
!unzip -j '/content/Dataset/Melody-ABC-Tunes-Dataset-CC-BY-NC-SA.zip'
!rm '/content/Dataset/Melody-ABC-Tunes-Dataset-CC-BY-NC-SA.zip'

# %cd /content/

"""# Process the MIDI dataset
## NOTE: If you are not sure what settings to select, please use original defaults
"""

#@title Process MIDIs to special MIDI dataset with Tegridy MIDI Processor
#@markdown NOTES:

#@markdown 1) Best results are achieved with the single-track, single-channel, single-instrument MIDI 0 files.

#@markdown 2) MIDI Channel = -1 means all MIDI channels but drums will be processed. MIDI Channel = 16 means all channels will be processed. Otherwise, only single indicated MIDI channel will be processed.

file_name_to_output_dataset_to = "/content/Markovify-Piano-Music-MIDI-Dataset" #@param {type:"string"}
desired_MIDI_channel_to_process = 0 #@param {type:"slider", min:-1, max:15, step:1}

MIDI_events_time_denominator = 10
melody_notes_in_chords = True

print('TMIDI Processor')
print('Starting up...')

###########

average_note_pitch = 0
min_note = 127
max_note = 0

files_count = 0

ev = 0

chords_list_f = []
melody_list_f = []

chords_list = []
chords_count = 0

melody_chords = []
melody_count = 0

###########

print('Loading MIDI files...')
print('This may take a while on a large dataset in particular.')

dataset_addr = "/content/Dataset/"
os.chdir(dataset_addr)
filez = os.listdir(dataset_addr)

print('Processing MIDI files. Please wait...')
for f in tqdm.auto.tqdm(filez):
try:
files_count += 1
chords_list, melody = TMIDI.Tegridy_MIDI_Processor(f,
desired_MIDI_channel_to_process,
MIDI_events_time_denominator,
)

fn = os.path.basename(f)
fno = fn.split('.')[0].replace(' ', '_')

chords_l, melody_l = TMIDI.Tegridy_Chords_Converter(chords_list,
melody,
fno,
melody_notes_in_chords)

chords_list_f.extend(chords_l)
melody_list_f.extend(melody_l)
chords_count += len(chords_list)
melody_count += len(melody_l)

except:
print('Problematic MIDI:', f)
continue

average_note_pitch = int((min_note + max_note) / 2)

print('Task complete :)')
print('==================================================')
print('Number of processed dataset MIDI files:', files_count)
print('Average note pitch =', average_note_pitch)
#print('Min note pitch =', min_note)
#print('Max note pitch =', max_note)
#print('Number of MIDI events recorded:', len(events_matrix))
print('Number of MIDI chords recorded:', chords_count)
print('The longest chord:', len(max(chords_list_f, key=len)), 'notes')
print(max(chords_list_f, key=len))
print('Number of recorded melody events:', len(melody_list_f))
print('First melody event:', melody_list_f[0], 'Last Melody event:', melody_list_f[-1])
print('Total number of MIDI events recorded:', len(chords_list_f))

# Dataset
MusicDataset = [chords_list_f, melody_list_f]

# Writing dataset to pickle file
TMIDI.Tegridy_Pickle_File_Writer(MusicDataset, file_name_to_output_dataset_to)

#@title Process MIDI Dataset to TXT Dataset (w/Tegridy MIDI-TXT Processor)
full_path_to_TXT_dataset = "/content/Markovify-Piano-Music-TXT-Dataset.txt" #@param {type:"string"}
simulate_velocity = True #@param {type:"boolean"}

reduce_MIDI_channels = False
reduce_notes_velocities = False
line_by_line_dataset = True
chords_durations_multiplier = 1

# MIDI Dataset to txt dataset converter
print('TMIDI-TXT Processor')
print('Starting up...')

if simulate_velocity:
print('Simulated velocity mode is enabled.')

TXT = ''
number_of_chords = 0
number_of_bad_chords = 0
dataset_name = 'DATASET=Intelligent_VIRTUOSO_TXT_Music_Dataset'

TXT, number_of_chords, number_of_bad_chords = TMIDI.Tegridy_MIDI_TXT_Processor(dataset_name,
chords_list_f,
melody_list_f,
simulate_velocity,
line_by_line_dataset,
0,
chords_durations_multiplier,
)

print('Number of chords recorded: ', number_of_chords)
print('Number of bad/skipped chords: ', number_of_bad_chords)
print('Done!')

TXT1, n = TMIDI.Tegridy_TXT_Reducer(TXT,
include_MIDI_channels=reduce_MIDI_channels,
include_notes_velocities=reduce_notes_velocities,
line_by_line_output_dataset=False)

TMIDI.Tegridy_TXT_Dataset_File_Writer(full_path_to_TXT_dataset, TXT_String=TXT1)

"""# Load processed TXT MIDI dataset into memory"""

#@title Load/Reload processed TXT dataset
full_path_to_TXT_dataset = "/content/Markovify-Piano-Music-TXT-Dataset.txt" #@param {type:"string"}

print('Loading TXT MIDI dataset. Please wait...')
with open(full_path_to_TXT_dataset) as f:
text = f.read()
print('Dataset loaded! Enjoy :)')

"""# Train TXT Markov chain/model"""

#@title Train Markov-chain/model
markov_chain_state_size = 4 #@param {type:"slider", min:1, max:10, step:1}

print('Training Markov chain/model. Please wait...')
markov_text_model = markovify.NewlineText(text, well_formed=False, state_size=markov_chain_state_size)

print('Model is ready! Enjoy :)')

#@title Save the model
full_path_to_json_save_file = "/content/Markovify-Piano-Music-Model.json" #@param {type:"string"}

print('Converting model to json...')
model_json = markov_text_model.to_json()

print('Saving model as json file...')
with open(full_path_to_json_save_file, 'w') as f:
json.dump(model_json, f)

print('Task complete! Enjoy! :)')

#@title Load/Re-load saved model
full_path_to_json_save_file = "/content/Markovify-Piano-Music-Model.json" #@param {type:"string"}

print('Loading model from json file...')
f = open(full_path_to_json_save_file)
model_json = json.load(f)

print('Restoring the model...')
markov_text_model = markovify.Text.from_json(model_json)

print('Model loaded and restored! Enjoy! :)')

"""# Generate music composition"""

#@title Generate Music

#@markdown HINT: Each note = 3-5 characters depending on the MIDI processing settings above
minimum_number_of_characters_to_generate = 5000 #@param {type:"slider", min:100, max:10000, step:100}
number_of_cycles_to_try_to_generate_desired_result = 5000 #@param {type:"slider", min:10, max:10000, step:10}
minimum_notes_to_generate = 210 #@param {type:"slider", min:10, max:1000, step:10}
print_generated_song = False #@param {type:"boolean"}

Output_TXT_String = ''

attempt = 0

print('Generating music composition. Please wait...')

while (len(Output_TXT_String.split(' ')[1:])-2) < minimum_notes_to_generate:
out = markov_text_model.make_sentence(min_chars=minimum_number_of_characters_to_generate,
tries=number_of_cycles_to_try_to_generate_desired_result)

Output_TXT_String = ''.join(out)
print('Attempt #', attempt)
attempt += 1

print('Generation complete!')
print('=' * 70)
print(Output_TXT_String.split(' ')[0], 'with', len(Output_TXT_String.split(' ')[1:])-2, 'notes.')
print('=' * 70)

if print_generated_song:
pprint(Output_TXT_String)
print('=' * 70)

"""# Convert generated music composition to MIDI file and download/listen to the output :)"""

#@title Convert generated song to MIDI
download_generated_composition = False #@param {type:"boolean"}
show_detailed_MIDI_stats = False #@param {type:"boolean"}

print('Converting generated song from TXT to MIDI events...')
SONG, SONG_Name = TMIDI.Tegridy_Reduced_TXT_to_Notes_Converter(Output_TXT_String, line_by_line_dataset=False, has_MIDI_channels=False, has_velocities=False)

fname = TMIDI.Tegridy_File_Time_Stamp('/content/Markovify_Piano_Composition_Generated_on_')
print('Composition', fname + '.mid')

detailed_stats = TMIDI.Tegridy_SONG_to_MIDI_Converter(SONG=SONG,
output_signature='Markovify_Piano',
track_name=SONG_Name,
output_file_name=fname)

if download_generated_composition:
print('Downloading your composition now...')
from google.colab import files
files.download(fname + '.mid')

if show_detailed_MIDI_stats:
print('Detailed MIDI stats:')
pprint(detailed_stats)

#@title Listen to the last generated composition
#@markdown NOTE: May be very slow with the long compositions
print('Synthesizing the last output MIDI. Please stand-by... ')
FluidSynth("/usr/share/sounds/sf2/FluidR3_GM.sf2", 16000).midi_to_audio(str(fname + '.mid'), str(fname + '.wav'))
Audio(str(fname + '.wav'), rate=16000)

0 comments on commit c835ed6

Please sign in to comment.