├── AdditionalExamples └── SCMIRMusicPredictor │ ├── KMeansZArchive.sc │ ├── SCMIRMusicPredictor.html │ ├── SCMIRMusicPredictor.sc │ └── SCMIRMusicPredictorComplexity.sc ├── COPYING ├── InstallLinux.txt ├── InstallMac.txt ├── README.txt ├── SCMIRExtensions ├── Classes │ ├── SCMIR.sc │ ├── SCMIRAudioFile.sc │ ├── SCMIRAudioFileBeatTracking.sc │ ├── SCMIRAudioFileDiffSum.sc │ ├── SCMIRAudioFileNormalization.sc │ ├── SCMIRAudioFileNovelty.sc │ ├── SCMIRAudioFileOnsets.sc │ ├── SCMIRAudioFileParallelExtraction.sc │ ├── SCMIRAudioFilePlotting.sc │ ├── SCMIRAudioFileSimilarity.sc │ ├── SCMIRBatchExtract.sc │ ├── SCMIRFile │ │ ├── SCMIRFile.sc │ │ ├── SCMIRMP3.sc │ │ ├── SCMIRZArchive.sc │ │ └── SCMIRZArchiveSupport.sc │ ├── SCMIRLearning │ │ ├── GMM.sc │ │ ├── HMM.sc │ │ ├── KNearestNeighbour.sc │ │ ├── MarkovModel.sc │ │ ├── MarkovPool.sc │ │ ├── NaiveBayes.sc │ │ ├── NeuralNet.sc │ │ ├── PPMC.sc │ │ ├── PPMCOnline.sc │ │ ├── SARDNET.sc │ │ ├── SOM.sc │ │ ├── VOGUE.sc │ │ └── VariableGapSequenceMining.sc │ ├── SCMIRLive.sc │ ├── SCMIRScore.sc │ ├── SCMIRSimilarityMatrix.sc │ ├── SCMIRSimilarityMatrixDTW.sc │ ├── SCMIRSimilarityMatrixNovelty.sc │ ├── SCMIRString.sc │ └── SCMIRStructuralSummary.sc ├── HelpSource │ └── Classes │ │ ├── GMM.schelp │ │ ├── HMM.schelp │ │ ├── KNearestNeighbour.schelp │ │ ├── MarkovModel.schelp │ │ ├── MarkovPool.schelp │ │ ├── NaiveBayes.schelp │ │ ├── NeuralNet.schelp │ │ ├── PPMC.schelp │ │ ├── SARDNET.schelp │ │ ├── SCMIR.schelp │ │ ├── SCMIRAudioFile.schelp │ │ ├── SCMIRLive.schelp │ │ ├── SCMIRSimilarityMatrix.schelp │ │ ├── SOM.schelp │ │ └── VOGUE.schelp └── examples │ ├── artistrecognitionviaNN.scd │ ├── batchextractinparallel.scd │ ├── customfeature.scd │ ├── differentiatesoundclasseslive.scd │ ├── genrerecognition.scd │ ├── gmmtest.scd │ ├── interfilesimilaritycomparison.scd │ ├── lowfreqpoweronsetsonly.scd │ ├── simpleclustering.scd │ ├── vamppluginsviasonicannotator.scd │ └── withweka.scd └── Source ├── CMakeLists.txt ├── NeuralNet ├── ffnet.cpp ├── ffnet.h └── main.cpp ├── gmm ├── Macros.cpp ├── Macros.h ├── MathLib.h ├── Matrix.cpp ├── Matrix.h ├── README ├── Vector.cpp ├── Vector.h ├── gmr.cpp ├── gmr.h └── main.cpp ├── hmm ├── dhmm.cpp ├── dhmm.h └── main.cpp ├── noveltycurve └── noveltycurve.cpp └── similaritymatrix └── similarity.cpp /AdditionalExamples/SCMIRMusicPredictor/KMeansZArchive.sc: -------------------------------------------------------------------------------- 1 | + KMeans { 2 | 3 | save {|filename| 4 | var a; 5 | 6 | filename = filename?? {SCMIR.tempdir++"KMeans"++".scmirZ"}; 7 | 8 | a = SCMIRZArchive.write(filename); 9 | 10 | a.writeItem(k); 11 | a.writeItem(data); 12 | a.writeItem(centroids); 13 | a.writeItem(assignments); 14 | 15 | a.writeClose; 16 | 17 | } 18 | 19 | 20 | load {|filename| 21 | 22 | var a; 23 | 24 | filename = filename?? {SCMIR.tempdir++"KMeans"++".scmirZ"}; 25 | 26 | a = SCMIRZArchive.read(filename); 27 | 28 | k = a.readItem; 29 | data = a.readItem; 30 | centroids = a.readItem; 31 | assignments = a.readItem; 32 | 33 | a.close; 34 | 35 | } 36 | 37 | } -------------------------------------------------------------------------------- /AdditionalExamples/SCMIRMusicPredictor/SCMIRMusicPredictor.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 19 | 20 | 21 |

SCMIRMusicPredictor

22 |


23 |

//As used in the algorithmic jury and early electronic dance music corpus analysis projects

24 |

//

25 |

//Trains up a three part model (timbre, rhythms, summed harmonic change) over a corpus. The model can then be tested on a target piece to see how well it predicts it. 

26 |

//

27 |

//Note that scores are relative to a given model. 

28 |


29 |

//REQUIREMENTS: you need to set some directories in the *initClass class method. 

30 |

//depends on KMeans from Quarks, and KMeansZArchive extension class for save and load

31 |


32 |

(

33 |

~corpus = [ "/data/audio/mirdata/EDM/Derrick May Innovator Disc 1/A Rest_Beyond Kaos.wav", "/data/audio/mirdata/EDM/Derrick May Innovator Disc 1/Another Kaos Beyond Kaos.wav", "/data/audio/mirdata/EDM/Derrick May Innovator Disc 1/Beyond The Dance - The Cult Mix.wav", "/data/audio/mirdata/EDM/Derrick May Innovator Disc 1/Daymares, It Is What It Is.wav", "/data/audio/mirdata/EDM/Derrick May Innovator Disc 1/Emanon Begins.wav", "/data/audio/mirdata/EDM/Derrick May Innovator Disc 1/Feel Surrreal Ends The Feel Surreal.wav", "/data/audio/mirdata/EDM/Derrick May Innovator Disc 1/Freestyle.wav", "/data/audio/mirdata/EDM/Derrick May Innovator Disc 1/R-Theme.wav", "/data/audio/mirdata/EDM/Derrick May Innovator Disc 1/Sinister.wav", "/data/audio/mirdata/EDM/Derrick May Innovator Disc 1/Some More Spaced Out.wav", "/data/audio/mirdata/EDM/Derrick May Innovator Disc 1/Spaced Out.wav", "/data/audio/mirdata/EDM/Derrick May Innovator Disc 1/Strings Of The Strings Of Life.wav", "/data/audio/mirdata/EDM/Derrick May Innovator Disc 1/The Dance.wav", "/data/audio/mirdata/EDM/Derrick May Innovator Disc 1/The End.wav", "/data/audio/mirdata/EDM/Derrick May Innovator Disc 2/A Relic Long Ago.wav", "/data/audio/mirdata/EDM/Derrick May Innovator Disc 2/Drama.wav", "/data/audio/mirdata/EDM/Derrick May Innovator Disc 2/Dreams Of Dreamers.wav", "/data/audio/mirdata/EDM/Derrick May Innovator Disc 2/Emanon Ends.wav", "/data/audio/mirdata/EDM/Derrick May Innovator Disc 2/Icon (Montage Mix).wav", "/data/audio/mirdata/EDM/Derrick May Innovator Disc 2/Kaotic Harmony.wav", "/data/audio/mirdata/EDM/Derrick May Innovator Disc 2/Nude Photo.wav", "/data/audio/mirdata/EDM/Derrick May Innovator Disc 2/Phantom Lurks.wav", "/data/audio/mirdata/EDM/Derrick May Innovator Disc 2/Phantom.wav", "/data/audio/mirdata/EDM/Derrick May Innovator Disc 2/Salsa Life.wav", "/data/audio/mirdata/EDM/Derrick May Innovator Disc 2/Strings Of Life.wav", "/data/audio/mirdata/EDM/Derrick May Innovator Disc 2/The Beginning.wav", "/data/audio/mirdata/EDM/Derrick May Innovator Disc 2/To Be Or Not To Be.wav", "/data/audio/mirdata/EDM/Derrick May Innovator Disc 2/Wiggin (Juan's Mix).wav", "/data/audio/mirdata/EDM/Derrick May Innovator Disc 2/Winter On The Blvd.wav" ];

34 |

)

