def writeLyrics(fileName, inputDir, outputDir, offset=0, newBPM=None): fullInputFileDirectory = "./{0}/{1}".format(inputDir, fileName) fullOutputFileDirectory = "./{0}/{1}.json".format( outputDir, fileName[:-4]) # Alternatively, do songTitle. lyricOverTime = [] karFile = mf.midifile() try: karFile.load_file(fullInputFileDirectory) except IndexError: #If, for some god-forsaken reason, this doesnt work, Then catch the errors that we are sick of seeing. print("Didn't Write '{}'".format(fileName)) except UnboundLocalError: print("Didn't Write '{}'".format(fileName)) syllables = karFile.karsyl times = karFile.kartimes bpms = karFile.bpm oldBPM = bpms[0][0] if offset is None: offset = 0 if newBPM is None or newBPM is 0: newBPM = oldBPM for i, time in enumerate(times): lyric = syllables[i] while lyric.find('\xb4') != -1: index = lyric.find('\xb4') lyric = lyric[:index] + "\'" + lyric[index + 1:] print("Found Lyric:", index) print("LYRIC:", lyric) # lyric = lyric.encode(ISO-8859-1') stamp = TimeStampEvent(time, lyric) stamp.shiftTime(offset) stamp.normalizeBPM(oldBPM, newBPM) lyricOverTime.append(stamp.__dict__) print(lyricOverTime) #Convert it to a json String jsonTimeStamps = json.dumps(lyricOverTime) #Create a new file for it at the full output directory timeStampFile = open(fullOutputFileDirectory, "w") if jsonTimeStamps != '[]': #Only write it if it has content. print("Wrote {}".format(fileName)) timeStampFile.write(jsonTimeStamps)
#!/usr/bin/env python # example1.py is an extremely simple text-based karaoke application that # runs on the console. It doesn't play any music, just shows the lyrics. # This adds music to example1.py using the power of pygame. # Requires: pygame. import midifile, time, datetime, sys import pygame filename=raw_input('Please enter filename of .mid or .kar file:') m=midifile.midifile() m.load_file(filename) pygame.mixer.init() pygame.mixer.music.load(filename) pygame.mixer.music.play(0,0) # Start song at 0 and don't loop start=datetime.datetime.now() if not m.karfile: print "This is not a karaoke file. I'll just play it" while pygame.mixer.music.get_busy(): time.sleep(1) sys.exit(0) #start=start-datetime.timedelta(0,90) # To start lyrics at a later point dt=0. while pygame.mixer.music.get_busy(): dt=(datetime.datetime.now()-start).total_seconds() m.update_karaoke(dt)
def __init__(self, filename, justForTheLyrics=False): self.filename = filename self.songname = os.path.basename(filename).replace(".kar", "") self.MP3S_DIR = "./mp3s/" + self.songname + "/" self.WAVS_DIR = self.MP3S_DIR.replace("mp3", "wav") self.lyrics = None self.tonedSyls = None self.tonedWords = None self.firstNoteTime = None self.midi = midifile.midifile() self.midi.load_file(filename) self.FNULL = open(os.devnull, 'w') # some initial clean up karsyl = list(self.midi.karsyl) kartimes = list(self.midi.kartimes) for (i, s) in enumerate(karsyl): s = s.replace('/', ' ') s = s.replace('\\', ' ') s = s.replace('_', ' ') s = s.replace('\"', '') s = s.replace('\'', '') s = s.replace(',', '') s = s.replace('.', '') s = s.replace('!', '') s = s.replace('?', '') karsyl[i] = s # get syllables and times syls = [(s, t) for (s, t) in zip(karsyl, kartimes) if s != ''] # this is a long string with the lyrics self.lyrics = "" for (s, t) in syls: self.lyrics += s self.lyrics = self.lyrics.strip() print self.lyrics.decode('iso-8859-1') if (justForTheLyrics): return # only return non-empty syllables syls = [(s.decode('iso-8859-1').lower().encode('iso-8859-1'), t) for (s, t) in syls if s != '' and s != ' '] noteTrack = None # figure out which track has notes for the lyrics minDiff = -1 candidatesForRemoval = [] toneTempoList = [] toneMedian = -1 toneMax = -1 firstNoteTime = -1 for n in range(self.midi.ntracks): thisTrack = [v for v in self.midi.notes if v[4] == n] if (len(thisTrack) > 0): candidatesForRemoval.append(n) # deal with percussion tracks with lots of "notes" if len(thisTrack) < 2 * len(syls): currentSum = 0 numberOfSums = len(syls) currentToneList = [] currentToneMin = -1 currentToneMax = -1 thisTracksFirstNoteTime = thisTrack[0][5] for (s, t) in syls: minDistance = -1 minDistanceTone = -1 minDistanceTempo = -1 for (i, v) in enumerate(thisTrack): if (minDistance == -1) or abs(t - v[5]) < minDistance: minDistance = abs(t - v[5]) minDistanceTone = v[0] minDistanceTempo = 0 ii = i while (minDistanceTempo == 0) and (ii + 1 < len(thisTrack)): minDistanceTempo = thisTrack[ii][5] - v[5] ii += 1 if (minDistanceTempo == 0): ii = max(1, i) minDistanceTempo = thisTrack[ii][ 5] - thisTrack[ii - 1][5] currentSum = currentSum + minDistance * minDistance currentToneList.append( (minDistanceTone, minDistanceTempo)) if (currentToneMin == -1) or (minDistanceTone < currentToneMin): currentToneMin = minDistanceTone if (currentToneMax == -1) or (minDistanceTone > currentToneMax): currentToneMax = minDistanceTone if (minDiff == -1) or (currentSum / numberOfSums < minDiff): minDiff = currentSum / numberOfSums noteTrack = n toneTempoList = currentToneList firstNoteTime = thisTracksFirstNoteTime toneMedian = int(currentToneMin + (currentToneMax - currentToneMin) / 2) toneMax = currentToneMax toneSum = sum( [tone for (tone, tempo) in toneTempoList]) print "tone(max, med, avg): %s %s %s" % ( currentToneMax, toneMedian, toneSum / len(toneTempoList)) if len(toneTempoList) > len(syls): toneTempoList = toneTempoList[0:len(syls)] if len(toneTempoList) < len(syls): syls = syls[0:len(toneTempoList)] if len(toneTempoList) != len(syls): print "tone list length doesn't equal syllable list length" sys.exit(0) ## zip tone array into syls ## this keeps track of tones relative to median self.tonedSyls = [(s.strip(), t, p - toneMedian, d) for ((s, t), (p, d)) in zip(syls, toneTempoList)] self.firstNoteTime = firstNoteTime ## write out wav from stripped midi if not os.path.exists(self.WAVS_DIR): os.makedirs(self.WAVS_DIR) tracks2remove = [ t for t in candidatesForRemoval if t != noteTrack and t != self.midi.kartrack ] outFileKar = self.filename.replace(".kar", "__.kar") self.midi.write_file(self.filename, outFileKar, tracks2remove, None, noteTrack) outFileWav = "%s/00.%s.wav" % (self.WAVS_DIR, self.songname) midiParams = "-A 100 %s -OwM -o %s" % (outFileKar, outFileWav) subprocess.call('timidity ' + midiParams, shell=True, stdout=self.FNULL, stderr=subprocess.STDOUT) os.remove(outFileKar) if (toneMax > 70): pitchParam = 70 - toneMax inFileWav = "%s/xx.%s.wav" % (self.WAVS_DIR, self.songname) subprocess.call('mv %s %s' % (outFileWav, inFileWav), shell='True', stdout=self.FNULL, stderr=subprocess.STDOUT) stParams = "%s %s -pitch=%s" % (inFileWav, outFileWav, pitchParam) subprocess.call('soundstretch ' + stParams, shell='True', stdout=self.FNULL, stderr=subprocess.STDOUT) subprocess.call('rm %s' % (inFileWav), shell='True', stdout=self.FNULL, stderr=subprocess.STDOUT) ## fix case where syllable has multiple syllables ultimateSyls = [] for (s, t, p, d) in self.tonedSyls: for w in s.split(): ultimateSyls.append((w, t, p, d)) # get tuple of (word, (trigger-times), (pitches), duration) words = [] sylIndex = 0 for w in self.lyrics.decode('iso-8859-1').lower().encode( 'iso-8859-1').split(): (s, t, p, d) = ultimateSyls[sylIndex] fromSyls = s tt = [t] pp = [p] dd = d sylIndex += 1 while (fromSyls != w): (s, t, p, d) = ultimateSyls[sylIndex] fromSyls += s tt.append(t) pp.append(p) dd += d sylIndex += 1 words.append((w, tt, pp, dd)) ## put words with same start time back together ultimateWords = [] i = 0 while (i < len(words)): currentWord = words[i] ii = i + 1 while (ii < len(words)) and (words[i][1][0] == words[ii][1][0]): currentWord = words[i] if ( words[i][3] > words[ii][3]) else words[ii] ii += 1 i = ii ultimateWords.append(currentWord) self.tonedWords = ultimateWords
import os, re, sys import midifile OUT_KAR_DIR = "./out-kars/" OUT_LYRICS_DIR = "./out-lyrics/" ## make sure we're getting a kar file inFileKar = '' if len(sys.argv) > 1 and sys.argv[1].endswith(".kar"): inFileKar = sys.argv[1] else: print "Please provide a .kar karaoke file" sys.exit(0) ## read it and make sure it's a valid kar file myKar = midifile.midifile() myKar.load_file(inFileKar) if not myKar.karfile: print "This is not a valid karaoke file" sys.exit(0) ## get filename from file location filename = os.path.basename(inFileKar) ## create directory for decomposed kar files if not os.path.exists(OUT_KAR_DIR): os.makedirs(OUT_KAR_DIR) ## determine which tracks are being used for notes candidateTracks = {} for v in myKar.notes: