Exemplo n.º 1
0
def getAlbum(savefilename, playlistindex, tracklist, flags, genre='', reset=True):
   metadata, tracklist = tracklist[0], tracklist[1:]
   metadata = list(metadata) + [genre]
   s = ParseTables.writeextendedtracklist("%s.txt" % os.path.join(dumpdir, savefilename), flags, metadata, tracklist)
   logger.info("\n%s" % s)
   
   playlistindex = int(playlistindex)
   albumlen = sum((s[1] for s in tracklist))
   logger.info("Length: %d" % albumlen)
   
   if playlistindex > vs['Constants']['maxplaylists']:
      logger.error("Playlist index must be %d or below" % vs['Constants']['maxplaylists'])
      sys.exit(2)

   MouseDo(' '.join([Wait(2),
                     Window(1), 
                     Move(*vs['Audacity']['record']), 
                     Click(), 
                     Wait(5), 
                     Window(0), 
                     Play(playlistindex), 
                     Wait(2),
                     Move(*vs['Grooveshark']['safespot'])
                    ]))
   if albumlen > 3600:
      Idle(albumlen + 30)
   else:
      time.sleep(albumlen + 30)
   MouseDo(' '.join([Move(100,100),
                     Wait(1),
                     Window(1),
                     Move(*vs['Audacity']['stop']),
                     Click()]))
   if reset:
      savepath = os.path.join(dumpdir, "%s.wav" % savefilename)
      MouseDo(' '.join([Move(*vs['Audacity']['file']),
                        Click(),
                        Move(*vs['Audacity']['export']),
                        Click(),
                        Type(savepath),
                        Enter(),
                        Enter(),
                        Move(*vs['Audacity']['middle']),
                        Wait(albumlen / 35),
                        Click(),
                        Toggle('c'),
                        Type('z'),
                        Toggle('c'),
                        Window(0),
                        Move(*vs['Grooveshark']['empty']),
                        Click(),
                        Wait(1),
                        Window(2)
                       ]))
      if split:
         splitTrack(savefilename)
Exemplo n.º 2
0
def checkcommands(c, pr=True):
   i = 0
   for savefilename, genre, playlistindex, metadata, flags in c:
      status, e = ParseTables.canread(metadata)
      good = True
      if not status:
         logger.error("Can't read metadata from %s. (%s)" % (metadata, str(e)))
         good = False
      elif pr:
         Util.foldtracklist(e, genre, flags)
         logger.info("%s\n%s\n" % (playlistindex, '\n'.join(['\t'.join(map(str, row)) for row in e])))
      i += 1
   return good
Exemplo n.º 3
0
def main():
   i = 0
   if record:
      CheckCommands.checkcommands(commands, pr=False)
      for alname, genre, playlistindex, metadata, flags in commands:
         savefilename = Util.removepunctuation(alname.strip().replace(' ', '').lower())
         logger.info("Saving to: %s.wav" % os.path.join(dumpdir, savefilename))
         tracklist = ParseTables.gettracklist(metadata)
         logger.info("Metadata found at %s" % metadata)
         if noreset and i == len(commands)-1:
            reset = False
         else:
            reset = True
         getAlbum(savefilename, playlistindex, tracklist, flags, genre, reset)
   elif split:
      savefilename = Util.removepunctuation(sys.argv[1].strip().replace(' ', '').lower())
      splitTrack(savefilename)
      i += 1
Exemplo n.º 4
0
         genre = sys.argv.pop(1)
      elif arg[1:] == '-year':
         year = int(sys.argv.pop(1))
      elif arg[1:] == '-artist':
         artist = sys.argv.pop(1)
      elif arg[1:] == '-album':
         album = sys.argv.pop(1)
      elif arg[1:] == 'b':
         bitrate = int(sys.argv.pop(1))
      elif arg[1] == 'n':
         number = True
      elif arg[1] == 't':
         dryrun = True
       
directory, url = sys.argv[1:]
tracklist = ParseTables.gettracklist(url)
if not artist:
   artist = tracklist[0][0]
