TITLE:: FluidProcessSlices summary:: Utility for batch processing slices categories:: Libraries>FluidCorpusManipulation related:: Classes/FluidLoadFolder, Classes/FluidSliceCorpus,Guides/FluidCorpusManipulation DESCRIPTION:: This class abstracts some of the boilerplate involved in batch processing a sequence of segments in a link::Classes/Buffer:: on the server. It does this by iteratively running a user supplied function and using slice point information passed as an link::Classes/IdentityDictionary:: (see link::Classes/FluidLoadFolder#-index:: for details on the format of this). CLASSMETHODS:: METHOD:: new Creates a new instance ARGUMENT:: featureFunc ANCHOR::featureFunction:: A function that will perform some processing on a section of a buffer. warning:: This function strong::must:: return a link::Classes/UGen:: that sets a code::done:: flag (see link::Classes/Done::), in order for the iteration and housekeeping to work. All code::FluidBuf*:: objects do this. :: The functions is passed the following arguments definitionlist:: ##src || The source link::Classes/Buffer:: containing the audio to process ##start || The start frame of the section to process, in samples ##num || The number of frames to process, in samples ##data || anchor::datadict:: An link::Classes/Association:: of the identifier for this segment, with an link::Classes/IdentityDictionary:: of useful extra data: definitionlist:: ## sr || The original sample rate of the segment ## numchans || The original channel count of the segment ## voice || By default link::#-play:: will run multiple jobs in parallel dependning on the link::#ntasks#tasks:: argument. This contains the task number, which allows you to maintain separate set of resources (e.g. temporary link::Classes/Buffer::s) for each task. ## index || The absolute count of slices processed. :: :: An example function that records statistics about the pitch of a segment in to a link::Classes/FluidDataSet:: could look like code:: ~featureBuffers = 4.do{Buffer.new}; ~avgPitch = { |src,start,num,data| var pitch, stats,statsbuf,identifier,voice; identifier = data.key; voice = data.value[\voice]; statsbuf = LocalBuf(7); pitch = FluidBufPitch.kr(src,start,num,numChans:1,features:~featurebuffers[voice]); stats = FluidBufStats.kr(~featurebuffers[voice],numChans:1, stats:statsbuf,trig:Done.kr(pitch)); FluidDataSetWr.kr(~mydataset, identifier, nil, statsbuf,Done.kr(stats)) }; :: INSTANCEMETHODS:: METHOD:: play Run the link::#featureFunction:: iteratively over segments of a link::Classes/Buffer::, specified by an link::Classes/IdentityDictionary:: ARGUMENT:: server The link::Classes/Server:: on which to process ARGUMENT:: sourceBuffer The source link::Classes/Buffer:: containing the audio to process ARGUMENT:: bufIdx An link::Classes/IdentityDictionary:: specifying identifiers, boundaries, sample rate and channel count for the segment. See link::Classes/FluidLoadFolder#-index:: for details. ARGUMENT:: action A function to run when processing is complete. This gets passed the same link::Classes/Association:: as link::#datadict#the processing function:: ARGUMENT:: tasks ANCHOR::ntasks:: The number of parallel processing tasks to run on the server. Default 4. This should probably never be greater than the number of available CPU cores. METHOD:: featureFunc Return the function uses by this instance. EXAMPLES:: code:: s.reboot; //Load all the Fluid Corpus Manipulation audio files ( ~path = FluidFilesPath(); ~loader = FluidLoadFolder(~path); ~loader.play(s,action:{ |dataDictionary| "Done loading".postln}); ~slicer = FluidSliceCorpus({ |src,start,num,dest| FluidBufOnsetSlice.kr(src,start,num,indices:dest, threshold:2) }); ~pitchdata = FluidDataSet(s); ~pitchbufs = 4.collect{Buffer.new}; ~statsbufs = 4.collect{Buffer.new}; ) //segment ~slicer.play(s,~loader.buffer,~loader.index,{|dataDictionary| "Slicing done".postln;}); //In the interests of brevity, let's just take a subset of the slices and process these ~subset = IdentityDictionary.newFrom(~slicer.index.asSortedArray[0..7].flatten(1)); //write pitch statistics into a dataset //definte the extraction function... ( ~extractor = FluidProcessSlices({|src,start,num,data| var pitch, stats, identifier,i; i = data.value[\voice]; identifier = data.key; pitch = FluidBufPitch.kr(src,start,num,features:~pitchbufs[i]); stats = FluidBufStats.kr(~pitchbufs[i],stats:~statsbufs[i],trig:Done.kr(pitch)); FluidDataSetWr.kr(~pitchdata,identifier,nil,buf:~statsbufs[i],trig:Done.kr(stats)) }); ) //... and run it ~extractor.play(s,~loader.buffer,~subset,{"Feature extraction done".postln}); //view the data ~pitchdata.print ::