35 |


36 |


37 |


38 |

m = SCMIRMusicPredictor("",~corpus); 

39 |


40 |


41 |

//first train up on corpus; about twice realtime speed, so can take some hours if you have hours of audio! 

42 |

(

43 |

{

44 |

m.prepare();

45 |

}.fork;

46 |

)

47 |


48 |


49 |

//use model to predict a piece, 4 scores result= timbre predictive score, npvi score (unused in actual projects mentioned above), predicted IOI score, predicted harmonic change score

50 |


51 |

(

52 |

{

53 |

~results = m.testPiece("/data/audio/mirdata/EDM/Derrick May Innovator Disc 1/A Rest_Beyond Kaos.wav");

54 |

}.fork;

55 |

)

56 |


57 |


58 |


59 |


60 |


61 | 62 | 63 | -------------------------------------------------------------------------------- /AdditionalExamples/SCMIRMusicPredictor/SCMIRMusicPredictorComplexity.sc: -------------------------------------------------------------------------------- 1 | //required in order to assess how difficult particular sequences are to predict, to counteract 2 | //simple things getting high average logloss 3 | 4 | + SCMIRMusicPredictor { 5 | 6 | //don't apply probabilitic model, extract token sequences and assess LZW compression 7 | //via RedSys Quark for RedLZW 8 | 9 | complexity {|filename| 10 | 11 | var score = 0.0; 12 | var g,f; 13 | var classes; 14 | var numtimbrefeatures = timbrefeatures.size; 15 | var outputcomplexity = 0!3; 16 | var temp, temp2; 17 | var numonsets,onsetdata; 18 | var iois; 19 | 20 | SCMIR.loadGlobalFeatureNorms(normdir++"timbrefeaturenorms.scmirZ"); 21 | 22 | g = SCMIRAudioFile(filename,timbrefeatures, start:filestart,dur:filedur); 23 | 24 | //uses global normalization 25 | g.extractFeatures(true, true); 26 | g.extractBeats(); 27 | 28 | g.gatherFeaturesByBeats; 29 | 30 | classes = Array.fill(g.featuredata.size.div(numtimbrefeatures),{|j| 31 | var indexnow = j*numtimbrefeatures; 32 | 33 | timbrekmeans.classify(g.featuredata.copyRange(indexnow,indexnow+numtimbrefeatures-1)); 34 | 35 | }); 36 | 37 | temp2= RedLZW.compress(classes); 38 | //what is compression reduction? 39 | outputcomplexity[0] = temp2.size/classes.size; 40 | 41 | f = SCMIRAudioFile(filename,start:filestart,dur:filedur); 42 | f.extractOnsets(); 43 | 44 | numonsets = f.numonsets; 45 | onsetdata = f.onsetdata; 46 | 47 | //nPVI 48 | iois = Array.fill(numonsets-1,{|i| onsetdata[i+1] - onsetdata[i]; }); 49 | 50 | if(iois.size<1) {iois = [filedur]}; 51 | 52 | //PPM-C over IOIs 53 | classes = iois.collect{|ioi| this.classifyIOI(ioi); }; 54 | 55 | temp2= RedLZW.compress(classes); 56 | 57 | outputcomplexity[1] = temp2.size/classes.size; 58 | 59 | SCMIR.loadGlobalFeatureNorms(normdir++"pitchfeaturenorms.scmirZ"); 60 | 61 | f = SCMIRAudioFile(filename,pitchfeatures,start:filestart,dur:filedur); 62 | f.extractFeatures(true,true); 63 | f.extractBeats(); 64 | f.gatherFeaturesByBeats; 65 | f.differentiateFeature(0,1); //absolute difference 66 | f.sumMultiFeature(0); 67 | 68 | classes = f.featuredata.collect{|diff| this.classifyChromaDiff(diff); }; 69 | 70 | temp2= RedLZW.compress(classes); 71 | 72 | outputcomplexity[2] = temp2.size/classes.size; 73 | 74 | ^outputcomplexity; 75 | } 76 | 77 | 78 | 79 | 80 | } -------------------------------------------------------------------------------- /InstallLinux.txt: -------------------------------------------------------------------------------- 1 | Installation on Linux: 2 | 3 | 4 | Advice from a more recent attempt to build: 5 | 6 | you may also want to update the installLinux file with 2 small things: 7 | the line 8 | cp -r /path/to/SCMIR0.9/SCMIRExtensions/SCMIR ~/.local/share/SuperCollider/Extensions/ 9 | should now be 10 | cp -r /path/to/SCMIR/SCMIRExtensions ~/.local/share/SuperCollider/Extensions/ 11 | 12 | You can also put a comments that when checking where libgsl is installed in the system: 13 | ld -lgsl --verbose 14 | users will see warning about missing cblas which is confusing but not material. 15 | Oded 16 | 17 | 18 | linux building: 19 | Hello, 20 | I think I solved the problems. If you want to update the linux install file here is some additional information. in the CMakeList file the part about LIBGSL the correct location may depend on the individual system. For me this library was installed in /lib64 21 | It seems that a good way to find the correct version is through: 22 | ld -lgsl --verbose 23 | 24 | Then I also needed to change libgsl.a to libgsl.so (and the same for libgslcblas). 25 | The final problem was that atoi needed to include stdlib.h (I presume on the Mac it is in stdio.h) 26 | 27 | Oded Ben-Tal 28 | 29 | 30 | 31 | Adapted from: http://new-supercollider-mailing-lists-forums-use-these.2681727.n2.nabble.com/SCMIR-SuperCollider-Music-Information-Retrieval-0-9-td7580724.html 32 | 33 | Some steps from that post already resolved, omitted from below as FIXED from 0.91 on in core already, no action required. 34 | 35 | 36 | From: Martin Marier 37 | Date: 19 July 2012 18:31:25 GMT+01:00 38 | To: Click Nilson , sc-users@lists.bham.ac.uk 39 | Subject: Re: [sc-users] SCMIR: SuperCollider Music Information Retrieval 0.9 40 | 41 | Hello Nick, 42 | 43 | Thank you for this great work. I plan on using SCMIR extensively in the 44 | future. 45 | 46 | For those interested, I managed to get this working under Ubuntu 12.04. 47 | Here is how to do it: 48 | 49 | 1. Get the UGens from the sc3-plugins repository. They compiled as is. 50 | 2. Get the SCMIR zip file. ( http://www.sussex.ac.uk/Users/nc81/code/SCMIR.zip ) 51 | 3. Install the debian package libgsl0-dev: 52 | 53 | sudo apt-get install libgsl0-dev 54 | 55 | 4. Modify the SCMIR0.9/SCMIRExtensions/SCMIR/Source/CMakeLists.txt file: 56 | a. These two lines: 57 | 58 | #set to locations of your gsl headers and library folders 59 | set(LIBGSL /Users/nickcollins/Desktop/tosort/gsl_universal_1.14/libs) 60 | include_directories(/Users/nickcollins/Desktop/tosort/gsl_universal_1.14/gsl) 61 | 62 | Were changed to this: 63 | 64 | #set to locations of your gsl headers and library folders 65 | set(LIBGSL /usr/lib) 66 | include_directories(/usr/include) 67 | 68 | 5. Compile the executables: 69 | 70 | cd /path/to/SCMIR0.9/SCMIRExtensions/SCMIR/Source 71 | mkdir build 72 | cd build 73 | cmake .. 74 | make 75 | make install 76 | 77 | 6. Install in your Extensions directory: 78 | 79 | cp -r /path/to/SCMIR0.9/SCMIRExtensions/SCMIR ~/.local/share/SuperCollider/Extensions/ 80 | 81 | 7. Recompile the SC class library. 82 | 83 | 9. Open the examples and try them (you will have modify the sound file 84 | paths, of course). 85 | 86 | 87 | I hope this will help people who want to try SCMIR under Linux. Thanks again, Nick. 88 | 89 | Cheers, 90 | 91 | Martin 92 | 93 | -------------------------------------------------------------------------------- /InstallMac.txt: -------------------------------------------------------------------------------- 1 | Installation on Mac: 2 | 3 | Put the folder SCMIRExtensions in your extensions directory. If you need hints on how to do that, see the [Using-Extensions] help file in SuperCollider (also linked in Main Help) 4 | 5 | You also need the SCMIRUGens from sc3-plugins (https://github.com/supercollider/sc3-plugins); these also go in your extensions directory. 6 | 7 | 8 | Compilation: Pre-compiled executables for Mac for Intel and M-series arm chips are now provided here: 9 | composerprogrammer.com/code/scmirexec.zip 10 | Put the executables for your machine type in a folder 'scmirexec' within SCMIRExtensions 11 | 12 | You can also compile the executables yourself using CMake. 13 | 14 | If you don't have Cmake installed, you can get it from MacPorts, brew etc. 15 | 16 | cd /path/to/SCMIR/Source 17 | mkdir build 18 | cd build 19 | cmake -DCMAKE_OSX_ARCHITECTURES='i386;x86_64' .. 20 | # for M-chip Macs use cmake -DCMAKE_OSX_ARCHITECTURES='arm64' .. 21 | make 22 | make install 23 | -------------------------------------------------------------------------------- /SCMIRExtensions/Classes/SCMIRAudioFileDiffSum.sc: -------------------------------------------------------------------------------- 1 | //experimental code for differences and sums over multifeatures 2 | 3 | + SCMIRAudioFile { 4 | 5 | 6 | //acts in place, directly on data 7 | //first discrete derivative by default 8 | differentiateFeature {|featurelist=0,type=0| 9 | 10 | var featurecounts = this.resolveFeatureNumbers; 11 | var numfeaturegroups = featureinfo.size; 12 | 13 | if(featurelist.size==0) {featurelist = [featurelist]}; 14 | 15 | featurelist.do {|which| 16 | 17 | var startindex = featurecounts[which][0]; 18 | var groupsize = featurecounts[which][1]; 19 | var top = groupsize - 1; 20 | var temp=0; 21 | var valthen = 0!groupsize; 22 | 23 | //always zero out first slots as no difference at start (to avoid massive starting transient) 24 | 25 | for(0,top,{|j| 26 | valthen[j] = featuredata[startindex+j]; 27 | featuredata[startindex + j] = 0; 28 | }); 29 | 30 | 31 | temp = numfeatures + startindex; 32 | 33 | for(2,numframes-1,{|i| 34 | 35 | for(0,top,{|j| 36 | 37 | var val = featuredata[temp+j]; 38 | var diff; 39 | 40 | //valthen[j] = data[temp+j-numfeatures]; 41 | 42 | diff = val - (valthen[j]); 43 | 44 | //only positive increases 45 | if(type==2) {diff= max(0,diff);}; 46 | 47 | //just size of change 48 | if(type==1) {diff= diff.abs;}; 49 | 50 | valthen[j] = val; 51 | 52 | featuredata[temp+j] = diff; 53 | 54 | }); 55 | 56 | temp = temp+ numfeatures; 57 | }); 58 | 59 | 60 | }; 61 | 62 | } 63 | 64 | 65 | 66 | sumMultiFeature {|which| 67 | 68 | var featurecounts = this.resolveFeatureNumbers; 69 | var numfeaturegroups = featureinfo.size; 70 | var groupindex = featurecounts[which][0]; 71 | var groupsize = featurecounts[which][1]; 72 | var newfeaturedata; 73 | var correction = 1 - groupsize; 74 | var newnumfeatures = numfeatures +correction; 75 | 76 | newfeaturedata = FloatArray.newClear(numframes*newnumfeatures); 77 | 78 | for(0,numframes-1,{|i| 79 | 80 | var base = i*newnumfeatures; 81 | var base2 = i*numfeatures; 82 | var sum; 83 | 84 | if(groupindex>0) { 85 | newfeaturedata[base..(base+groupindex-1)] = featuredata[base2..(base2+groupindex-1)] 86 | }; 87 | 88 | 89 | sum = featuredata[(base2+groupindex)..(base2+groupindex+groupsize-1)].sum; 90 | 91 | newfeaturedata[base+groupindex] = sum; 92 | 93 | if(groupindex<(numfeaturegroups-1)) { 94 | newfeaturedata[(base+groupindex+1)..(base+newnumfeatures-1)] = featuredata[(base2+(featurecounts[which+1]))..(base2+numfeatures-1)] 95 | }; 96 | 97 | 98 | }); 99 | 100 | featuredata = newfeaturedata; 101 | numfeatures = newnumfeatures; 102 | 103 | featureinfo[which] = [\Summed]; 104 | 105 | } 106 | 107 | 108 | 109 | 110 | 111 | 112 | } -------------------------------------------------------------------------------- /SCMIRExtensions/Classes/SCMIRAudioFileNovelty.sc: -------------------------------------------------------------------------------- 1 | //create novelty curve from similarity matrix (only for self similarity) 2 | //Foote method is checkerboard kernel; may need to take account of different separations 3 | //from 1 to 3 seconds say 4 | 5 | + SCMIRAudioFile { 6 | 7 | novelty {|matrix, kernelsize=10| 8 | 9 | ^matrix.novelty(kernelsize); 10 | } 11 | 12 | 13 | noveltyOld {|matrix, kernelsize=10| 14 | 15 | var file; 16 | var temp; 17 | var numcols; //should be exact 18 | var noveltycurve; 19 | var data; 20 | var outputfilename, inputfilename; 21 | 22 | data = matrix.matrix; 23 | 24 | if (matrix.isNil){ 25 | "SCMIRAudioFile:novelty: no similarity matrix passed in".postln; ^nil 26 | }; 27 | 28 | if (matrix.reducedcolumns != matrix.reducedrows){ 29 | "SCMIRAudioFile:novelty: similiarity matrix not square".postln; ^nil 30 | }; 31 | 32 | numcols = matrix.reducedcolumns; //should be same as reducedrows //matrix.size.sqrt.asInteger; 33 | 34 | inputfilename = SCMIR.getTempDir ++ "noveltyinput"; 35 | outputfilename = SCMIR.getTempDir ++ "noveltyoutput"; 36 | 37 | file = SCMIRFile(inputfilename,"wb"); 38 | 39 | file.putInt32LE(data.size); 40 | file.putInt32LE(numcols); 41 | 42 | file.writeLE(data); 43 | 44 | file.close; 45 | 46 | temp = ((SCMIR.executabledirectory)++"noveltycurve") + kernelsize + inputfilename + outputfilename; //"noveltyinput"+ "noveltyoutput"; 47 | 48 | //unixCmd(temp); 49 | //SCMIR.processWait("noveltycurve"); 50 | 51 | SCMIR.external(temp); 52 | 53 | file = SCMIRFile(outputfilename,"rb"); 54 | 55 | noveltycurve= FloatArray.newClear(numcols); 56 | 57 | //file.readLE(noveltycurve) ; 58 | 59 | numcols.do{|j| 60 | noveltycurve[j] = file.getFloatLE; 61 | }; 62 | 63 | 64 | file.close; 65 | 66 | ^noveltycurve; 67 | 68 | 69 | } 70 | 71 | 72 | //must occur within a Routine 73 | findSections {|metric=0, unit= 10, kernelsize= 43| 74 | 75 | var matrix, noveltycurve; 76 | var list, detectioncurve; 77 | var convertedtimes, conversion; 78 | //features are every 1024 samples at 44.1kHz 79 | //(1024/44100)*10 = 0.23219954648526 80 | 81 | conversion = (SCMIR.framerate.reciprocal)*unit; //(1024.0/44100)*unit; 82 | 83 | matrix = this.similarityMatrix(unit, metric); 84 | 85 | //taking account of about 10 second window around a given point 86 | noveltycurve = this.novelty(matrix,kernelsize); 87 | 88 | //could add values in to remove zeroes here 89 | 90 | #list,detectioncurve= SCMIR.peakPick(noveltycurve.normalize); 91 | 92 | //remove any from list within kernelsize of start or end, to avoid artefact jump from zeroes 93 | 94 | list = list.select{|val| (val>kernelsize) && (val<(noveltycurve.size-kernelsize))}; 95 | 96 | 97 | convertedtimes = if(featuresbysegments) { 98 | 99 | //list has beat times, look up in beatdata 100 | //list.collect{|listtime| 101 | // var index; 102 | // index =listtime*unit; 103 | // 104 | // if(index>=numbeats,{index = numbeats-1}); 105 | // 106 | // [index,listtime, index].postln; 107 | // beatdata[index]; } 108 | 109 | 110 | //list has beat times, look up in beatdata 111 | list.collect{|listtime| 112 | var index; 113 | index =listtime*unit; 114 | 115 | if(index>=numsegments,{index = numsegments-1}); 116 | 117 | //[index,listtime, index].postln; 118 | segmenttimes[index]; } 119 | 120 | 121 | } {list*conversion}; 122 | 123 | ^(convertedtimes); //convert to actual times in seconds (accurate only roughly) 124 | } 125 | 126 | 127 | } 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | -------------------------------------------------------------------------------- /SCMIRExtensions/Classes/SCMIRBatchExtract.sc: -------------------------------------------------------------------------------- 1 | 2 | + SCMIR { 3 | 4 | //classvar outputbackup; 5 | 6 | //run normalization procedures for all standard features over all filenames in list, in parallel 7 | *findGlobalFeatureNormsParallel {|filenamelist, featureinfo, normalizationtype=0,filestart=0,filedur=0,numquantiles=10,whichchannel| 8 | 9 | SCMIR.batchExtractParallel(filenamelist,featureinfo,true, false, normalizationtype,whichchannel, segmentations:[nil], oncompletion:{|durations, output, framesum| 10 | 11 | //to remove array of segmentations since didn't need it 12 | output = output.collect{|val| val[0]}; 13 | 14 | SCMIR.findGlobalFeatureNorms([0,output,durations,framesum],nil,normalizationtype,numquantiles:numquantiles); 15 | 16 | },normstats:true,filestart:filestart,filedur:filedur); 17 | 18 | } 19 | 20 | 21 | 22 | *batchExtractParallel {|filenames, features, normalize=true, useglobalnormalization=false,normalizationmode=0, whichchannel, segmentations, oncompletion, normstats=false,filestart=0,filedur=0| 23 | 24 | var spawnNRT; 25 | var pid; 26 | var numactive = 0; 27 | var maxactive = SCMIR.numcpus; 28 | var numtorender; 29 | var numscheduled = 0; 30 | var numrendered = 0; 31 | var time = Main.elapsedTime; 32 | var increment = 0; 33 | var outputdata; 34 | var durations; 35 | var framesum; 36 | 37 | 38 | [\normstats,normstats].postln; 39 | 40 | 41 | framesum = 0; //needed for some normalisation operations, so just calculate it anyway 42 | 43 | segmentations = segmentations ?? {[[[0.0],0]]}; 44 | 45 | //summary types 46 | //0 mean, 1 max, 2 min, 3 stddev 47 | 48 | 49 | numtorender = filenames.size; 50 | 51 | //if(saveoutput) { 52 | outputdata = {}!numtorender; 53 | //}; 54 | durations = {}!numtorender; 55 | 56 | 57 | spawnNRT = {|action, whichspawn| 58 | 59 | var e; 60 | var whichnow = whichspawn -1; 61 | 62 | //[\spawnNRT,whichspawn,~filenames[whichspawn-1]].postln; 63 | 64 | //e = SCMIRAudioFile(Platform.resourceDir +/+ "sounds/a11wlk01.wav", [[MFCC, 13], [Chromagram, 12]]); 65 | 66 | e = SCMIRAudioFile(filenames[whichnow], features, normalizationmode,filestart,filedur); 67 | 68 | durations[whichnow] = e.duration; 69 | 70 | //shortcut versions also work, defaults will be applied for MFCC (10 coeffs) and Chromagram (12TET) 71 | //e = SCMIRAudioFile(Platform.resourceDir +/+"sounds/a11wlk01.wav",[MFCC,Chromagram]); 72 | 73 | e.extractFeaturesParallel(normalize,useglobalnormalization,whichchannel,{|result, pid, saf| 74 | 75 | //[result,pid].postln; 76 | 77 | framesum = framesum + e.numframes; 78 | 79 | //if(saveoutput) { 80 | 81 | outputdata[whichnow] = segmentations.collect{|segmentation| var segments,summarytype; 82 | 83 | //return raw feature data if nil, else find an average or other stat 84 | if(segmentation.isNil) {e.featuredata; } { 85 | segments = segmentation[0]; summarytype = segmentation[1]; e.gatherFeaturesBySegments(segments,false,summarytype); 86 | }; 87 | 88 | }; 89 | 90 | 91 | action.(result, pid, saf); 92 | 93 | 94 | // }; 95 | 96 | [\finished,whichspawn, \after, Main.elapsedTime - time].postln; 97 | 98 | },whichspawn,normstats); 99 | 100 | }; 101 | 102 | 103 | 104 | { 105 | 106 | //var accumulatepids = List[]; 107 | 108 | while({numscheduled=numtorender) 130 | //if(numrendered == numtorender) 131 | { 132 | 133 | [\totaltime, Main.elapsedTime - time].postln; 134 | oncompletion.(durations,outputdata, framesum); 135 | }; 136 | 137 | 138 | },numscheduled) }); 139 | 140 | 0.01.wait; 141 | //1.wait; 142 | }); 143 | 144 | 145 | }.fork; 146 | 147 | } 148 | 149 | 150 | 151 | } 152 | 153 | 154 | 155 | 156 | 157 | -------------------------------------------------------------------------------- /SCMIRExtensions/Classes/SCMIRFile/SCMIRFile.sc: -------------------------------------------------------------------------------- 1 | //to cope with binary files in Little and Big Endian, e.g., Intel or PPC 2 | 3 | SCMIRFile : File { 4 | 5 | //for auxilliary operations 6 | classvar <>littleendian; //0 = little endian (Intel) = default 7 | 8 | *initClass { 9 | 10 | littleendian = true; 11 | 12 | } 13 | 14 | *new { arg pathName, mode; 15 | ^super.new(pathName, mode).initSCMIRFile(pathName, mode); 16 | } 17 | 18 | initSCMIRFile {|path1,mode1| 19 | 20 | //could try multiple times? 21 | if(not(this.isOpen)) { 22 | 23 | //try again 24 | this.close; 25 | 26 | SCMIR.clearResources; 27 | 28 | this.open(path1,mode1); 29 | 30 | } 31 | } 32 | 33 | 34 | 35 | //return boolean based on reading of LE binary file with one entry; file created dynamically by tiny test C executable 36 | *testEndianness { 37 | var def, ugenindex; 38 | var testfilename, duration= 0.1; 39 | var auxfile, serveroptions; 40 | var temp, score; 41 | 42 | //test via FeatureSave 43 | def = SynthDef(\SCMIREndianTest,{ 44 | 45 | FeatureSave.kr(SinOsc.ar(0,0.5pi), Impulse.kr(1)); 46 | 47 | }); 48 | 49 | //find synth index for FeatureSave 50 | 51 | def.children.do{|val,i| if(val.class==FeatureSave,{ugenindex = val.synthIndex})}; 52 | def.writeDefFile; 53 | 54 | testfilename = "endiantest.data"; 55 | 56 | score = [ 57 | [0.0, [ \s_new, \SCMIREndianTest, 1000, 0, 0]], 58 | [0.01,[\u_cmd, 1000, ugenindex, "createfile",testfilename]], //can't be at 0.0, needs allocation time for synth before calling u_cmd on it 59 | [duration,[\u_cmd, 1000, ugenindex, "closefile"]], 60 | [duration, [\c_set, 0, 0]] 61 | ]; 62 | 63 | serveroptions = ServerOptions.new; 64 | serveroptions.numOutputBusChannels = 1; // mono output 65 | 66 | Score.recordNRTSCMIR(score,SCMIR.nrtanalysisfilename,SCMIR.nrtoutputfilename, nil,44100, "WAV", "int16", serveroptions); // synthesize 67 | 68 | //LOAD FEATURES 69 | //Have to be careful; Little Endian is standard for Intel processors 70 | auxfile = File(testfilename,"rb"); 71 | 72 | auxfile.getInt32LE; 73 | 74 | //[\numframes,numframes].postln; 75 | 76 | temp = auxfile.getInt32LE; 77 | temp = (1==temp); 78 | 79 | auxfile.close; 80 | 81 | ^temp; 82 | } 83 | 84 | *setEndianness { 85 | 86 | littleendian = SCMIRFile.testEndianness(); 87 | 88 | } 89 | 90 | 91 | //only these calls used by SCMIR, so only ones overwritten 92 | getInt32LE { ^if(littleendian){super.getInt32LE}{super.getInt32} } 93 | getFloatLE { ^if(littleendian){super.getFloatLE}{super.getFloat} } 94 | getDoubleLE { ^if(littleendian){super.getDoubleLE}{super.getDouble} } 95 | putInt32LE {arg anInteger; ^if(littleendian){super.putInt32LE(anInteger)}{super.putInt32(anInteger)} } 96 | putFloatLE {arg aFloat; ^if(littleendian){super.putFloatLE(aFloat)}{super.putFloat(aFloat)} } 97 | readLE {arg buffer; ^if(littleendian){super.readLE(buffer)}{super.read(buffer)} } 98 | writeLE {arg item; ^if(littleendian){super.writeLE(item)}{super.write(item)} } 99 | 100 | 101 | } 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | -------------------------------------------------------------------------------- /SCMIRExtensions/Classes/SCMIRFile/SCMIRMP3.sc: -------------------------------------------------------------------------------- 1 | //adapts code from Dan Stowell's MP3 Quark, readToBuffer function, simplified and recast using SCMIR constructions. Similar assumption on lame location, hard coded here 2 | 3 | + SCMIRAudioFile { 4 | 5 | 6 | //nonunique added to save disk space when working over a big corpus, only creates one wav at a time 7 | resolveMP3 {|inputfilename, usehash=false, nonunique=true| 8 | 9 | //"/tmp/ 10 | var outputfilename; 11 | var inputprefix; 12 | 13 | inputprefix = PathName(inputfilename).fileNameWithoutExtension; 14 | 15 | outputfilename = SCMIR.tempdir ++ inputprefix ++ ".wav"; 16 | 17 | if(usehash) { 18 | outputfilename = SCMIR.tempdir ++ "sc3mp3read-" ++ (this.hash) ++ ".wav"; 19 | }; 20 | 21 | if(nonunique) { 22 | outputfilename = SCMIR.tempdir ++ "conversionfrommp3num" ++ (SCMIR.mp3tempindex) ++ ".wav"; 23 | SCMIR.mp3tempindex = (SCMIR.mp3tempindex + 1)%(SCMIR.nummp3tempfiles); 24 | }; 25 | 26 | //outputfilename = SCMIR.tempdir ++ "sc3mp3read-" ++ (if(unique,{this.hash},{"temp"})) ++ ".wav"; 27 | 28 | "Creating temporary .wav from MP3".postln; 29 | 30 | //"/usr/local/bin/lame" 31 | SCMIR.external((SCMIR.lamelocation) + "--decode" + "\"" ++ inputfilename ++ "\"" + "\""++ outputfilename ++"\""); 32 | 33 | // if(().systemCmd == 0, { 34 | // ^Buffer.read(server,tmpPath,startFrame,numFrames, {("rm" + tmpPath).unixCmd} <> action, bufnum); 35 | // }, { 36 | // ("MP3: unable to read file:" + path).warn; 37 | // ("rm" + tmpPath).unixCmd; 38 | // }); 39 | 40 | 41 | "Created temporary .wav from MP3".postln; 42 | 43 | ^outputfilename; 44 | } 45 | 46 | 47 | } -------------------------------------------------------------------------------- /SCMIRExtensions/Classes/SCMIRFile/SCMIRZArchive.sc: -------------------------------------------------------------------------------- 1 | //ZArchive no longer in SC core, so reintroduced here. Originally written by Chris Sattinger as part of the crucial library 2 | 3 | SCMIRZArchive : File { 4 | 5 | var stringCache,<>path,<>version=1.0; 6 | var lastItem,count=0,stringCacheStart; 7 | 8 | *write { arg pathName; 9 | ^super.new(pathName, "wb").path_(pathName).check.initStringCache 10 | } 11 | *read { arg pathName; 12 | ^super.new(pathName, "rb").path_(pathName).check 13 | .getStringCache 14 | } 15 | writeItem { arg thing, extraArgs; 16 | var check; 17 | // == has some problems 18 | check = lastItem === thing; 19 | 20 | if(check,{ 21 | count = count + 1; 22 | },{ 23 | lastItem = thing; 24 | this.saveCount; 25 | thing.writeSCMIRZArchive(this,extraArgs); 26 | }); 27 | } 28 | readItem { arg class; // if class not nil, it will assert that the item is of that class 29 | 30 | var type,size,thing,classname; 31 | if(count > 0,{ 32 | count = count - 1; 33 | ^lastItem 34 | }); 35 | type = this.getChar; 36 | 37 | if(type == $.,{ // ... repetition 38 | count = this.getInt32 - 1; 39 | ^lastItem 40 | }); 41 | 42 | if(type == $N,{ 43 | if(class.notNil and: (Nil !== class),{ 44 | die("ZArchive got wrong type:",nil,"expected:",class.asString); 45 | }); 46 | ^lastItem = nil 47 | }); 48 | if(type == $F,{ 49 | thing = this.getFloat; 50 | if(class.notNil and: (thing.class !== class),{ 51 | die("ZArchive got wrong type:",thing,"expected:",class.asString); 52 | }); 53 | ^lastItem = thing 54 | }); 55 | if(type == $I,{ 56 | thing = this.getInt32; 57 | if(class.notNil and: (thing.class !== class),{ 58 | die("ZArchive got wrong type:",thing,"expected:",class.asString); 59 | }); 60 | ^lastItem = thing 61 | }); 62 | if(type == $s,{ // small string < 128 63 | thing = this.readString; 64 | if(class.notNil and: (thing.class !== class),{ 65 | die("ZArchive got wrong type:",thing,"expected:",class.asString); 66 | }); 67 | ^lastItem = thing 68 | }); 69 | if(type == $S,{ // large string 70 | thing = this.readLargeString; 71 | if(class.notNil and: (thing.class !== class),{ 72 | die("ZArchive got wrong type:",thing,"expected:",class.asString); 73 | }); 74 | ^lastItem = thing 75 | }); 76 | if(type == $y,{ // symbol 77 | thing = this.readString.asSymbol; 78 | if(class.notNil and: (thing.class !== class),{ 79 | die("ZArchive got wrong type:",thing,"expected:",class.asString); 80 | }); 81 | ^lastItem = thing 82 | }); 83 | if(type == $D,{// dictionaries 84 | classname = this.readString; 85 | size = this.getInt32; 86 | thing = classname.asSymbol.asClass.new(size); 87 | if(class.notNil and: (thing.class !== class),{ 88 | die("ZArchive got wrong type:",thing,"expected:",class.asString); 89 | }); 90 | size.do({ arg i; 91 | thing.put(this.readItem,this.readItem); 92 | }); 93 | ^lastItem = thing 94 | }); 95 | if(type == $C,{ // collection 96 | classname = this.readString; 97 | size = this.getInt32; 98 | thing = classname.asSymbol.asClass.new(size); 99 | if(class.notNil and: (thing.class !== class),{ 100 | die("ZArchive got wrong type:",thing,"expected:",class.asString); 101 | }); 102 | size.do({ arg i; 103 | thing.add( this.readItem ); 104 | }); 105 | ^lastItem = thing 106 | }); 107 | if(type == $x,{ // small compile string 108 | thing = this.readString.interpret; 109 | if(class.notNil and: (thing.class !== class),{ 110 | die("ZArchive got wrong type:",thing,"expected:",class.asString); 111 | }); 112 | ^lastItem = thing 113 | }); 114 | if(type == $X,{ // large compile string 115 | thing = this.readLargeString.interpret; 116 | if(class.notNil and: (thing.class !== class),{ 117 | die("ZArchive got wrong type:",thing,"expected:",class.asString); 118 | }); 119 | ^lastItem = thing 120 | }); 121 | if(this.pos >= (stringCacheStart),{ 122 | this.close; 123 | "End of file, unable to read item".die(this); 124 | }); 125 | this.close; 126 | die("ZArchive read error, got bad type code:",type); 127 | } 128 | 129 | writeClose { 130 | var stringCacheStart; 131 | this.saveCount; 132 | stringCacheStart = this.pos; 133 | 134 | // if you do go over the limit, you could simply raise the limit but your old files aren't compatible 135 | if(stringCache.size >= (2**16),{ 136 | "Too many strings in archive ! Not supported.".die; 137 | }); 138 | 139 | this.putInt16(stringCache.size); 140 | stringCache.keysValuesDo({ |string,index| 141 | this.putInt16(index); 142 | this.putInt8(string.size); 143 | this.putString(string); 144 | }); 145 | this.putInt32(stringCacheStart); 146 | 147 | this.close; 148 | } 149 | 150 | //PRIVATE 151 | saveCount { 152 | if(count > 0,{ 153 | super.putChar($.); 154 | super.putInt32(count); 155 | count = 0; 156 | }); 157 | } 158 | check { 159 | if(this.isOpen.not,{ "ZArchive failed to open".die(path); }); 160 | } 161 | initStringCache { stringCache = Dictionary.new } 162 | getStringCache { 163 | var size; 164 | this.seek(-4,2); // bytes from the end 165 | stringCacheStart = this.getInt32; 166 | 167 | this.seek(stringCacheStart,0); 168 | size = this.getInt16; 169 | stringCache = Array.newClear(size); 170 | size.do({ arg i; 171 | var index,ssize,string; 172 | index = this.getInt16; 173 | ssize = this.getInt8; 174 | /*if(ssize < 0,{ 175 | Error("ZArchive read error. String table returns negative sized item").throw; 176 | });*/ 177 | string = String.newClear(ssize); 178 | this.read(string); 179 | stringCache.put( index, string ); 180 | }); 181 | this.seek(0,0); 182 | 183 | // the one thing you will never archive is the archiver itself 184 | // so lastItem starts with this 185 | lastItem = this; 186 | // any other item in existence is something you might potentially save next 187 | // eg. Object, nil 188 | } 189 | writeString { arg string; 190 | var prev; 191 | prev = stringCache[string]; 192 | if(prev.isNil,{ 193 | prev = stringCache.size; 194 | stringCache[string] = prev; 195 | }); 196 | this.putInt16(prev); 197 | } 198 | readString { 199 | var ix,string; 200 | ix = this.getInt16; 201 | ^stringCache[ix] ?? {"ZArchive failed to find String : index:".die(ix);}; 202 | } 203 | writeLargeString { arg string; 204 | this.putInt32(string.size); 205 | this.putString(string); 206 | } 207 | readLargeString { 208 | var size,string; 209 | size = this.getInt32; 210 | string = String.newClear(size); 211 | this.read(string); 212 | ^string 213 | } 214 | asZArchive { ^this } 215 | } 216 | /* 217 | to detect repeats of dictionaries (or events) and arrays: 218 | instead of internally using writeItem/readItem, use prWriteItem,prReadItem. 219 | this would break current archives, so you could use a different type code 220 | for the new ones 221 | */ 222 | -------------------------------------------------------------------------------- /SCMIRExtensions/Classes/SCMIRFile/SCMIRZArchiveSupport.sc: -------------------------------------------------------------------------------- 1 | 2 | + Object { 3 | writeSCMIRZArchive { arg akv; 4 | var thing; 5 | thing = this.asCompileString; 6 | if(thing.size > 127,{ 7 | akv.putChar($X); 8 | akv.writeLargeString( thing ); 9 | } , { 10 | akv.putChar($x); 11 | akv.writeString( thing ); 12 | }) 13 | } 14 | // this.new, then new.readSCMIRZArchive 15 | *readSCMIRZArchive { arg ... args; 16 | var new,akv; 17 | new = this.new; 18 | akv = args.first; 19 | if(akv.isString,{ 20 | akv = SCMIRZArchive.read(Document.standardizePath(akv)); 21 | args[0] = akv; 22 | }); 23 | new.performList(\readSCMIRZArchive,args); 24 | ^new 25 | } 26 | // see Help file 27 | readSCMIRZArchive { /*arg akv; ^akv.readItem;*/ } 28 | } 29 | 30 | + Nil { 31 | writeSCMIRZArchive { arg akv; 32 | akv.putChar($N); 33 | } 34 | } 35 | 36 | + String { 37 | asSCMIRZArchive { 38 | ^SCMIRZArchive.write(Document.standardizePath(this)) 39 | } 40 | writeSCMIRZArchive { arg akv; 41 | if(this.size < 128,{ 42 | akv.putChar($s); 43 | akv.writeString(this); 44 | ^this 45 | },{ // up to 4294967296 46 | akv.putChar($S); 47 | akv.writeLargeString(this); 48 | }); 49 | } 50 | } 51 | + Symbol { 52 | writeSCMIRZArchive { arg akv; 53 | akv.putChar($y); 54 | akv.writeString(this.asString); 55 | } 56 | } 57 | 58 | + Float { 59 | writeSCMIRZArchive { arg akv; 60 | akv.putChar($F); 61 | akv.putFloat(this); 62 | } 63 | } 64 | + Integer { 65 | writeSCMIRZArchive { arg akv; 66 | akv.putChar($I); 67 | akv.putInt32(this); 68 | } 69 | } 70 | 71 | /* raw arrays could cut in half by not having to repeat the class 72 | if(this.isKindOf(RawArray),{ // check the type of this.at(0) 73 | akv.putChar($S); 74 | classname = this.class.name.asString; 75 | akv.putInt8(classname.size); 76 | akv.putString(classname); 77 | akv.putInt32(this.size); 78 | akv.write(this); // do ? 79 | } 80 | */ 81 | 82 | // classname is written, so you will get the correct class back 83 | + Dictionary { 84 | writeSCMIRZArchive { arg akv; 85 | var classname; 86 | akv.putChar($D); 87 | classname = this.class.name.asString; 88 | akv.writeString(classname); 89 | akv.putInt32(this.size); 90 | this.keysValuesDo({ arg k,v,i; 91 | akv.writeItem(k); 92 | akv.writeItem(v) 93 | }); 94 | } 95 | } 96 | 97 | + SequenceableCollection { 98 | writeSCMIRZArchive { arg akv; 99 | var classname; 100 | akv.putChar($C); 101 | classname = this.class.name.asString; 102 | akv.writeString(classname); 103 | akv.putInt32(this.size); 104 | this.do({ arg it; akv.writeItem(it) }); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /SCMIRExtensions/Classes/SCMIRLearning/GMM.sc: -------------------------------------------------------------------------------- 1 | //Gaussian mixture model 2 | //calls out to external gmm program executable for calculations 3 | 4 | GMM { 5 | //classvar modelpath; //path to unique Gaussian model data used by this class; automatically in temp directory, but can be set 11 | var bestscore) { 233 | 234 | bestscore = scorenow; 235 | //[i,bestscore].postln; 236 | result = i; 237 | 238 | } 239 | 240 | } 241 | 242 | }; 243 | 244 | if(outputscore) { 245 | ^[result, bestscore]; 246 | } 247 | 248 | 249 | } { 250 | file = SCMIRFile((SCMIR.tempdir++"gmmtest"), "wb"); 251 | 252 | file.writeLE(FloatArray.newFrom(input)); 253 | 254 | file.close; 255 | 256 | //invoke program to act on it 257 | 258 | SCMIR.external(SCMIR.executabledirectory++"gmm"+ 1 + modelpath + (SCMIR.tempdir++"gmmtest")+(SCMIR.tempdir++"gmmoutput")); // + "noveltyoutput"; 259 | 260 | file = SCMIRFile(SCMIR.tempdir++"gmmoutput","rb"); 261 | 262 | result = file.getInt32LE(); 263 | 264 | // probs= FloatArray.newClear(numstates); 265 | // 266 | // numstates.do{|j| 267 | // noveltycurve[j] = file.getFloatLE; 268 | // }; 269 | // 270 | // 271 | file.close; 272 | } 273 | 274 | ^result; 275 | } 276 | 277 | 278 | 279 | } -------------------------------------------------------------------------------- /SCMIRExtensions/Classes/SCMIRLearning/HMM.sc: -------------------------------------------------------------------------------- 1 | //Hidden Markov Model (for discrete observation symbols) 2 | //calls out to external hmm program executable for calculations 3 | //depends on external saved model file 4 | 5 | HMM { 6 | classvar modelpath; //path to unique HMM data used by this class; automatically in temp directory, but can also be set 10 | 11 | var transitioncounts; 10 | var 0.0000000000001) { 74 | 75 | local = local/total; 76 | 77 | for(0,numstates-1,{|k| 78 | 79 | transitionprobabilities[index+k] = local[k] 80 | }); 81 | 82 | //transitionprobabilities[index..(index+numstates-1)] = local/total; 83 | } 84 | 85 | }; 86 | 87 | //minimalprob = min(transitionprobabilities over 0)*0.1; 88 | minimalprob = 1.0; 89 | 90 | transitionprobabilities.do{|val| 91 | 92 | if(val>0.0000000000001) { 93 | 94 | if(valorder,{ 114 | 115 | inputsequence.copyRange(inputsequence.size-order,inputsequence.size-1); 116 | 117 | },inputsequence); 118 | 119 | 120 | 121 | if(input.size3) { 51 | 52 | //copyRange is inclusive 53 | context = array.copyRange(0,array.size-4); 54 | 55 | lastthree = array.copyRange(array.size-3,array.size-1); 56 | 57 | context.do{|val| 58 | 59 | var pitchclass = val%poolsize; 60 | var index = lastthree[0]*numobjects+lastthree[1]; 61 | 62 | transitiontables[pitchclass][index][lastthree[2]] = transitiontables[pitchclass][index][lastthree[2]] + 1; 63 | 64 | } 65 | 66 | }; 67 | 68 | } 69 | 70 | 71 | //in general, weights by distance of events in the past, decay 72 | scoreOptions {|array| 73 | 74 | var context,lasttwo; 75 | var sum = 0.0!numobjects; 76 | 77 | if(array.size>2) { 78 | 79 | context = array.copyRange(array.size-2-windowsize,array.size-3); 80 | 81 | lasttwo = array.copyRange(array.size-2,array.size-1); 82 | 83 | context.do{|val,j| 84 | 85 | var pitchclass = val%poolsize; 86 | var index = lasttwo[0]*numobjects+lasttwo[1]; 87 | var prop = ((j+1)/(context.size)); 88 | 89 | sum = sum + ((transitiontables[pitchclass][index]).normalizeSum * prop); 90 | 91 | 92 | }; 93 | 94 | //sum.postln; 95 | 96 | //alternatives include rolling dice for each component in sum, and doing a majority vote, or rolling dice on sum.normalizeSum 97 | 98 | ^sum; 99 | 100 | } { 101 | 102 | ^nil; 103 | }; 104 | 105 | 106 | 107 | } 108 | 109 | generate {|array,n=1| 110 | 111 | var output = List[]; 112 | var next; 113 | 114 | if(array.size>2) { 115 | 116 | n.do{ 117 | 118 | var next = this.scoreOptions(array); 119 | 120 | //"here".postln; 121 | //next.postln; 122 | 123 | //alternatives include rolling dice for each component in sum, and doing a majority vote, or rolling dice on sum.normalizeSum 124 | 125 | //next = next.maxIndex; 126 | 127 | next = (0..(next.size-1)).wchoose(next.normalizeSum); 128 | 129 | 130 | output.add(next); 131 | 132 | array = array.copyRange(1,array.size-1) ++ [next]; 133 | }; 134 | 135 | }; 136 | 137 | ^output.asArray; 138 | 139 | } 140 | 141 | save { |filename| 142 | var a; 143 | 144 | filename = filename?? {SCMIR.tempdir++"markovpool"++".scmirZ"}; 145 | 146 | a = SCMIRZArchive.write(filename); 147 | 148 | a.writeItem(order); 149 | a.writeItem(poolsize); 150 | a.writeItem(numobjects); 151 | a.writeItem(transitiontables); 152 | a.writeItem(windowsize); 153 | 154 | a.writeClose; 155 | } 156 | 157 | 158 | load { |filename| 159 | var a; 160 | 161 | filename = filename?? {SCMIR.tempdir++"markovpool"++".scmirZ"}; 162 | 163 | a = SCMIRZArchive.read(filename); 164 | 165 | order = a.readItem; 166 | poolsize = a.readItem; 167 | numobjects = a.readItem; 168 | transitiontables = a.readItem; 169 | windowsize = a.readItem; 170 | 171 | a.close; 172 | 173 | } 174 | 175 | 176 | } 177 | -------------------------------------------------------------------------------- /SCMIRExtensions/Classes/SCMIRLearning/NaiveBayes.sc: -------------------------------------------------------------------------------- 1 | //assumes all features conditionally independent, continuous features modeled via Gaussian distribution 2 | //alternative would be to discretise via binning (possibly even histogram equalisation): assisted by having max/min normalisation already in place, and must have at least 1 artificial count in each bin. Will work best for large training sets, 3 | 4 | //P(C) product over i P(Fi/C) 5 | //log P(C) + sum over i log P(Fi/C) 6 | 7 | //by default, prior over classes is flat, equiprobable 8 | 9 | NaiveBayes { 10 | var maxscore) { 97 | 98 | maxscore = score; 99 | 100 | maxindex = classnow; 101 | }; 102 | 103 | }; 104 | 105 | ^maxindex; 106 | } 107 | 108 | 109 | 110 | save { |filename| 111 | var a; 112 | 113 | filename = filename?? {SCMIR.tempdir++"naivebayes"++".scmirZ"}; 114 | 115 | a = SCMIRZArchive.write(filename); 116 | 117 | a.writeItem(numfeatures); 118 | a.writeItem(numclasses); 119 | a.writeItem(nummodels); 120 | a.writeItem(means); 121 | a.writeItem(stddevs); 122 | 123 | a.writeClose; 124 | } 125 | 126 | 127 | load { |filename| 128 | var a; 129 | 130 | filename = filename?? {SCMIR.tempdir++"naivebayes"++".scmirZ"}; 131 | 132 | a = SCMIRZArchive.read(filename); 133 | 134 | numfeatures = a.readItem; 135 | numclasses = a.readItem; 136 | nummodels = a.readItem; 137 | means = a.readItem; 138 | stddevs = a.readItem; 139 | 140 | a.close; 141 | 142 | } 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | //given input, find probabilities of outputs 151 | calculate {|input| 152 | 153 | //use this log version to avoid underflow in multiplication 154 | //log P(C) + sum over i log P(Fi/C) 155 | 156 | var probs = (0..(numclasses-1)).collect {|classnow| 157 | var score; 158 | var baseindex = classnow*numfeatures; 159 | 160 | score = 0.0; 161 | 162 | numfeatures.do{|featureindex| 163 | var indexnow = baseindex + featureindex; 164 | var mean = means[indexnow]; 165 | var stddev = stddevs[indexnow]; 166 | 167 | //log of Gaussian 168 | // - log(stddev) - ((val-mean)**2/2*stddev**2) 169 | 170 | score = score - log(stddev) - ((input[featureindex]-mean).squared/(2*(stddev.squared))); 171 | 172 | }; 173 | 174 | score 175 | 176 | }; 177 | 178 | //or exp(probs?) 179 | /* probs = probs - (probs.minItem); 180 | probs.normalizeSum; 181 | 182 | //always values 0-1, always winner at 1 183 | ^probs = probs/(probs.maxItem); 184 | */ 185 | ^exp(probs); 186 | 187 | } 188 | 189 | 190 | 191 | 192 | 193 | } 194 | -------------------------------------------------------------------------------- /SCMIRExtensions/Classes/SCMIRLearning/PPMCOnline.sc: -------------------------------------------------------------------------------- 1 | PPMCOnline : PPMC { 2 | var maxorder) { 24 | 25 | //List[3,4,5].copyRange(1,2) 26 | 27 | received = received.copyRange(received.size-maxorder,received.size-1); 28 | 29 | }; 30 | 31 | trienow = trie; 32 | 33 | context = received[(received.size-pastsize)..(received.size-1)]; 34 | 35 | context.do {|pastval,j| 36 | 37 | var node = trienow[pastval]; 38 | 39 | 40 | //[pastval, trienow, node].postln; 41 | 42 | 43 | if(node.notNil) { 44 | 45 | //increment and continue 46 | node[0] = node[0] +1 ; 47 | 48 | trienow = node[1]; 49 | 50 | } { 51 | 52 | //if Dictionary remains empty, leaf 53 | node = [1,Dictionary[]]; 54 | 55 | //["here", node].postln; 56 | 57 | trienow.put(pastval,node); 58 | 59 | trienow = node[1]; 60 | 61 | //["here2", trie, trienow, node].postln; 62 | 63 | 64 | } 65 | 66 | } 67 | 68 | 69 | 70 | 71 | } 72 | 73 | 74 | 75 | } -------------------------------------------------------------------------------- /SCMIRExtensions/Classes/SCMIRLearning/SARDNET.sc: -------------------------------------------------------------------------------- 1 | //1-dimensional SARDNET which does not store sequences, but learns maps from sequences 2 | 3 | SARDNET { 4 | 5 | var