if not album:
   album = tracklist[0][1]
if not year:
   year = tracklist[0][2]
tracklist = tracklist[1:]
outputdir = os.path.join(os.environ['MUSIC'], 'iTunes', 'iTunes Music', artist, album)

print artist, album, year

tracks = glob.glob(os.path.join(directory, '*.wav'))
trackcount = len(tracks)
contents = os.listdir(outputdir)
for i, track in enumerate(tracks):
Exemplo n.º 5
0
def splitTrack(savefilename):
   savepath = os.path.join(dumpdir, "%s.wav" % savefilename)
   wav = SplitTracks.WAVSplitter(savepath)
   tracks = wav.parsewav()

   notesfile = open(os.path.join(dumpdir, "%s-notes.txt" % savefilename), 'w')
   
   logger.debug(', '.join(["%f" % t['length'] for t in tracks]), notesfile) 
   
   flags, metadata, tracklist = ParseTables.readextendedtracklist(os.path.join(dumpdir, "%s.txt" % savefilename))
   album, artist, year, genre = metadata
   maxnamelen = max((len(t[0]) for t in tracklist))
   
   #Match up the measured tracks with the "official" list, tracklist
   
   trackmappings = [None] * len(tracklist)
   tracksfailed = [False] * len(tracklist)
   lastmatched = 0
   #Match any with similar track lengths
   #for i, t in enumerate(tracklist):
   #   for j, track in enumerate(tracks[lastmatched:]):
   #      if abs(track['length'] - t[1]) < vs['Constants']['tracklentoleranceseconds']:
   #         trackmappings[i] = [lastmatched+j]
   #         lastmatched = j
   #See if runs of tracks can be combined to match with official ones

   #Simple case if numbers of found and official tracks match
   if len(tracks) == len(tracklist):
      for i in xrange(len(tracklist)):
         trackmappings[i] = [i]
   else:
      def trackmappingbounds(i):
         j = i + 1
         while j < len(trackmappings) and not trackmappings[j]:
            j += 1
            
         k = i - 1
         while k >= 0 and not trackmappings[k]:
            k -= 1
         
         #print "bounds=[%d, %d)" % (k, j)
         if k < 0:
            if j < len(trackmappings):
               indexrange = range(trackmappings[j][0])
            else:
               indexrange = range(len(tracks))
         elif j >= len(trackmappings):
            indexrange = range(trackmappings[k][-1] + 1, len(tracks))
         else:
            indexrange = range(trackmappings[k][-1]+1, trackmappings[j][0])
         return indexrange
      
      for i, t in enumerate(tracklist):
         if trackmappings[i] is None:
            indexrange = trackmappingbounds(i)
            
            matched = False
            
            #indexrange is the range of track indices in tracks that may be part of official track i
            for j1 in range(len(indexrange)):
               for j2 in range(j1+1, len(indexrange)+1):
                  if abs(sum((tracks[k]['length'] for k in indexrange[j1:j2])) - t[1]) < vs['Constants']['tracklentoleranceseconds']:
                     trackmappings[i] = indexrange[j1:j2]
                     #print "Succeed", i, trackmappings[i]
                     matched = True
                     for k, tracki in enumerate(trackmappings[i][:-1]):
                        if tracks[trackmappings[i][k]]['sharpend'] and tracks[trackmappings[i][k+1]]['sharpstart']:
                           track = tracks[tracki]
                           logger.warn("Removing skip at %f in %s" % (float(track['tend'] - track['tstart']) / wav.framerate, tracklist[i][0]), notesfile)
                     break
               if matched:
                  break
                  
      for i, t in enumerate(tracklist):
         if trackmappings[i] is None:
            indexrange = trackmappingbounds(i)
            trackmappings[i] = indexrange
            #print "Fail   ", i, trackmappings[i]
            #print trackmappings
            logger.warn("Failed to locate track %d, %s" % (i+1, tracklist[i][0]), notesfile)
            tracksfailed[i] = True
   
   realtracks = list()
   
   for i, m in enumerate(trackmappings):
      failed = tracksfailed[i]
      if len(m) == 0:
         continue
      sharpstart = tracks[m[0]]['sharpstart']
      sharpend = tracks[m[-1]]['sharpend']
      startreason = tracks[m[0]]['startreason']
      endreason = tracks[m[-1]]['endreason']
      if sharpstart:
         startchar = '|'
      else:
         startchar = '<'
      if sharpend:
         endchar = '|'
      else:
         endchar = '>'
      trackslen = sum((tracks[t]['length'] for t in m))
      diff = trackslen - tracklist[i][1]
      diffint = int(abs(diff))
      if diff < 0:
         diffstr = "%.3f seconds %s" %  (diff, '-' * diffint)
      else:
         diffstr = "%.3f seconds %s" %  (diff, '+' * diffint)
      if failed:
         logger.warn("%d. %s (%d secs):" % (i+1, tracklist[i][0].ljust(maxnamelen), tracklist[i][1]) + " =/=\t%s%s Tracks %s, Length = %.3f secs  (%s) %s%s" % (startchar, startreason, str(map(lambda x: x+1, m)), trackslen, diffstr, endreason, endchar), notesfile)
      else:
         logger.info("%d. %s (%d secs):" % (i+1, tracklist[i][0].ljust(maxnamelen), tracklist[i][1]) + " ==\t%s%s Tracks %s, Length = %.3f secs (%s) %s%s" % (startchar, startreason, str(map(lambda x: x+1, m)), trackslen, diffstr, endreason, endchar), notesfile)

      #Only remove actual skips and not intentional silence in tracks
      indexpairs = [ [tracks[m[0]]['tstart']] ]
      for j, t in enumerate(m[:-1]):
         if tracks[t]['sharpend'] and tracks[m[j+1]]['sharpstart']:
            indexpairs[-1].append(tracks[t]['tend'])
            indexpairs.append([tracks[m[j+1]]['tstart']])
      indexpairs[-1].append(tracks[m[-1]]['tend'])
      
      track = wav.compoundslice(indexpairs)
      
      realtrack = dict()
      realtrack['indexpairs'] = indexpairs
      realtrack['sharpstart'] = tracks[m[0]]['sharpstart']
      realtrack['sharpend'] = tracks[m[-1]]['sharpend']
      realtrack['startreason'] = tracks[m[0]]['startreason']
      realtrack['endreason'] = tracks[m[-1]]['endreason']
      realtrack['length'] = sum((tracks[t]['length'] for t in m))
      realtrack['tracknum'] = i+1
      
      #Dynamics analysis
      realtrack['maxlevel'] = max((tracks[t]['maxlevel'] for t in m))
      realtrack['rmsintervals'] = SplitTracks.rmsintervals(track, chunksize=wav.framerate, dbfs=True)
      realtrack['maxintervalrms'] = np.amax(realtrack['rmsintervals'])
      realtrack['dynamicrange'] = realtrack['maxlevel'] - realtrack['maxintervalrms']
      realtrack['rms'] = SplitTracks.todbfs(SplitTracks.rms(track))
      
      realtrack['dbtoamplify'] = 0.0
      realtrack['limitlevel'] = 0.0
      realtrack['residue'] = 0.0
      realtrack['postlimitamplify'] = 0.0
      
      #Skip if the track is slaved
      if ('p' in flags and i in flags['p']) or ('a' in flags and i in flags['a']):
         realtracks.append(realtrack)
         continue
         
      realtrack['hist'], bins = SplitTracks.peakhistogram(track, rangeratio=(0.5,1.0), proportional=True)
      realtrack['top10hist'], top10bins = SplitTracks.peakhistogram(track, rangeratio=(0.9,1.0), proportional=True)
      if np.amin(realtrack['top10hist']) > 0:
         realtrack['topratio'] = float(realtrack['top10hist'][-1]) / float(np.amax(realtrack['top10hist'][:-1]))
      else:
         realtrack['topratio'] = 0
      i = realtrack['hist'].shape[0]-1
      s = realtrack['hist'][i]
      while s <= vs['Constants']['proportionpeakstochop']:
         i -= 1
         s += realtrack['hist'][i]
      realtrack['choplevel'] = SplitTracks.todbfs(2**(8*wav.sampwidth-1) * (0.5 + float(i) / (2 * realtrack['hist'].shape[0])))
      logger.debug("\tMaxlevel: %.3f\tAverage RMS: %.3f\tMax RMS: %.3f\tTopratio: %.3f" % (realtrack['maxlevel'], realtrack['rms'], realtrack['maxintervalrms'], realtrack['topratio']), notesfile)
      
      realtracks.append(realtrack)
   
   #Can do analysis of the entire album's dynamics here
   maxlevel = max((rt['maxlevel'] for rt in realtracks))
   maxrms = max((rt['maxintervalrms'] for rt in realtracks))
   mindynamicrange = min((rt['dynamicrange'] for rt in realtracks))
   mindynamicrange = min(vs['Constants']['mindynamicrange'], mindynamicrange)
   
   
   #Dynamics analysis
   for i, rt in enumerate(realtracks):
      if ('p' in flags and i in flags['p']) or ('a' in flags and i in flags['a']):
         continue
      #We need either the dbtoamplify (for flat tracks) or the information for
      #limiting and post-limiting amplification
      #The simple case: the track has already been peak limited, so just amplify
      #it to the desired max level
      if rt['topratio'] >= vs['Constants']['topratioforbrickwalled'] or rt['dynamicrange'] < vs['Constants']['mindynamicrange']:
         rt['dbtoamplify'] = rt['maxlevel'] * -1 + vs['Constants']['dbfstoamplify']
      else:
         #Otherwise, set the limiting level to the level that
         #proportionpeakstochop occur above
         #Maximum amount of headroom we can lose from the peaks (will be negative)
         #Determined by the minimum dynamic range
         maxdbtochop = rt['dynamicrange'] - mindynamicrange
         minlimitlevel = 20 * math.log10((math.pow(10, float(-1 * maxdbtochop)/20) - 0.2)/0.8)
         limitlevel = max(rt['choplevel'], minlimitlevel)
         
         residue = vs['Constants']['baseresidue']
         lllfbr = vs['Constants']['lowestlimitlevelforbaseresidue']
         #Lower residual level if the chop is extreme enough
         if limitlevel <= lllfbr:
            residue *= max(1.0 - 0.75 * (lllfbr - limitlevel) / (-1 * lllfbr), 0.25)
         
         #Calculate level to amplify to reach full scale
         limit = float(SplitTracks.fromdbfs(limitlevel))
         newlimit = limit + (2**(8*wav.sampwidth - 1) - limit) * residue
         newmaxlevel = SplitTracks.todbfs(newlimit)
         postlimitamplify = -1 * newmaxlevel - rt['maxlevel']
         hllffs = vs['Constants']['highestlimitlevelforfullscale']
         if limitlevel >= hllffs:
            postlimitamplify += (hllffs - limitlevel)/(-1 * hllffs)
      
         rt['limitlevel'] = limitlevel
         rt['residue'] = residue
         rt['postlimitamplify'] = postlimitamplify
         

   #Build runs
   if continuous or 'c' in flags:
      runs = [0] * len(realtracks)
      runnum = 1
   else:
      runs = [None] * len(realtracks)
      runnum = 0
      for i, rt in enumerate(realtracks):
         #Skip if part of a run
         if runs[i] is not None:
            continue
         
         #Look for tracks that connect to this one (unless it is not like its
      #neighbors)
         j = i-1
         while j >= 0 and ((realtracks[j+1]['sharpstart'] and realtracks[j]['sharpend']) or 
                           ('p' in flags and j in flags['p']) or ('a' in flags and j+1 in flags['a'])):
            j -= 1
         lowerbound = j + 1
         j = i+1
         while j < len(realtracks) and (realtracks[j-1]['sharpend'] and realtracks[j]['sharpstart'] or 
                           ('p' in flags and j-1 in flags['p']) or ('a' in flags and j in flags['a'])):
            j += 1
         upperbound = j
         if upperbound - lowerbound > 1:
            for j in xrange(lowerbound,upperbound):
               runs[j] = runnum
            runnum += 1

   #Connect all runs
   for i in xrange(runnum):
      run = [realtracks[j] for j in xrange(len(runs)) if runs[j] == i and ('q' not in flags or j not in flags['q'])]

      limitedrun = [t for t in run if t['dbtoamplify'] != 0.0]
      nonlimitedrun = [t for t in run if t['limitlevel'] != 0.0]
      if len(limitedrun) > 0:
         dbta = min((t['dbtoamplify'] for t in limitedrun))
         for tr in run:
            tr['dbtoamplify'] = dbta
      elif len(nonlimitedrun) > 0:
         mindex = 0
         maxl = nonlimitedrun[0]['limitlevel']
         for j, t in enumerate(nonlimitedrun[1:]):
            if (t['limitlevel'] > maxl and t['limitlevel'] != 0.0) or maxl == 0.0:
               maxl = t['limitlevel']
               mindex = j + 1
         for tr in run:
            tr['limitlevel'] = nonlimitedrun[mindex]['limitlevel']
            tr['residue'] = nonlimitedrun[mindex]['residue']
            tr['postlimitamplify'] = nonlimitedrun[mindex]['postlimitamplify']
      if len(run) > 0:
         ml = min((t['maxlevel'] for t in run))
         for tr in run:
            rt['maxlevel'] = ml

   #Apply dynamics changes and save tracks
   for i, rt in enumerate(realtracks):
      if rt['dbtoamplify'] != 0.0:
         logger.info("Amplifying track %d by %.3f dB" % (rt['tracknum'], rt['dbtoamplify']), notesfile)
      else:
         logger.info("Limiting track %d to %.3f dBfs (r=%.2f) and amplifying by %.3f dB" % (rt['tracknum'], rt['limitlevel'], rt['residue'], rt['postlimitamplify']), notesfile)
            
      if not test:
         savename = os.path.join(dumpdir, "%s%d-%s.wav" % (savefilename[:3], i, savefilename))
         wav.saveslice(rt['indexpairs'], savename, rt['sharpstart'], rt['sharpend'], rt['maxlevel'], rt['dbtoamplify'], rt['limitlevel'], rt['residue'], rt['postlimitamplify'])
         #Util.mp3encode(savename, tracklist[i][0], artist=artist, album=album, year=year, trackno=rt['tracknum'], trackcount=len(tracklist), genre=genre)
   notesfile.close()
