Using Python to split an Audio Track into chords
I thought it would be interesting to use Chordify as a tool for remixing. I have tried some remixing in the past, but it was very tedious to harmonize tracks, it took a lot of trial and error.
It is a little easier now with Chordify, but still hard to find tracks with matching chords. I wrote a Python script to split tracks in to smaller tracks based on the chord progression using the time aligned midi export.
This is what the wav output look like :
000.D#_G_A#_86bpm-Thick As Thieves.wav
"000" = The segment number, "D#_G_A#" = the chord note names, "86bpm" = bpm, and "Thick As Thieves" = the song name.
The entire output looks like this. I've checked in FL Studio and the Audio Segments line up correctly with the time aligned midi file.

Now I can search for "D#_G_A#" and get all song segments that use the exact same chord. In theory these would be ideal to to use together in a remix. The nice thing about this is I can search in a directory of songs and find other song segment in songs that use the same chord. I'm not sure...how useful this will actually be for remixing, but I'm look forward to finding that out soon.

'''
Here is the script for windows.
WARNING: This is messy "uncommented" code.
I would have been lost without ChatGPT.
I am not going to do much more code writing until
I see how useful this is in practice.
Notes:
If you copy and paste this in to vs code you will see the
space format is wrong, Maybe someone knows how to fix that?
Requirements: mido
https://pypi.org/project/mido/
MP3 import would be nice, I couldn't figure out how to do this with PyDub or Librosa.
You have to convert mp3 to wav.
I use ffmpeg: ffmpeg -i file.mp3 file.wav
Here is a windows batch script to convert a directory of mp3 files:
for %%a in (*.mp3) do (
ffmpeg -i "%%~na.mp3" "%%~na.wav"
)
---
To run the script, edit these variables:
mid = Path to midi File
song = Path to WAV file.
out = "Full:/Path/To/ Ouput/Directory/"
name = "Song Name"
'''
import mido
mid = mido.MidiFile("D:\Midi Python\Thick as Thieves_86_BPM.mid")
song = "D:\Midi Python\Thick As Thieves_86_BPM.wav"
out = "D:/Midi Python/Thick As Thieves_86_BPM_Chords/"
name = "Thick As Thieves"
ppq = mid.ticks_per_beat
# Get the first tempo change message
def get_bpm():
tempo_change = mido.midifiles.MetaMessage("set_tempo", tempo=0, time=0)
for track in mid.tracks:
for msg in track:
if msg.type == "set_tempo":
tempo_change = msg
break
# Calculate the BPM from the first tempo change message
tempo = tempo_change.tempo
bpm = 60 / (tempo / 1000000) * (ppq / mid.ticks_per_beat)
return int(bpm)
def get_mpt():
bpm = get_bpm()
return 60000 / (bpm * ppq)
mpt=get_mpt()
bpm = get_bpm()
secs = []
track_num=-1
time_start = 0
time_end =0
for track in mid.tracks:
msg_time = 0
track_num+=1
for msg in track:
if track_num==2:
time_start = time_end
if (msg.type == 'note_on'):
time_start = time_end
if (msg.type == 'note_off'):
time_end = time_start+msg.time*mpt
secs.append([time_start/1000,time_end/1000])
midi_nums = []
chord=[]
track_num=-1
for track in mid.tracks:
track_num+=1
for msg in track:
if track_num==1:
if (msg.type == 'note_on'):
d=msg.dict()
chord.append(d['note'])
if (msg.type == 'note_off'):
if len(chord)>0:
midi_nums.append(chord)
chord=[]
#print(midi_nums)
NOTES = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B']
OCTAVES = list(range(11))
NOTES_IN_OCTAVE = len(NOTES)
errors = {
'program': 'Bad input, please refer this spec-\n'
'http://www.electronics.dit.ie/staff/tscarff/Music_technology/midi/program_change.htm',
'notes': 'Bad input, please refer this spec-\n'
'http://www.electronics.dit.ie/staff/tscarff/Music_technology/midi/midi_note_numbers_for_octaves.htm'
}
def get_note(number: int):
octave = number // NOTES_IN_OCTAVE
assert octave in OCTAVES, errors['notes']
assert 0 <= number <= 127, errors['notes']
note = NOTES[number % NOTES_IN_OCTAVE]
#return note+str(octave)
return note
chords = []
for num in midi_nums:
s=''
for n in num:
s+=get_note(n)+"_"
s+=str(bpm)+"bpm-"+name
chords.append(s)
import wave
idx=0
for start,end in secs:
with wave.open(song, "rb") as infile:
nchannels = infile.getnchannels()
sampwidth = infile.getsampwidth()
framerate = infile.getframerate()
infile.setpos(int(start * framerate))
data = infile.readframes(int((end - start) * framerate))
file_name = out+str(idx).zfill(3)+"."+chords[idx]+".wav"
with wave.open(file_name, 'w') as outfile:
outfile.setnchannels(nchannels)
outfile.setsampwidth(sampwidth)
outfile.setframerate(framerate)
outfile.setnframes(int(len(data) / sampwidth))
outfile.writeframes(data)
idx+=1
Bitte melden Sie sich an, um einen Kommentar zu hinterlassen.
Kommentare
0 Kommentare