defaults=defaultpath, scriptpath=os.path.dirname(__file__)) p = userinterface.printer(ops.VERBOSITY, os.path.dirname(__file__), "/tmp/agsegmentationlog.txt") p.printProgramInfo(audioguide.__version__, force=True) AnalInterface = ops.createAnalInterface(p) ############ ## TARGET ## ############ p.middleprint('AUDIOGUIDE SEGMENT SOUNDFILE', force=True) filetosegment = sfsegment.target(ops.TARGET, AnalInterface) filetosegment.initAnal(AnalInterface, ops, p) filetosegment.stageSegments(AnalInterface, ops, p) minamp = util.ampToDb(min(filetosegment.whole.desc['power'])) p.pprint("Evaluating %s from %.2f-%.2f" % (filetosegment.filename, filetosegment.whole.segmentStartSec, filetosegment.whole.segmentEndSec), colour="BOLD") p.pprint("\nAN ONSET HAPPENS when", colour="BOLD") p.pprint( "The amplitude crosses the Relative Onset Trigger Threshold: ${YELLOW}" + "%.2f" % options.TRIGGER_THRESHOLD + " ${NORMAL}(-t option)\n", colour="NORMAL") p.pprint("\nAN OFFSET HAPPENS when", colour="BOLD") p.pprint( "1. Offset Rise Ratio: when next-frame's-amplitude/this-frame's-amplitude >= ${YELLOW}" + "%.2f" % options.RISERATIO + " ${NORMAL}(-r option)", colour="NORMAL") p.pprint("\t...or...", colour="BOLD")
def initAnal(self, AnalInterface, ops, p): # Start by loading the entire target as an # sfsegment to get the whole amplitude envelope. # see if we need to time stretch the target file... if self.stretch != 1: self.timeStretch(AnalInterface, ops, p) # analise the whole target sound! self.whole = sfsegment(self.filename, self.startSec, self.endSec, AnalInterface, envDb=self.envDb, normtag='tgtwhole') self.startSec = self.whole.segmentStartSec self.endSec = self.whole.segmentEndSec self.whole.midiPitchMethod = self.midiPitchMethod self.lengthInFrames = self.whole.lengthInFrames # SEGMENTATION DATA CONTAINERS self.segs = [] self.segmentationInFrames = [] self.segmentationInOnsetFrames = [] self.extraSegmentationData = [] self.seglengths = [] # SEGMENTATION power = AnalInterface.getDescriptorColumn(self.filename, 'power') self.segmentationMinLenFrames = AnalInterface.s2f( self.segmentationMinLenSec, self.filename) self.segmentationMaxLenFrames = AnalInterface.s2f( self.segmentationMaxLenSec, self.filename) p.log( "TARGET SEGMENTATION: minimum segment length %.3f sec; maximum %.3f sec" % (self.segmentationMinLenSec, self.segmentationMaxLenSec)) self.minPower = min(power) if self.minPower < util.dbToAmp( self.segmentationOffsetThreshAbs): # use absolute threshold self.powerOffsetValue = util.dbToAmp( self.segmentationOffsetThreshAbs) p.log( "TARGET SEGMENTATION: using an offset amplitude value of %s" % (self.segmentationOffsetThreshAbs)) else: self.powerOffsetValue = self.minPower * util.dbToAmp( self.segmentationOffsetThreshAdd) p.log( "TARGET SEGMENTATION: the amplitude of %s never got below the offset threshold of %sdB specified in offsetThreshAbs. So, I'm using offsetThreshAdd dB (%.2f) above the minimum found power -- a value of %.2f dB." % (self.filename, self.segmentationOffsetThreshAdd, self.segmentationOffsetThreshAdd, util.ampToDb(self.powerOffsetValue))) if self.segmentationFilepath == None: import descriptordata odf = descriptordata.odf(power, 7) # do multirise segmentation? if self.multiriseBool: multiriseDeviation = self.segmentationOffsetRise * ( self.multirisePercentDev / 100.) riseRatioList = np.linspace( max(1.05, self.segmentationOffsetRise - multiriseDeviation), self.segmentationOffsetRise + multiriseDeviation, num=self.multiriseSteps) else: # just use one rise ration riseRatioList = [self.segmentationOffsetRise] for userRiseRatio in riseRatioList: # this a list of rises if desired! segments, logic = segmentationAlgoV2( self.segmentationThresh, self.powerOffsetValue, userRiseRatio, power, odf, self.segmentationMinLenFrames, self.segmentationMaxLenFrames, AnalInterface) # ensure that each segment isn't in the list already for idx in range(len(segments)): if segments[idx] in self.segmentationInOnsetFrames: continue self.segmentationInOnsetFrames.append(segments[idx]) if ops.TARGET_SEGMENT_LABELS_INFO == 'logic': self.extraSegmentationData.append(logic[idx]) # end loop closebartxt = "Found %i segments (threshold=%.1f offsetrise=%.2f offsetthreshadd=%.2f)." % ( len(self.segmentationInOnsetFrames), self.segmentationThresh, self.segmentationOffsetRise, self.segmentationOffsetThreshAdd) else: # load target segments from a file p.log("TARGET SEGMENTATION: reading segments from file %s" % (self.segmentationFilepath)) for dataentry in util.readAudacityLabelFile( self.segmentationFilepath): startf = AnalInterface.s2f(dataentry[0], self.filename) endf = AnalInterface.s2f(dataentry[1], self.filename) self.segmentationInOnsetFrames.append((startf, endf)) self.extraSegmentationData.append('from file') closebartxt = "Read %i segments from file %s" % ( len(self.segmentationInFrames), os.path.split(self.segmentationFilepath)[1]) ###################### ## partial analysis ## ###################### if self.has_partials: self.partial_analysis()
def standard_concatenate(self): ############################## ## initialise concatenation ## ############################## self.p.logsection("CONCATENATION") self.tgt.setupConcate(self.ops._mixtureDescriptors) self.AnalInterface.done( dataGbLimit=self.ops.DESCRIPTOR_DATABASE_SIZE_LIMIT, dataDayLimit=self.ops.DESCRIPTOR_DATABASE_AGE_LIMIT) distanceCalculations = simcalc.distanceCalculations( self.ops.SUPERIMPOSE, self.ops.RANDOM_SEED, self.AnalInterface, self.tgt.segs, self.p) distanceCalculations.setTarget(self.ops.SEARCH, self.tgt.segs) self.instruments = musicalwriting.instruments( self.ops.INSTRUMENTS, self.ops.CORPUS, self.tgt.segs, self.tgt.lengthInFrames, self.cps.postLimitSegmentNormList, self.AnalInterface.hopLengthSec, self.p) superimp = concatenativeclasses.SuperimposeTracker( self.tgt.lengthInFrames, len(self.tgt.segs), self.ops.SUPERIMPOSE.overlapAmpThresh, self.ops.SUPERIMPOSE.peakAlign, self.ops.SUPERIMPOSE.peakAlignEnvelope, len(self.ops.CORPUS), self.ops.RESTRICT_CORPUS_OVERLAP_BY_STRING, self.p) superimp.setup_subtract(self.ops.SUPERIMPOSE.subtractScale, self.tgt.segs, self.cps.postLimitSegmentNormList) self.cps.setupConcate(self.tgt, self.AnalInterface) self.outputEvents = [] ####################################### ### sort segments by power if needed ## ####################################### if self.ops.SUPERIMPOSE.searchOrder == 'power': self.tgt.segs = sorted(self.tgt.segs, key=operator.attrgetter("power"), reverse=True) else: self.tgt.segs = sorted(self.tgt.segs, key=operator.attrgetter("segmentStartSec")) ################# ## CONCATENATE ## ################# self.p.startPercentageBar(upperLabel="CONCATINATING", total=len(self.tgt.segs) + 1) htmlSelectionTable = [[ 'time x overlap', ]] for sidx, s in enumerate(self.ops.SEARCH): htmlSelectionTable[0].append('spass #%s: %s' % (sidx + 1, s.method)) while False in [t.selectiondone for t in self.tgt.segs]: self.p.percentageBarNext() for segidx, tgtseg in enumerate(self.tgt.segs): if tgtseg.selectiondone == True: continue ############################################################## ## check to see if we are done with this particular segment ## ############################################################## if tgtseg.seek >= tgtseg.lengthInFrames: tgtseg.selectiondone = True continue ######################################## ## run selection superimposition test ## ######################################## tif = tgtseg.segmentStartFrame + tgtseg.seek if tif >= self.tgt.lengthInFrames: tgtseg.selectiondone = True continue timeInSec = self.AnalInterface.f2s(tif) tgtsegdur = tgtseg.segmentDurationSec - self.AnalInterface.f2s( tgtseg.seek) segidxt = superimp.test('segidx', segidx, self.ops.SUPERIMPOSE.minSegment, self.ops.SUPERIMPOSE.maxSegment) overt = superimp.test('overlap', tif, self.ops.SUPERIMPOSE.minOverlap, self.ops.SUPERIMPOSE.maxOverlap) onsett = superimp.test('onset', tif, self.ops.SUPERIMPOSE.minFrame, self.ops.SUPERIMPOSE.maxFrame) trigVal = tgtseg.thresholdTest( tgtseg.seek, self.AnalInterface.tgtOnsetDescriptors) trig = trigVal >= self.tgt.segmentationThresh #################################################### # skip selecting if some criteria doesn't match!!! # #################################################### if 'notok' in [onsett, overt, segidxt]: if segidxt == 'notok': superimp.skip('maximum selections this segment', superimp.cnt['segidx'][segidx], timeInSec) if onsett == 'notok': superimp.skip('maximum onsets at this time', superimp.cnt['onset'][tif], timeInSec) if overt == 'notok': superimp.skip('maximum overlapping selections', superimp.cnt['overlap'][tif], timeInSec) tgtseg.seek += self.ops.SUPERIMPOSE.incr continue # next frame ############################################################## ## see if a selection should be forced without thresholding ## ############################################################## if 'force' not in [onsett, overt, segidxt]: # test for amplitude threshold if not trig: superimp.skip('target too soft', trigVal, timeInSec) tgtseg.seek += self.ops.SUPERIMPOSE.incr continue # not loud enough, next frame ############################## ## get valid corpus handles ## ############################## validSegments = self.cps.evaluateValidSamples( tif, timeInSec, tgtseg.idx, self.ops.ROTATE_VOICES, self.ops.VOICE_PATTERN, self.ops.VOICE_TO_ONSET_MAPPING, self.ops.CLUSTER_MAPPING, tgtseg.classification, superimp, self.instruments) if len(validSegments) == 0: superimp.skip( 'no corpus sounds made it past restrictions and limitations', None, timeInSec) tgtseg.seek += self.ops.SUPERIMPOSE.incr continue distanceCalculations.setCorpus(validSegments) ################################################ ## search and see if we find a winning sample ## ################################################ returnBool = distanceCalculations.executeSearch( tgtseg, tgtseg.seek, self.ops.SEARCH, self.ops.SUPERIMPOSE) if not returnBool: # nothing valid, so skip to new frame... superimp.skip( 'no corpus sounds made it through the search passes', None, timeInSec) tgtseg.seek += self.ops.SUPERIMPOSE.incr maxoverlaps = np.max( superimp.cnt['overlap'][tgtseg.seek:tgtseg.seek + tgtseg.lengthInFrames]) htmlSelectionTable.append([ "%.2fx%i" % (timeInSec, int(maxoverlaps) + 1), ] + distanceCalculations.lengthAtPassesVerbose) continue ################################################### ## if passing this point, picking a corpus sound ## ################################################### superimp.pick(trig, trigVal, onsett, overt, segidxt, timeInSec) selectCpsseg = distanceCalculations.returnSearch() ##################################### ## MODIFY CHOSEN SAMPLES AMPLITUDE ## ##################################### minLen = min(tgtseg.lengthInFrames - tgtseg.seek, selectCpsseg.lengthInFrames) # apply amp scaling sourceAmpScale = util.dbToAmp(self.ops.OUTPUT_GAIN_DB) sourceAmpScale *= util.dbToAmp(selectCpsseg.envDb) ###################$########################### ## subtract power and update onset detection ## ###################$########################### if self.ops.SUPERIMPOSE.calcMethod != None: tgtseg.desc.mixture_subtract(selectCpsseg, sourceAmpScale * superimp.subtract_scalar, minLen, verbose=True) ##################################### ## mix chosen sample's descriptors ## ##################################### if self.ops.SUPERIMPOSE.calcMethod == "mixture": tgtseg.desc.mixture_mix(selectCpsseg, sourceAmpScale, tgtseg.seek, self.ops._mixtureDescriptors) tgtseg.has_been_mixed = True ################################# ## append selected corpus unit ## ################################# transposition = util.getTransposition(tgtseg, selectCpsseg) self.cps.updateWithSelection(selectCpsseg, timeInSec, segidx) cpsEffDur = selectCpsseg.desc.get('effDurFrames-seg') maxoverlaps = np.max(superimp.cnt['overlap'][tif:tif + minLen]) eventTime = (timeInSec * self.ops.OUTPUTEVENT_TIME_STRETCH ) + self.ops.OUTPUTEVENT_TIME_ADD transposition, sourceAmpScale = self.tgt.partial_analysis_cpsseg_winner( selectCpsseg, transposition, sourceAmpScale) oeObj = concatenativeclasses.outputEvent( selectCpsseg, eventTime, util.ampToDb(sourceAmpScale), transposition, superimp.cnt['selectionCount'], tgtseg, maxoverlaps, tgtsegdur, tgtseg.idx, self.ops.CSOUND_STRETCH_CORPUS_TO_TARGET_DUR, self.AnalInterface.f2s(1), self.ops.OUTPUTEVENT_DURATION_SELECT, self.ops.OUTPUTEVENT_DURATION_MIN, self.ops.OUTPUTEVENT_DURATION_MAX, self.ops.OUTPUTEVENT_ALIGN_PEAKS) self.outputEvents.append(oeObj) corpusname = os.path.split( self.cps.data['vcToCorpusName'][selectCpsseg.voiceID])[1] superimp.increment( tif, tgtseg.desc.get('effDurFrames-seg', start=tgtseg.seek), segidx, selectCpsseg, distanceCalculations.returnSearchPassText(), corpusname) self.instruments.increment( tif, tgtseg.desc.get('effDurFrames-seg', start=tgtseg.seek), oeObj) tgtseg.numberSelectedUnits += 1 printLabel = "searching @ %.2f x %i" % (timeInSec, maxoverlaps + 1) printLabel += ' ' * (24 - len(printLabel)) printLabel += "search pass lengths: %s" % (' '.join( distanceCalculations.lengthAtPasses)) self.p.percentageBarNext(lowerLabel=printLabel, incr=0) htmlSelectionTable.append([ "%.2fx%i" % (timeInSec, int(maxoverlaps) + 1), ] + distanceCalculations.lengthAtPassesVerbose) self.p.percentageBarClose(txt='Selected %i events' % len(self.outputEvents)) self.p.maketable(htmlSelectionTable)
def write(self, outputfile, targetSegs, corpusNameList, outputEvents, bachSlotsDict, velocityMapping, tgtStaffType, cpsStaffType, addTarget=True): bs = audioguide_bach_segments(bachSlotsDict, velocityMapping) ######################### ## parse target sounds ## ######################### if addTarget: # using target? bs.add_voice('target', 'target', clef=tgtStaffType) for tobj in targetSegs: skip = tobj.segmentStartSec if hasattr(tobj, 'decomposeSfSkip'): # hack for signal decomposition skip = tobj.decomposeSfSkip bs.add_note('target', tobj.segmentStartSec, tobj.segmentDurationSec, tobj.desc.get('MIDIPitch-seg'), tobj.filename, skip, tobj.soundfileChns, util.ampToDb(tobj.power), tobj.envDb, tobj.envAttackSec, tobj.envDecaySec, tobj.desc, 0, tobj.numberSelectedUnits) ############################### ## add any instrument voices ## ############################### repr_voices = [] if self.active: # using instruments? for v_id, ddict in self.instruments.items(): bs.add_voice( v_id, self.instruments[v_id]['displayname'], clef=self.instruments[v_id]['params'].params['clef']) repr_voices.extend(list(self.instruments[v_id]['cps'].keys())) ########################################## ## add any non-instrument corpus voices ## ########################################## non_instrument_corpus_idxs = [ eobj.voiceID for eobj in outputEvents if eobj.voiceID not in repr_voices ] non_instrument_corpus_idxs = list(set(non_instrument_corpus_idxs)) if len( non_instrument_corpus_idxs ) > 0: # if there are any, add _all_ voices even if they don't have notes. gotta be consistent. for voiceid in non_instrument_corpus_idxs: bs.add_voice("cps%i" % voiceid, util.cpsPathToTrackName(corpusNameList[voiceid]), clef=cpsStaffType) ######################################################## ## loop through all select corpus items and add notes ## ######################################################## for eobj in outputEvents: LINKED_TO_INSTRUMENT = self.active and eobj.selectedinstrument != None if not LINKED_TO_INSTRUMENT: bs.add_note('cps%i' % eobj.voiceID, eobj.timeInScore, eobj.duration, eobj.midi, eobj.filename, eobj.sfSkip, eobj.sfchnls, eobj.rmsSeg, eobj.envDb, eobj.envAttackSec, eobj.envDecaySec, eobj.sfseghandle.desc, eobj.transposition, eobj.simSelects) else: thiscps = self.instruments[eobj.selectedinstrument]['cps'][ eobj.voiceID] # see which dynamic to write if eobj.dynamicFromFilename != None: dyn = eobj.dynamicFromFilename else: dyn = self.instruments[eobj.selectedinstrument]['cps'][ eobj.voiceID]['cobj_to_dyn'][eobj.sfseghandle] bs.add_note( eobj.selectedinstrument, eobj.timeInScore, eobj.duration, eobj.midi, eobj.filename, eobj.sfSkip, eobj.sfchnls, eobj.rmsSeg, eobj.envDb, eobj.envAttackSec, eobj.envDecaySec, eobj.sfseghandle.desc, eobj.transposition, eobj.simSelects, instr_dynamic=dyn, instr_technique=thiscps['technique'], instr_temporal_mode=thiscps['temporal_mode'], instr_articulation=thiscps['articulation'], instr_notehead=thiscps['notehead'], instr_annotation=thiscps['annotation'], ) ################################ ## make a big ol' bach string ## ################################ bachstring = 'roll ' # set up clefs bachstring += "[clefs %s] " % ' '.join( [vd['clef'] for vd in bs.voices_ordered]) # set up voices bachstring += "[voicenames %s] " % ' '.join( [vd['displayname'] for vd in bs.voices_ordered]) # init slots initslots = [] for slotname, slotdict in bs.slots.items(): if slotdict['range'] == False: # no range needed slotdict['range'] = '' elif type(slotdict['range']) == type([]): # range is fixed slotdict['range'] = '[range %f %f]' % tuple(slotdict['range']) elif slotdict['range'] == 'auto': # range needs to be calculated minny, maxxy = min( bs.slots_minmax_lists[slotdict['idx']]), max( bs.slots_minmax_lists[slotdict['idx']]) slotdict['range'] = '[range %f %f] ' % (minny, maxxy) if slotname in [ 'instr_dynamic', 'instr_articulation', 'instr_notehead', 'instr_annotation' ]: continue slotname = slotname.replace('cps_', '') slotname = slotname.replace('instr_', '') initslots.append('[%i [type %s] [name %s] %s]' % (slotdict['idx'], slotdict['type'], slotname, slotdict['range'])) bachstring += '[slotinfo %s]' % ' '.join(initslots) # write notes for voice in bs.voice_to_notes: bachstring += '[ ' # instrument start for time, (notelist, slotDictOnce) in bs.voice_to_notes[voice].items(): bachstring += ' [%i.' % time # note start for didx, d in enumerate(notelist): # only write once slots for first note always = ' '.join(d[3]) if didx == 0: once = ' '.join(slotDictOnce) slotstring = '[slots %s %s ]' % (once, always) else: # already wrote slots on a prev not in this chord slotstring = '[slots %s ]' % (always) bachstring += ' [%i %i %i %s] ' % (d[0], d[1], d[2], slotstring) bachstring += '] ' # note end bachstring += '] ' # instrument end fh = open(outputfile, 'w') fh.write(bachstring) fh.close()
sourceAmpScale = util.dbToAmp(selectCpsseg.postSelectAmpMin) elif sourceAmpScale > util.dbToAmp(selectCpsseg.postSelectAmpMax): sourceAmpScale = util.dbToAmp(selectCpsseg.postSelectAmpMax) else: # nothing sourceAmpScale = 1 # apply amp scaling sourceAmpScale *= util.dbToAmp(ops.OUTPUT_GAIN_DB) sourceAmpScale *= util.dbToAmp(selectCpsseg.envDb) ###################$########################### ## subtract power and update onset detection ## ###################$########################### if ops.SUPERIMPOSE.calcMethod != None: #oneInCorpusLand = (1-cps.powerStats['mean'])/cps.powerStats['stddev'] #normalizationPowerRatio = (oneInCorpusLand*tgt.powerStats['stddev'])+tgt.powerStats['mean'] preSubtractPeak = util.ampToDb(np.max(tgtseg.desc['power'][tgtseg.seek:tgtseg.seek+minLen])) rawSubtraction = tgtseg.desc['power'][tgtseg.seek:tgtseg.seek+minLen]-(selectCpsseg.desc['power'][:minLen]*sourceAmpScale*ops.SUPERIMPOSE.subtractScale) tgtseg.desc['power'][tgtseg.seek:tgtseg.seek+minLen] = np.clip(rawSubtraction, 0, sys.maxsize) # clip it so its above zero postSubtractPeak = util.ampToDb(np.max(tgtseg.desc['power'][tgtseg.seek:tgtseg.seek+minLen])) p.log("\tsubtracted %i corpus frames from target's amplitude -- original peak %.1fdB, new peak %.1fdB"%(minLen, preSubtractPeak, postSubtractPeak)) # recalculate onset envelope SdifDescList, ComputedDescList, AveragedDescList = tgtseg.desc.getDescriptorOrigins() for dobj in ComputedDescList: if dobj.describes_energy and dobj.name != 'power': tgtseg.desc[dobj.name] = descriptordata.DescriptorComputation(dobj, tgtseg, None, None) for d in AveragedDescList: tgtseg.desc[d.name].clear() ##################################### ## mix chosen sample's descriptors ## #####################################