Exemplo n.º 6
0
   num = False
   if sys.argv[1] == '-n':
      sys.argv.pop(1)
      num = True
   discno = None
   if sys.argv[1].isdigit():
      discno = int(sys.argv.pop(1))
   folder = sys.argv[1]
   foldercompressed = Util.removepunctuation(os.path.split(folder.strip(os.sep))[1].replace(' ', '').lower())
   namere = re.compile(r'^(?:%s\d{1,2}-)?%s(?:-notes)?\.(?:wav|txt)$' % (foldercompressed[:3], foldercompressed))
   toremove = list()
   for f in os.listdir(dumpdir):
      fullname = os.path.join(f, os.path.join(dumpdir, f))
      if os.path.isfile(fullname):
         fname, suffix = f.rsplit('.', 1)
         if namere.match(f):
            if suffix == 'txt' and fname.lower() == foldercompressed:
               flags, metadata, tracklist = ParseTables.readextendedtracklist(fullname)
            toremove.append(fullname)
   if len(sys.argv) > 2:
      url = sys.argv[2]
      if os.path.exists(url):
         flags, metadata, tracklist = ParseTables.readextendedtracklist(url)
      else:
         tracklist = ParseTables.gettracklistfromurl(url)
         metadata = tracklist[0]
         tracklist = tracklist[1:]
   addmetadata(folder, tracklist, metadata, num, discno)
   for fn in toremove:
      os.remove(fn)