├── 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 1) {
120 | temp[offset+j] = freqcounts[b][0];
121 | };
122 |
123 | //else transition doesn't occur, zero probability already assigned
124 |
125 | };
126 |
127 | statetransitionmatrix[i] = temp;
128 |
129 | };
130 |
131 | "transitions from gap states".postln;
132 |
133 | //transitions from gap states
134 | numqg.do{|i|
135 |
136 | var gap = statedata[1][i];
137 |
138 | temp = 0!numstates;
139 |
140 | temp[qindices[2]+gap[1]] = 1.0;
141 |
142 | statetransitionmatrix[qindices[1]+i] = temp;
143 | };
144 |
145 | "transitions from second states".postln;
146 |
147 | //transitions from second states and universal involve same calculation
148 |
149 | temp = 0!numstates;
150 |
151 | numqs1.do {|i|
152 |
153 | var firstsymbol = statedata[0][i];
154 |
155 | temp[i] = alpha * vgs.freqjb(firstsymbol);
156 | };
157 |
158 | temp[qindices[3]] = 1 - alpha;
159 |
160 | numqs2.do {|i|
161 |
162 | statetransitionmatrix[qindices[2]+i] = temp;
163 |
164 | };
165 |
166 | //transitions from universal gap
167 | statetransitionmatrix[qindices[3]] = temp;
168 |
169 |
170 | //3.2.5 state duration probabilities
171 |
172 | "calculating state duration probabilities".postln;
173 |
174 | numqs1.do{|i|
175 | statedurationprobabilities[i][0]=1.0;
176 | };
177 |
178 | offset = qindices[2];
179 |
180 | numqs2.do{|i|
181 | statedurationprobabilities[offset+i][0]=1.0;
182 | };
183 |
184 | //gaps
185 | offset = qindices[1];
186 |
187 | temp = vgs.gapdata;
188 |
189 | numqg.do{|i|
190 |
191 | var now = temp[i];
192 |
193 | statedurationprobabilities[offset+i] = now/(now.sum);
194 |
195 | };
196 |
197 | statedurationprobabilities[qindices[3]][0]=1.0;
198 |
199 | //3.2.6 initial state probabilities
200 |
201 | "calculating initial state probabilities".postln;
202 |
203 | temp = vgs.freqqs1(numqs1);
204 |
205 | numqs1.do{|i|
206 | initialstateprobabilities[i]=alpha*temp[i]; //TO SORT
207 | };
208 |
209 | initialstateprobabilities[qindices[3]]=1.0-alpha;
210 |
211 | }
212 |
213 | //output sequence of a certain length
214 | generate {|n=1|
215 |
216 | var statenow = 0;
217 | var output = List[];
218 | var temp;
219 | var duration = 1;
220 | var allstates = (0..(numstates-1));
221 | var allsymbols = (0..(alphabetsize-1));
222 | var alldurations = (1..(vgs.maxgap));
223 |
224 | //create initial state
225 | statenow = allstates.wchoose(initialstateprobabilities);
226 |
227 | //generate n output tokens following hidden state transitions and emissions probabilities
228 |
229 | n.do {|i|
230 |
231 | //emit symbol
232 |
233 | temp = allsymbols.wchoose(emissionprobabilitymatrix[statenow]);
234 |
235 | output.add(temp);
236 |
237 | if(duration>1) {duration = duration-1;}
238 | {
239 | //update state
240 |
241 | statenow = allstates.wchoose(statetransitionmatrix[statenow]);
242 |
243 | duration = alldurations.wchoose(statedurationprobabilities[statenow]);
244 | };
245 |
246 | //[i, \statenow, statenow, \duration, duration, \temp, temp].postcs;
247 |
248 | };
249 |
250 | ^vgs.indicesToSymbolArray(output.asArray);
251 | }
252 |
253 | }
254 |
--------------------------------------------------------------------------------
/SCMIRExtensions/Classes/SCMIRLearning/VariableGapSequenceMining.sc:
--------------------------------------------------------------------------------
1 | //aka VGS, p.6 in VOGUE paper
2 | //but assumes input sequences are always just integer sequences e.g., [0,3,4,2,3,2,0,4,0..]
3 | //assumes 2-sequences only, really
4 |
5 | VariableGapSequenceMining {
6 | var <>minsupport,<>maxgap,<>maxsubseqsize;
7 | var =minsupport};
95 |
96 | }
97 |
98 | statedata {
99 |
100 | var n1list = List[];
101 | var n2list = List[];
102 | var gapcount = 0;
103 | var gapconnects = List[];
104 |
105 | subsequences.keysValuesDo{|key,val|
106 |
107 | if(key.size>1) {
108 | n1list.add(key[0]);
109 | n2list.add(key[1]);
110 |
111 | if(val[1].copyRange(1,maxgap).sum>0) {gapcount = gapcount+1; gapconnects.add(key);};
112 | };
113 |
114 | };
115 |
116 | //^[this.symbolArrayToIndices(n1list.asSet.asArray.sort),this.symbolArrayToIndices(gapconnects),this.symbolArrayToIndices(n2list.asSet.asArray.sort)];
117 |
118 | ^[n1list.asSet.asArray.sort,gapconnects,n2list.asSet.asArray.sort];
119 |
120 | }
121 |
122 | gapdata {
123 |
124 | var list= List[];
125 |
126 | subsequences.keysValuesDo{|key,val|
127 |
128 | if(key.size>1) {
129 |
130 | if(val[1].copyRange(1,maxgap).sum>0) {
131 | list.add(val[1].copyRange(1,maxgap));
132 | };
133 | };
134 |
135 | };
136 |
137 | ^list.asArray
138 | }
139 |
140 | //relative probability all starting points
141 | freqqs1 {|numqs1|
142 | var n1list = 0!numqs1;
143 | var temp;
144 |
145 | subsequences.keysValuesDo{|key,val|
146 |
147 | if(key.size>1) {
148 |
149 | temp = key[0];
150 |
151 | n1list[temp] = n1list[temp] + val[0];
152 |
153 | };
154 |
155 | };
156 |
157 | ^n1list/(n1list.sum);
158 | }
159 |
160 |
161 |
162 |
163 | symbolArrayToIndices {|array|
164 |
165 | ^array.collect{|val| alphabetindexlookup[val] };
166 |
167 | }
168 |
169 | indicesToSymbolArray {|array|
170 |
171 | ^array.collect{|i| alphabet[i] };
172 |
173 | }
174 |
175 | freqab {|arrayab|
176 |
177 | ^arrayab.collect{|ab|
178 |
179 | subsequences[ab][2].normalizeSum;
180 |
181 | };
182 |
183 | }
184 |
185 |
186 | //transitions between states; anything starting with a to any other alphabet
187 | freqaj {|a|
188 |
189 | var temp;
190 | var total=0;
191 | var probs = 0!(alphabet.size); //one for each possible continuation
192 |
193 | subsequences.keysValuesDo{|key,val|
194 |
195 | //[key,val].postcs;
196 |
197 | if(key.size>1) {
198 |
199 | if(key[0]==a) {
200 |
201 | temp = val[1]; //gap information
202 |
203 | //[\temp,temp,temp.sum,temp[0],temp.copyRange(1,2)].postln;
204 |
205 | total = total + (temp.sum);
206 |
207 | probs[key[1]] = [temp[0], temp.copyRange(1,maxgap).sum];
208 |
209 | //[\temp,temp,temp.sum,temp[0],temp.copyRange(1,2)].postln;
210 |
211 | }
212 | };
213 |
214 | };
215 |
216 | ^probs/total;
217 |
218 | }
219 |
220 | freqjb {|j|
221 |
222 | var temp;
223 | var totalj=0;
224 | var all=0;
225 |
226 | subsequences.keysValuesDo{|key,val|
227 |
228 | if(key.size>1) {
229 |
230 | all = all + val[0]; //count for this duplet
231 |
232 | if(key[0]==j) {
233 |
234 | temp = val[1]; //gap information
235 |
236 | totalj = totalj + (temp.sum); //isn't temp.sum always equal to val[0]?
237 |
238 | }
239 | };
240 |
241 | };
242 |
243 | ^totalj/all;
244 | }
245 |
246 |
247 |
248 |
249 | save {}
250 |
251 | load {}
252 |
253 |
254 |
255 | }
--------------------------------------------------------------------------------
/SCMIRExtensions/Classes/SCMIRLive.sc:
--------------------------------------------------------------------------------
1 | SCMIRLive {
2 |
3 | classvar <>livecount;
4 |
5 | var synthdefname;
11 | var <>oscname;
12 |
13 | //not saved or loaded since dynamic server state
14 | var fitbelow){currentcolumn[j-1][0] + dij}{mincost};
84 | newcosts[1] = if(j<=previoushighest) {previouscolumn[j][0] + dij}{mincost}; //i always valid since start second column if(i>0){}{mincost};
85 | newcosts[2] = if(j>0){previouscolumn[j-1][0] + (2*dij)}{mincost};
86 |
87 | newcosts.do{|val,k| if(val0.0) && (bsum>0.0)) {
27 |
28 | //can normalize and actually calculate
29 |
30 | a = a/asum;
31 | b= b/bsum;
32 |
33 | m = (a+b)*0.5;
34 |
35 | m.do{|val,i|
36 |
37 | var ai = a[i];
38 | var bi= b[i];
39 |
40 | //if a[i] >0 then m[i] also >0.0 so division safe
41 | //assumption is that 0 (log 0) is 0, so can bypass that eventuality (ie no contribution to sum, so no need to do anything!)
42 | if(ai>0.0) {answer = answer + (ai * log2(ai / val)) };
43 | if(bi>0.0) {answer = answer + (bi * log2(bi / val)) };
44 |
45 | }
46 |
47 | };
48 |
49 | ^answer;
50 |
51 | }
52 |
53 |
54 | //data in form of features as block featuresframe1, featuresframe2, etc.
55 |
56 | //m is dimensionality of featurevector
57 | *structuralChange {|data,m,numframes,windowsizes|
58 |
59 | var cumulative = Array.fill(numframes+1,nil);
60 | var change = Array.fill(windowsizes.size,nil);
61 |
62 | if( (m*numframes) != (data.size)) {
63 |
64 | ["size error in data provided!",m,numframes,m*numframes,data.size].postln;
65 |
66 | };
67 |
68 |
69 | cumulative[0] = 0.0!m; //zeroes at beginning for ease of calculation below
70 | cumulative[1] = data[0..(m-1)];
71 |
72 | (numframes-1).do{|i|
73 | var index = (i+1)*m;
74 |
75 | //as vectors, easier for later calculations
76 | cumulative[i+2] = (cumulative[i+1].copy) + (data[index..(index+m-1)]);
77 |
78 | //
79 | // m.do{|j|
80 | //
81 | // cumulative[index] = cumulative[index-m]+data[index];
82 | //
83 | // };
84 | //
85 | };
86 |
87 | //cumulative.postcs;
88 |
89 | windowsizes.do{|windowsize,i|
90 |
91 | var startindex = windowsize;
92 | var endindex = numframes - windowsize;
93 | var numcalc = endindex-startindex+1;
94 | var changearray;
95 |
96 | //for some sort of logging as proceed
97 | //[\windowsize,windowsize].postln;
98 |
99 | changearray = Array.fill(numframes,0.0);
100 |
101 | //test in case window too big for data
102 | if((startindex=0)) {
103 |
104 | numcalc.do{|j|
105 |
106 | var framenow = startindex + j;
107 | var firstframe = framenow-windowsize;
108 | var lastframe = framenow+windowsize-1;
109 | var s1,s2;
110 |
111 | //get means via cumulative
112 |
113 | //[framenow,firstframe,lastframe+1,framenow].postln;
114 |
115 | s1 = cumulative[framenow] - cumulative[firstframe];
116 | s2 = cumulative[lastframe+1] - cumulative[framenow];
117 |
118 | //COULD take mean of s1 and s2 properly, eg divide by windowsize. But normalisation in ~jensenshannon makes that an irrelevant operation
119 |
120 | //[s1,s2].postln;
121 |
122 | changearray[framenow] = SCMIRStructuralSummary.jensonShannon(s1,s2);
123 |
124 | };
125 |
126 |
127 | };
128 |
129 |
130 | change[i] = changearray;
131 |
132 | };
133 |
134 | ^change;
135 | }
136 |
137 | *structuralSummary {|data,m,numframes,windowsizes|
138 |
139 | var tracks;
140 |
141 | tracks = SCMIRStructuralSummary.structuralChange(data,m,numframes,windowsizes);
142 |
143 | //var results = Array.fill(windowsizes.size*2,0.0);
144 |
145 | ^ ((tracks.collect{|val| val.mean}) ++ (tracks.collect{|val| val.median}));
146 |
147 | }
148 |
149 | }
150 |
151 |
152 |
153 |
154 | + SCMIRAudioFile {
155 |
156 | structuralSummary {|windowsizes|
157 |
158 | ^SCMIRStructuralSummary.structuralSummary(featuredata,numfeatures,numframes,windowsizes);
159 |
160 | }
161 |
162 | }
163 |
164 |
165 |
--------------------------------------------------------------------------------
/SCMIRExtensions/HelpSource/Classes/GMM.schelp:
--------------------------------------------------------------------------------
1 | title:: GMM
2 | summary:: Gaussian Mixture Model
3 | categories:: SCMIR, Machine Learning
4 | related:: Classes/NeuralNet, Classes/NaiveBayes
5 |
6 | description::
7 |
8 | Experimental machine learning class, useful for density estimation or clustering. Certain functions require the use of MathLib from the Quarks. Also relies on an auxilliary external for the hard work.
9 |
10 | FUTURE: Can remove dependency on MathLib if roll own matrix multiplications
11 |
12 | code::
13 |
14 | g = GMM(4);
15 |
16 | g.numstates;
17 |
18 |
19 | //if don't have distinguished states, can get situation where fails to train!
20 | //d = Array.fill(100,{|i| [i]++({0.2.rand2}!10)});
21 | d = Array.fill(1000,{|i| ({rrand(i%4,2*(i%4)+1.0)}!10)});
22 |
23 | g.train(d) //can take a while if thousands of data points
24 |
25 |
26 | g.loadModel; //get GMM model parameters into SC
27 |
28 |
29 | g.generate; //create random new points from the mixture model
30 |
31 |
32 | //test specific data points for predicted class
33 | (
34 | a = {rrand(0.0,1.0)}!10;
35 | g.test(a);
36 | )
37 |
38 | //some sprinkling of classes 2 and 3
39 | 100.do{ g.test({rrand(2.0,7.0)}!10).postln }
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 | ////////////////////////////////////////////////////////////////////////
49 | //TESTING
50 | //http://en.wikipedia.org/wiki/Eigendecomposition_(matrix)
51 |
52 | //check that covarA is the 'square root' of covarmatrices
53 | a = g.covarA[0]
54 | a*(a.flop)
55 | b = g.covarmatrices[0]
56 |
57 |
58 | ::
59 |
60 |
61 |
62 | examples::
63 |
64 |
--------------------------------------------------------------------------------
/SCMIRExtensions/HelpSource/Classes/HMM.schelp:
--------------------------------------------------------------------------------
1 | title:: HMM
2 | summary:: Hidden Markov Model
3 | categories:: SCMIR, Machine Learning
4 | related:: Classes/NeuralNet, Classes/NaiveBayes, Classes/GMM
5 |
6 | description::
7 |
8 | Hidden Markov Model wrapper class, relying on an auxilliary external for the hard work. You can set the number of hidden states, and the number of discrete observation classes. Note that observations are just nonnegative integers, from 0 to numsymbols-1. If you have continuous feature vectors, these must be turned into single labels, via vector quantisation with k-means clustering or similar discretisation processes.
9 |
10 |
11 | code::
12 |
13 |
14 | h = HMM(5,3) //create a HMM object with 5 hidden states, and 3 observation classes (0,1,2 below)
15 |
16 |
17 | //In general, the training data of observation sequences must be an array of observation sequences (each themselves an array of integers)
18 | (
19 | ~data = [
20 | [0,1,2,2,1,0,1,2,0,1,2,2,1,2,1,0,1,1,2,2,1,0,1,2,0,1,2,2,1,2,1,0,1,2,2,0,1,2,1,2,2,1,0,1,2,0,1,2,2,1,2,1,0,1,2,2,0,1,2,2,2,0,1,2,2,1,1]
21 | ];
22 | )
23 |
24 | (
25 | {
26 | h.train(~data,10) //10 is the number of iterations for the expectation maximisation training of a HMM via Baum-Welch
27 | }.fork;
28 | )
29 |
30 | //use the HMM to generate novel output sequences from the hidden states and their transition and emission probabilities
31 | (
32 | {
33 | h.generate(100).postln; //create an output sequence of 100 symbols
34 | }.fork;
35 | )
36 |
37 |
38 | //Viterbi solution to find the optimal sequence of hidden states, given some observation sequence
39 | (
40 | {
41 | h.mostprobablestatesequence([2,1,0,2,1,2,1,2,1,2,1,1,2,2,2,2,0,1,1,0]).postln;
42 | }.fork;
43 | )
44 |
45 |
46 |
47 | //more complicated example
48 | h = HMM(13,10)
49 |
50 | ~data = [{10.rand}!1000, {5.rand+2}!650];
51 |
52 | (
53 | {
54 | h.train(~data,10)
55 | }.fork;
56 | )
57 |
58 |
59 | (
60 | {
61 | h.mostprobablestatesequence({10.rand}!10).postln;
62 | }.fork;
63 | )
64 |
65 |
66 | (
67 | {
68 | h.generate(100).postln;
69 | }.fork;
70 | )
71 |
72 |
73 |
74 | (
75 | {
76 | h.probability({10.rand}!10).postln;
77 | }.fork;
78 | )
79 |
80 | (
81 | {
82 | h.probability(1!10).postln;
83 | }.fork;
84 | )
85 |
86 |
87 | ::
88 |
89 |
90 |
91 | examples::
92 |
93 |
--------------------------------------------------------------------------------
/SCMIRExtensions/HelpSource/Classes/KNearestNeighbour.schelp:
--------------------------------------------------------------------------------
1 | TITLE:: KNearestNeighbour
2 | summary:: Given a new input vector, find a best fit from the closest k examples in a training dataset
3 | categories:: SCMIR, Machine Learning
4 | related:: Classes/NeuralNet, Classes/NaiveBayes
5 |
6 | DESCRIPTION::
7 | This algorithm assumes that
8 |
9 | CLASSMETHODS::
10 |
11 | METHOD:: new
12 |
13 | ARGUMENT:: k
14 | Number of closest examples to take into account
15 |
16 | ARGUMENT:: data
17 | Must be of the form [[[f1,f2,...],classindex],[[f1,f2,...],classindex]...]
18 |
19 | ARGUMENT:: range
20 | class indices in the data must use numbers from 0 to range-1
21 |
22 |
23 | EXAMPLES::
24 |
25 | code::
26 |
27 | //kNearestNeighbour
28 |
29 | a = Array.fill(1000,{[Array.rand(3,0.0,1.0),rrand(0,7)]})
30 |
31 | n = KNearestNeighbour(3,a,8)
32 |
33 | n.k
34 | n.data
35 | n.range
36 |
37 | b = n.findClosestK([0.5,0.5,0.5])
38 |
39 | a[591]
40 |
41 | b = n.findClosest([0.5,0.5,0.5]) //single closest winner
42 |
43 | //if each of the k said a different option, would return lowest number option rather than closest overall
44 | b = n.test([0.5,0.5,0.5]);
45 |
46 | //weighted version biased to closest according to 1/(n+1) from n = 0..(k-1)
47 | b = n.testWeighted([0.5,0.5,0.5]);
48 |
49 |
50 | //another test
51 | a = Array.fill(1000,{[Array.rand(3,0.0,1.0),rrand(0,10)]})
52 |
53 | n = KNearestNeighbour(8,a,11)
54 |
55 | b = n.findClosestK([0.5,0.5,0.5])
56 | b.size
57 | b = n.test([0.5,0.5,0.5]);
58 | b = n.testWeighted([0.5,0.5,0.5]);
59 | b = n.findClosest([0.5,0.5,0.5]);
60 |
61 | ::
--------------------------------------------------------------------------------
/SCMIRExtensions/HelpSource/Classes/MarkovModel.schelp:
--------------------------------------------------------------------------------
1 | title:: MarkovModel
2 | summary:: Markov Model
3 | categories:: SCMIR, Machine Learning
4 | related:: Classes/PPMC
5 |
6 | description::
7 |
8 | Machine learning class for discrete state sequences.
9 |
10 | There are further Markov modeling classes in the MathLib Quark.
11 |
12 | code::
13 | m = MarkovModel(6,2); //num states = 6, order =2 (last 2 states predict next)
14 |
15 | m.transitioncounts.size //216 = 6**3
16 |
17 | //sequence for training
18 | a =Array.fill(1000,{ 6.rand })
19 |
20 | m.train(a)
21 |
22 | //counts over all possible transition situations
23 | m.transitioncounts.class.postcs
24 |
25 | m.transitionprobabilities
26 |
27 | log(m.minimalprob)
28 |
29 | m.averagelogloss(Array.fill(100,{ 6.rand }))
30 |
31 | m.averagelogloss(Array.fill(100,{ 4.rand }))
32 |
33 |
34 | ::
35 |
36 |
37 |
38 | examples::
39 |
40 |
--------------------------------------------------------------------------------
/SCMIRExtensions/HelpSource/Classes/MarkovPool.schelp:
--------------------------------------------------------------------------------
1 | title:: MarkovPool
2 | summary:: Markov Pool
3 | categories:: SCMIR, Machine Learning
4 | related:: Classes/PPMC, Classes/MarkovModel
5 |
6 | description::
7 | Efficient committee of Markov Models generated from a given context.
8 |
9 | Introduced at SMPC 2013, see the abstract:
10 | Nick Collins (2013) Presentation on "Markov Pools for Music Prediction" at Society for Music Perception and Cognition conference, Toronto
11 |
12 | code::
13 | m = MarkovPool();
14 |
15 | c = Array.fill(10000,{[0,2,4,5,7,9,11,12,14,16,17,19,21,23].choose});
16 |
17 | c[0..200].plot
18 |
19 | m.train(c)
20 |
21 | d = Array.fill(1000,{[0,2,4,5,7,9].choose});
22 |
23 | m.generate(Array.fill(10,{([0,2,4,5,7,9]).choose}),100).plot
24 |
25 |
26 | m = MarkovPool(6,12,5);
27 |
28 | c = [0,1,2,0,0,1,2,0,2,3,4,2,3,4,4,5,4,3,2,0,0,0,4,5,4,3,2,0,0,0,0,4,0,0,0,4,0,0]
29 |
30 | c.plot
31 |
32 | m.train(c)
33 |
34 | d = m.generate([0,1,2,0],10)
35 |
36 | d.plot
37 |
38 | Pseq(\degree,d,\dur,0.5).play
39 |
40 | ::
41 |
42 |
43 |
44 | examples::
45 |
46 |
--------------------------------------------------------------------------------
/SCMIRExtensions/HelpSource/Classes/NaiveBayes.schelp:
--------------------------------------------------------------------------------
1 | title:: NaiveBayes
2 | summary:: Naive Bayes Classifier
3 | categories:: SCMIR, Machine Learning
4 | related:: Classes/NeuralNet, Classes/GMM
5 |
6 | description::
7 |
8 | Supervised machine learning class.
9 |
10 | code::
11 | n = NaiveBayes(10,4); //numfeatures = 10, numclasses = 4
12 |
13 | //generate data which overlaps a bit to cause potential confusion in generalisation
14 | (
15 |
16 | //generate random test example 10 dimensional feature vectors as a function of class
17 | ~example = {|class| {class*rrand(0.0,1.0)+(class*0.1)+(0.2.rand2)}!10};
18 |
19 | d = Array.fill(1000,{
20 | var class = 4.rand;
21 |
22 | //array of 10 dimensions for each data point, one class
23 | [~example.(class),class]
24 | })
25 | )
26 |
27 | //data d is in form of array of [features array,class]
28 |
29 | n.train(d)
30 |
31 | n.means //40 of these = 10*4
32 | n.stddevs
33 |
34 | //2 gets mistaken for 3 or 1 occasionally, generally pretty well separated (given large first term anyway)
35 | 100.do{ n.test(~example.(2)).postln }
36 |
37 | ::
38 |
39 |
40 |
41 | examples::
42 |
43 |
--------------------------------------------------------------------------------
/SCMIRExtensions/HelpSource/Classes/PPMC.schelp:
--------------------------------------------------------------------------------
1 | title:: PPMC
2 | summary:: Prediction by Partial Match - 'C' variant, Variable order Markov Model
3 | categories:: SCMIR, Machine Learning
4 | related:: Classes/NeuralNet, Classes/GMM
5 |
6 | description::
7 |
8 | Machine learning class for discrete state sequences. See:
9 |
10 | Ron Begleiter, Ran El-Yaniv and Golan Yona (2004) 'On Prediction Using Variable Order Markov Models.' Journal of Artificial Intelligence Research 22: 385-421
11 |
12 | and
13 |
14 | Marcus Pearce and Geraint Wiggins (2004) 'Improved Methods for Statistical Modelling of Monophonic Music.' Journal of New Music Research 33(4)
15 |
16 | This implementation uses +1 in the numerator and demoninator in the probability calculations, (what Pearce and Wiggins denote the PPM-AX variant of C) and no exclusion mechanism, just escape.
17 |
18 |
19 | code::
20 |
21 | m = PPMC(2); //order =2 (last 2 states predict next)
22 |
23 | //sequence for training; can be array of integers, or a String (as Array of characters)
24 | a =Array.fill(100,{ 6.rand })
25 |
26 | m.train(a)
27 |
28 | m.trie.postcs
29 |
30 | a
31 |
32 | m.probability([19])
33 |
34 |
35 | m.averagelogloss(a)
36 |
37 | m.averagelogloss(a.reverse)
38 |
39 | m.averagelogloss(a.rotate(16))
40 |
41 | m.averagelogloss(Array.fill(100,{ 6.rand }))
42 |
43 | m.averagelogloss(Array.fill(100,{ 4.rand }))
44 |
45 | //empty context
46 | m.generate
47 |
48 | m.generate(a[0..1]);
49 |
50 |
51 | (
52 | var lastvals = List[];
53 |
54 | b = Array.fill(100,{
55 |
56 | var nextval = m.generate(lastvals.reverse);
57 |
58 | if(lastvals.size<3) {lastvals.addFirst(nextval);} {
59 |
60 | lastvals.addFirst(nextval);
61 | lastvals.pop;
62 |
63 | };
64 |
65 | nextval;
66 | });
67 |
68 | )
69 |
70 |
71 | b.plot
72 | a.plot
73 |
74 | m.averagelogloss(b)
75 |
76 | m.averagelogloss(a)
77 |
78 | //b can be more likely than a according to the model; b was generated from model's stats, which approximate a but are not the same as a
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 | /////////////////////////////////////////////////////////TESTS
92 |
93 | //test of save load on large object
94 |
95 | p = PPMC(5)
96 |
97 | a =Array.fill(100000,{ 6.rand })
98 |
99 | p.train(a)
100 |
101 | p.trie.postcs
102 |
103 | p.save("/Users/nickcollins/Desktop/testme.scmirz")
104 |
105 | p = PPMC(5);
106 | p.load("/Users/nickcollins/Desktop/testme.scmirz")
107 |
108 |
109 |
110 | //test of recursive save load on trie
111 |
112 | m = PPMC(5); //order =2 (last 2 states predict next)
113 |
114 | //sequence for training; can be array of integers, or a String (as Array of characters)
115 | a = Array.fill(10000,{ 6.rand })
116 |
117 | m.train(a)
118 |
119 | m.trie.postcs
120 |
121 | (
122 | w = SCMIRZArchive.write("/Users/nickcollins/Desktop/testppmc.scmirZ");
123 |
124 | m.saveDictionary(w,m.trie,0);
125 |
126 | w.writeClose;
127 | )
128 |
129 |
130 | (
131 | r = SCMIRZArchive.read("/Users/nickcollins/Desktop/testppmc.scmirZ");
132 |
133 | d= m.loadDictionary(r,0);
134 |
135 | r.close;
136 | )
137 |
138 | d == m.trie
139 |
140 | d.postcs
141 |
142 |
143 | ::
144 |
145 |
146 |
147 | examples::
148 |
149 |
--------------------------------------------------------------------------------
/SCMIRExtensions/HelpSource/Classes/SARDNET.schelp:
--------------------------------------------------------------------------------
1 | title:: SARDNET
2 | summary:: SARDNET temporal sequence self-organising map
3 | categories:: SCMIR, Machine Learning
4 | related:: Classes/SOM, Classes/NeuralNet
5 |
6 | description::
7 |
8 | 1-D self-organising map for vector sequences (Kohonen map with temporal dynamics)
9 |
10 | James, D. L., & Miikkulainen, R. (1995). SARDNET: A self-organizing feature map for sequences. Advances in neural information processing systems, 7, 577.
11 |
12 | code::
13 |
14 | //100 representatives, 10 dimensions
15 | a=SARDNET(10, 100, 1000, 0.5, 0.5);
16 |
17 | a.numrepresentatives
18 | a.representatives
19 |
20 |
21 | a.train(Array.fill(10,{Array.rand(10,0.0,1.0)}));
22 |
23 | a.representatives //representatives are perturbed by what they contain
24 |
25 |
26 |
27 | a.test(Array.fill(10,{Array.rand(10,0.0,1.0)}))
28 |
29 | //output is list [[index in map, activation score] ... ] where final element will have activation score 1.0 and all earlier ones drop off by 0.75 multiplier. Not really so useful! Only critical data here is the ordering of the nodes invoked
30 |
31 |
32 | a.test(Array.fill(10,{Array.rand(10,0.0,1.0)})).collect{|val| val[0]}
33 |
34 | //Node list gives a discretisation
35 |
36 |
37 | //test with SCMIR; real feature vector sequences extracted from a piece
38 | //may need to keep an eye on learning rate and topological spread
39 |
40 |
41 | f = SCMIRAudioFile("/data/audio/mirdata/pixiesivebeentired.wav", [[MFCC, 13], [Chromagram, 12]]);
42 |
43 | {f.extractFeatures();}.fork; //wait for me to finish
44 |
45 | f.featuredata
46 |
47 | f.numframes
48 |
49 | f.numframes.div(43) //181
50 |
51 | f.numfeatures
52 |
53 |
54 | (
55 | var siz = 43* f.numfeatures;
56 |
57 | ~reps = Array.fill(f.numframes.div(43), {|i|
58 |
59 | var frameindex = 43*i;
60 | var index = f.numfeatures * frameindex;
61 |
62 | Array.fill(43,{|j| var base = index + (j*f.numfeatures); f.featuredata.copyRange(base, base + f.numfeatures-1); });
63 |
64 | })
65 | )
66 |
67 |
68 | a=SARDNET(f.numfeatures, 100, 300, 0.5, 0.5);
69 |
70 | a.representatives.size
71 |
72 | ~reps.do{|array| a.train(array); };
73 |
74 |
75 |
76 | ~reps[9].postcs
77 |
78 |
79 | a.test(~reps[10]).collect{|val| val[0]};
80 |
81 |
82 |
83 | ~results = ~reps.collect{|array| a.test(array).collect{|val| val[0]}};
84 |
85 |
86 | ~results.postcs
87 |
88 |
89 | ~results[1].plot
90 | ~results[115].plot
91 |
92 | //distance between components (Manhattan)
93 | (~results[1] - ~results[21]).abs.sum
94 | (~results[1] - ~results[121]).abs.sum
95 |
96 | //can use to construct a similarity matrix
97 |
98 | 181* 181 entries = 32761 pixels
99 |
100 | ~data = Array.fill(181,{|i| Array.fill(181,{|j| (~results[i] - ~results[j]).abs.sum }); }).flat
101 |
102 | //~data.flatten
103 |
104 | ~data = ~data/(~data.maxItem)
105 |
106 | b = SCMIRSimilarityMatrix.newFromMatrix(~data,181,181);
107 |
108 | b.plot(2,0.5)
109 |
110 | c = b.novelty(10)
111 |
112 | c.plot
113 |
114 | novelty
115 |
116 |
117 | g = SCMIR.peakPick(c.normalize);
118 |
119 | g[0]
120 | g[1]
121 |
122 | //a couple correspond to song structural features but not overly impressive
123 | List[ 9, 36, 71, 98, 131, 166 ] //corresponds to seconds through piece, roughly
124 |
125 | ::
126 |
127 |
128 |
129 | examples::
130 |
131 |
--------------------------------------------------------------------------------
/SCMIRExtensions/HelpSource/Classes/SCMIR.schelp:
--------------------------------------------------------------------------------
1 | title:: SCMIR
2 | summary:: SuperCollider Music Information Retrieval library main class
3 | categories:: SCMIR
4 | related:: Classes/SCMIRAudioFile, Classes/SCMIRSimilarityMatrix, Classes/SCMIRLive
5 |
6 | description::
7 |
8 | by Nick Collins.
9 |
10 | This extension library (with additional plugins and language invoked executables) for SuperCollider demonstrates carrying out music information retrival tasks in SuperCollider for audio content analysis. You can extract features for an audio file in NRT mode, plot features, get similarity matrices, plot them, extract novelty curves from the similarity matrices and thus find section boundaries, and more. The library is robust to batch processing over larger databases of hundreds of audio files in overnight runs.
11 |
12 | By default, \tmp is used for storing temporary files required in calculation, which should be robust to network permissions on Mac and Linux. The SCMIR base class allows you to set an alternative base directory for storing SCMIRAudioFile analysis data (.scmirZ format). Note that file permissions can be an issue on network systems; for full access it is suggested that you work with a local copy of SuperCollider where you have write permissions. Do not try to analyze audio files from directories where you have no write access if you want to write out analysis files alongside them.
13 |
14 |
15 | examples::
16 |
17 | code::
18 |
19 | SCMIR.setTempDir("/Users/nickcollins/Desktop/SCMIRTest");
20 |
21 | SCMIR.tempdir
22 |
23 | //don't use a tempdir, saved .scmirZ archives appear in the same directory as any target sound file
24 | SCMIR.setTempDir(nil);
25 |
26 | SCMIR.tempdir
27 |
28 |
29 | SCMIR.setFrameHop(512); //default is 1024. Only these two numbers allowed for now. Note that changing this affects all open and to be loaded SCMIRAudioFiles; it may invalidate settings. It should only be used on a per complete project basis
30 |
31 | SCMIR.setFrameHop(1024); //restore default
32 |
33 | ::
34 |
--------------------------------------------------------------------------------
/SCMIRExtensions/HelpSource/Classes/SCMIRLive.schelp:
--------------------------------------------------------------------------------
1 | title:: SCMIRLive
2 | summary:: Represents a live feature extractor for the SCMIR analysis system
3 | categories:: SCMIR
4 | related:: Classes/SCMIRAudioFile, Classes/SCMIRSimilarityMatrix, Classes/SCMIR
5 |
6 | description::
7 |
8 | Run feature extraction and normalization live.
9 |
10 |
11 | examples::
12 |
13 | code::
14 |
15 | //extract features for a given file
16 | e = SCMIRAudioFile(Platform.resourceDir +/+ "sounds/a11wlk01.wav", [[MFCC, 13], [Chromagram, 12]]);
17 |
18 | e.extractFeatures(normalize:false); //must not already normalize unless you will manually pass over the normalization data; SCMIRLive will deal with that in next step
19 |
20 | //now convert to live feature extractor
21 | l = SCMIRLive(e)
22 |
23 | l.dump
24 | l.featureinfo
25 | l.normalizationinfo
26 |
27 | //create SynthDef for this feature set with normalization
28 | l.createSynthDef.add
29 | //save to disk SynthDef with own specified name, no normalization
30 | //l.createSynthDef(\SCMIRLiveownname,false).writeDefFile
31 |
32 | l.createResponder({arg input; input.postln }); //create a language side responder for this feature extractor
33 |
34 | //run Synth with the SynthDef already added, input bus is 8, which is first audio input on machine
35 | a = l.run(8)
36 |
37 | l.synth
38 |
39 | l.removeResponder
40 |
41 |
42 | //store for future use (assumes saved SynthDef as well, or at least will createSynthDef on reload before live use
43 | l.save("/tmp/scmirlive1.scmirZ");
44 |
45 | l.load("/tmp/scmirlive1.scmirZ");
46 |
47 | l.createSynthDef.add //make a new SynthDef: required for safety after load to
48 | avoid repeated names over multiple sessions
49 |
50 | a = l.run(8)
51 |
52 | //short cut creation
53 | l = SCMIRLive.newFromFile("/tmp/scmirlive1.scmirZ");
54 |
55 |
56 |
57 |
58 | //using with global norms
59 |
60 | //calculate global norms
61 | (
62 | ~files = [Platform.resourceDir +/+ "sounds/a11wlk01.wav", "/data/audio/mirdata/pixiesivebeentired.wav","/data/audio/mirdata/Yellow Submarine.wav"];
63 | ~featureinfo= [[MFCC,13],[Chromagram,12]];
64 |
65 | {SCMIR.findGlobalFeatureNorms(~files,~featureinfo); }.fork
66 |
67 | )
68 |
69 | SCMIR.globalfeaturenorms //in this case, will be minimums in first array, maximums in second
70 |
71 |
72 | //no need to extract fresh features in this case
73 | e = SCMIRAudioFile(Platform.resourceDir +/+ "sounds/a11wlk01.wav", [[MFCC, 13], [Chromagram, 12]]);
74 |
75 | //create SCMIRLive via this
76 | l = SCMIRLive(e, SCMIR.globalfeaturenorms)
77 |
78 | l.normalizationinfo
79 |
80 | l.createSynthDef.add
81 |
82 | l.createResponder({arg input; input.postln }); //create a language side responder for this feature extractor
83 |
84 | //run Synth with the SynthDef already added, input bus is 8, which is first audio input on machine
85 | a = l.run(8)
86 |
87 |
88 |
89 |
90 | //example with live feature view GUI
91 |
92 |
93 | //extract features for a given file
94 | e = SCMIRAudioFile(Platform.resourceDir +/+ "sounds/a11wlk01.wav", [[MFCC, 13], [Chromagram, 12]]);
95 |
96 | e.extractFeatures(normalize:false);
97 |
98 | //now convert to live feature extractor
99 | l = SCMIRLive(e)
100 |
101 | //create SynthDef for this feature set with normalization; server must be booted already
102 | l.createSynthDef.add
103 |
104 | //create GUI
105 | (
106 | w=Window.new("25 features", Rect(200,400,300,300));
107 |
108 | ~ms= MultiSliderView.new(w, Rect(10,10,260,280));
109 |
110 | ~ms.value_(Array.fill(25,0.0));
111 | ~ms.valueThumbSize_(300/25);
112 | ~ms.indexThumbSize_(300/25);
113 | ~ms.gap_(0);
114 |
115 | w.front;
116 | )
117 |
118 |
119 | (
120 | l.createResponder({arg input;
121 |
122 | //input.postln;
123 | {~ms.value_(input[3..(27)]); }.defer
124 | }); //create a language side responder for this feature extractor which updates the GUI
125 | )
126 |
127 | //run Synth with the SynthDef already added, input bus is 8, which is first audio input on machine
128 | a = l.run(8);
129 |
130 | l.removeResponder;
131 |
132 | w.close;
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 | ::
141 |
--------------------------------------------------------------------------------
/SCMIRExtensions/HelpSource/Classes/SCMIRSimilarityMatrix.schelp:
--------------------------------------------------------------------------------
1 | title:: SCMIRSimilarityMatrix
2 | summary:: Similarity matrix calculation and resources including plotting and DTW
3 | categories:: SCMIR
4 | related:: Classes/SCMIRAudioFile, Classes/SCMIR, Classes/SCMIRLive
5 |
6 | description::
7 |
8 | by Nick Collins.
9 |
10 | An instance of the SCMIRSimilarityMatrix class calculates and stores a distance matrix over a single input sequence (self-similarity) or between two sequences. The sequences can be multi-dimensional feature vector sequences, though they must be in a flattened array format:
11 | [feature0 at time0, feature 1 at time 0, ... feature dimensions-1 at time 0, feature 0 at time 1, ...]
12 |
13 | Note that the cosine metric only gives non zero similarity values if the number of feature dimensions is greater than 1. Note also that the similarity value is 0.0 if two points are maximally close (hence, it is a distance matrix).
14 |
15 | The internal code operates on FloatArrays (due to interaction with external processing and need for RawArrays in file output and input), and auto conversion is imposed at input if required.
16 |
17 | The dtw method returns [minimum path cost, best path] where the best path is a List of (x,y) co-ordinates into the matrix. Larger x is further along sequence1, larger y is further along the other sequence (sequence 1 again for self similarity, or a different sequence 2).
18 |
19 |
20 | code::
21 |
22 | //self similarity demo
23 |
24 | a = FloatArray[0.05,0.1,0.5,0.8,1.2,1.4];
25 |
26 | m = SCMIRSimilarityMatrix(1,a); //1 dimensional data, stored in a
27 |
28 | m.sequence1 //a
29 | m.sequence2 //also a
30 | m.self //1 = self similarity matrix
31 | m.columns //size of a
32 | m.rows //also size of a
33 | m.dimensions //1
34 |
35 | m.calculate(1,1); //create matrix of unit 1, metric 1 = Manhattan
36 |
37 | m.matrix
38 |
39 | m.plot(100);
40 |
41 | m.dtw //should get back diagonal as cheapest path
42 |
43 |
44 |
45 | //two sequences
46 | a = [0.0,0.1,0.5,0.8,1.2,1.4];
47 | b = [0.03,0.05,0.3,0.7,0.8,1.1,1.38,0.4,1.1];
48 |
49 | //comparison
50 | m = SCMIRSimilarityMatrix(1,a,b); //1 dimensional data, stored in a and b
51 |
52 | //note how order swapped since first array should have largest size
53 | m.sequence1 //converted to FloatArray internally (required for file writing for invoking calculation externals)
54 | m.sequence2 //converted to FloatArray internally (required for file writing for invoking calculation externals)
55 |
56 | m.calculate(1,1); //create matrix, won't be square since a and b are different lengths
57 |
58 | m.matrix
59 |
60 | m.matrix.plot //as linear array; order is d(a(0), b(0)), d(a(0, b(1))... d(a(1),b(0))...d(a(last),b(last))
61 |
62 | m.plot(100) //built in matrix plotting, stretch factor of 10 here
63 |
64 | m.dtw; //get best path and distance of the path
65 |
66 |
67 |
68 | //use with SCMIRAudioFile
69 |
70 | e = SCMIRAudioFile("sounds/a11wlk01.wav", [[MFCC, 13], [Chromagram, 12]]);
71 |
72 | //shortcut versions also work, defaults will be applied for MFCC (10 coeffs) and Chromagram (12TET)
73 | //e = SCMIRAudioFile("/Applications/SuperCollider/SuperCollider3.4/sounds/a11wlk01.wav",[MFCC,Chromagram]);
74 |
75 | e.extractFeatures(); //wait for me to finish
76 |
77 | e.numframes
78 |
79 | m = e.similarityMatrix(1,0); //m is an SCMIRSimilarityMatrix object
80 |
81 | m.matrix.size.sqrt //184
82 |
83 | m.plot(2) //built in matrix plotting, stretch factor of 10 here
84 |
85 | a = m.dtw
86 |
87 | m.plot(2,path:a) //plot with best dtw path
88 |
89 | m.reducedcolumns
90 | m.reducedrows
91 |
92 | m.matrix.postcs
93 |
94 |
95 |
96 |
97 | //comparison of two audio files
98 |
99 |
100 |
101 | (
102 | ~files = ["/data/audio/mirdata/pixiesivebeentired.wav","/data/audio/mirdata/Yellow Submarine.wav"];
103 |
104 | ~audio = ~files.collect{|filename,i|
105 |
106 | e = SCMIRAudioFile(filename, [MFCC,Loudness]);
107 |
108 | e.extractFeatures();
109 |
110 | };
111 |
112 | m = ~audio[0].similarityMatrix(40,0,other:~audio[1]);
113 | )
114 |
115 | ~audio[0].numfeatures
116 | (~audio[0].numframes).div(40)
117 | (~audio[1].numframes).div(40)
118 |
119 | m.reducedcolumns //194
120 | m.reducedrows //172
121 |
122 | d = m.dtw
123 |
124 | m.plot(2,1, path:d) //show path on the similarity matrix plot
125 |
126 |
127 |
128 | //very minimal test
129 |
130 | //SCMIR.setTempDir("/Users/nickcollins/Desktop/");
131 |
132 | a = FloatArray[0.05,0.1,0.9];
133 |
134 | m = SCMIRSimilarityMatrix(1,a); //1 dimensional data, stored in a
135 |
136 | m.calculate(1,2); //create matrix
137 |
138 | m.matrix
139 |
140 | d = m.dtw //should get back diagonal as cheapest path
141 |
142 | m.plot(100,path:d)
143 |
144 |
145 |
146 | //check alternative options in similaritymatrix2 external
147 |
148 | a = FloatArray[0.05,0.1,0.9];
149 |
150 | m = SCMIRSimilarityMatrix(1,a); //1 dimensional data, stored in a
151 |
152 | m.calculate(1,2,0,0); //create matrix, max within segments rather than mean
153 |
154 | m.matrix
155 |
156 | d = m.dtw //should get back diagonal as cheapest path
157 |
158 | m.plot(100,path:d)
159 |
160 | m.calculate(1,2,1,0); //create matrix, max within segments rather than mean, post calculate (make large similarity matrix then reduce)
161 |
162 | m.matrix //same results
163 |
164 | d = m.dtw //should get back diagonal as cheapest path
165 |
166 | m.plot(100,path:d)
167 |
168 |
169 |
170 |
171 | //check that post reduction also works on larger scale
172 | e = SCMIRAudioFile("/Applications/SuperCollider/SuperCollider3.4/sounds/a11wlk01.wav", [[MFCC, 13], [Chromagram, 12]]);
173 |
174 | e.extractFeatures();
175 |
176 | m = e.similarityMatrix(2,0,1,0); //m is an SCMIRSimilarityMatrix object
177 |
178 | m.matrix.size.sqrt //92
179 |
180 | m.plot(2) //built in matrix plotting, stretch factor of 10 here
181 |
182 | a = m.dtw
183 |
184 | m.plot(2,path:a) //plot with best dtw path
185 |
186 |
187 | //directly create a SCMIRSimilarityMatrix object with existing matrix data
188 |
189 | //16 element array, 4 by 4 matrix
190 | a = SCMIRSimilarityMatrix.newFromMatrix([0.1,0.3,0.5,0.2,0.3,0.1,0.0,0.7,0.3,0.5,0.0,1.0,0.1,0.4,0.7,0.3],4,4)
191 | //data is in form col1, col2, col3, ... where each column starts from the bottom row and goes to the top
192 |
193 | a.plot(100)
194 |
195 | a.novelty(2).plot
196 |
197 |
198 |
199 | ::
200 |
201 |
202 | examples::
203 |
204 |
205 |
--------------------------------------------------------------------------------
/SCMIRExtensions/HelpSource/Classes/SOM.schelp:
--------------------------------------------------------------------------------
1 | title:: SOM
2 | summary:: 1 dimensional self organising map
3 | categories:: SCMIR, Machine Learning
4 | related:: Classes/SARDNET, Classes/NeuralNet
5 |
6 | description::
7 |
8 | 1-dimensional self organising map for feature vectors, using the Manhattan metric for finding closest matches
9 |
10 | examples::
11 |
12 | code::
13 |
14 | a=SOM([[1,0,1,0],[1,1,1,1],[0,0,0.5,0.3]], 100, 0.5, 0.5);
15 |
16 | a.representatives
17 |
18 | a.addsample([0.7,0.2,0.1,0.6]);
19 |
20 | a.representatives //representatives are perturbed by what they contain
21 |
22 | (
23 | //random set of vectors from b= Array.fill(10,{Array.rand(4,0.0,1.0)}); Post << b << nl;
24 | b= [ [ 0.51869738101959, 0.080885529518127, 0.31222653388977, 0.69995737075806 ], [ 0.6439403295517, 0.1963427066803, 0.17634284496307, 0.14897918701172 ], [ 0.45320868492126, 0.23293280601501, 0.54554259777069, 0.070706605911255 ], [ 0.89824318885803, 0.7641407251358, 0.6628520488739, 0.75039482116699 ], [ 0.56789028644562, 0.086152672767639, 0.13435745239258, 0.69306933879852 ], [ 0.084696531295776, 0.9174644947052, 0.91573286056519, 0.28425991535187 ], [ 0.80475306510925, 0.21441471576691, 0.57193303108215, 0.9413183927536 ], [ 0.40608489513397, 0.23033368587494, 0.48240029811859, 0.44695520401001 ], [ 0.066552996635437, 0.17492353916168, 0.97465658187866, 0.77240884304047 ], [ 0.23405659198761, 0.70120179653168, 0.57822358608246, 0.39226639270782 ] ];
25 | )
26 |
27 | b.do({arg val; a.addsample(val);});
28 |
29 | a.representatives //representatives are perturbed by what they contain
30 |
31 | a.lists //show how those vectors were allocated, uneven assignment
32 |
33 | ::
34 |
35 |
36 |
--------------------------------------------------------------------------------
/SCMIRExtensions/HelpSource/Classes/VOGUE.schelp:
--------------------------------------------------------------------------------
1 | title:: VOGUE
2 | summary:: VOGUE
3 | categories:: SCMIR, Machine Learning
4 | related:: Classes/PPMC, Classes/MarkovModel, Classes/HMM, Classes/MarkovPool
5 |
6 | description::
7 | A Variable Order Hidden Markov Model with Duration based on Frequent Sequence Mining
8 | Mohammed J. Zaki et al. ACM Transactions on Knowledge Discovery from Data 4(1)
9 |
10 | For 2-sequences only (doesn't model 1-sequences either).
11 |
12 | Uses the auxilliary VariableGapSequenceMining class.
13 |
14 | code::
15 | ~vgs = VariableGapSequenceMining();
16 | ~vgs.train([0,1,3,2,4,3,2,10,1,3,0,1,2,10,4,3]);
17 |
18 | ~vgs.freqaj(2)
19 |
20 | ~vgs.freqqs1(~vgs.statedata[0].size)
21 |
22 |
23 | a = ~vgs.subsequences
24 |
25 | a.keys
26 |
27 | ~vgs.statedata.flatten.size
28 |
29 |
30 | b = Dictionary();
31 | b[0] = 1
32 | b[2] = [1,[3,4]]
33 |
34 | b.keysDo{|key,i| i.postln}
35 |
36 |
37 |
38 | ~vgs = VariableGapSequenceMining();
39 |
40 | ~source = "ACBDAHCBADFGAIEB".ascii - 65; //as per paper
41 |
42 | ~vgs.train(~source);
43 |
44 | a = ~vgs.subsequences
45 |
46 | a.keys
47 |
48 | v = VOGUE(~vgs)
49 | v.alphabetsize
50 | v.numqs1
51 | v.numqg
52 | v.numqs2
53 | v.numqu
54 | v.qindices
55 | v.train
56 |
57 |
58 | (
59 | ~vgs = VariableGapSequenceMining();
60 | ~source = "ACBDAHCBADFGAIEB".ascii - 65; //as per paper
61 | ~vgs.train(~source);
62 | v = VOGUE(~vgs);
63 | v.train;
64 | )
65 |
66 | v.emissionprobabilitymatrix.postcs
67 | v.statetransitionmatrix.postcs
68 | v.statedurationprobabilities.postcs
69 | v.initialstateprobabilities
70 |
71 | v.generate(100);
72 |
73 |
74 | (
75 | ~vgs = VariableGapSequenceMining();
76 | ~source = [60,62,64,60,60,62,64,60,64,65,67,64,65,67,67,69,67,65,64,60,67,69,67,65,64,60,60,55,60,60,55,60]; //frere jacques
77 | ~vgs.train(~source);
78 | v = VOGUE(~vgs);
79 | v.train;
80 | v.generate(100);
81 | )
82 |
83 | Pbind(\midinote,Pseq(v.generate(1000),1),\dur,0.125).play
84 |
85 | ::
86 |
87 |
88 |
89 | examples::
90 |
91 |
--------------------------------------------------------------------------------
/SCMIRExtensions/examples/artistrecognitionviaNN.scd:
--------------------------------------------------------------------------------
1 | //small scale demo of artist recognition (using tracks from late 70s/early 80s synth pop project); requires SC Neural Net class
2 |
3 |
4 | (
5 | //10 Depeche Mode tracks
6 | ~dm = ["/data/audio/mirdata/synthpop/Speak & Spell/01 New Life.wav", "/data/audio/mirdata/synthpop/Speak & Spell/02 I Sometimes Wish I Was Dead (2006 Digital Remaster).wav", "/data/audio/mirdata/synthpop/Speak & Spell/03 Puppets (2006 Digital Remaster).wav", "/data/audio/mirdata/synthpop/Speak & Spell/04 Boys Say Go! (2006 Digital Remaster).wav", "/data/audio/mirdata/synthpop/Speak & Spell/05 Nodisco (2006 Digital Remaster).wav", "/data/audio/mirdata/synthpop/Speak & Spell/06 What's Your Name_.wav", "/data/audio/mirdata/synthpop/Speak & Spell/07 Photographic (2006 Digital Remaster).wav", "/data/audio/mirdata/synthpop/Speak & Spell/08 Tora! Tora! Tora! (2006 Digital Remaster).wav", "/data/audio/mirdata/synthpop/Speak & Spell/09 Big Muff (2006 Digital Remaster).wav", "/data/audio/mirdata/synthpop/Speak & Spell/10 Any Second Now (Voices) (2006 Digital Remaster).wav"];
7 |
8 | //10 David Bowie tracks
9 | ~bowie = ["/data/audio/mirdata/synthpop/Lodger/01 Fantastic Voyage.wav", "/data/audio/mirdata/synthpop/Lodger/02 African Night Flight.wav", "/data/audio/mirdata/synthpop/Lodger/03 Move On.wav", "/data/audio/mirdata/synthpop/Lodger/04 Yassassin (Long Live).wav", "/data/audio/mirdata/synthpop/Lodger/05 Red Sails.wav", "/data/audio/mirdata/synthpop/Lodger/06 D.J..wav", "/data/audio/mirdata/synthpop/Lodger/07 Look Back In Anger.wav", "/data/audio/mirdata/synthpop/Lodger/08 Boys Keep Swinging.wav", "/data/audio/mirdata/synthpop/Lodger/09 Repetition.wav", "/data/audio/mirdata/synthpop/Lodger/10 Red Money.wav"];
10 |
11 | //10 Yellow Magic Orchestra tracks
12 | ~ymo = ["/data/audio/mirdata/synthpop/Yellow Magic Orchestra/01 Computer Game _Theme From The Circus_.wav", "/data/audio/mirdata/synthpop/Yellow Magic Orchestra/02 Firecracker.wav", "/data/audio/mirdata/synthpop/Yellow Magic Orchestra/03 Simoon.wav", "/data/audio/mirdata/synthpop/Yellow Magic Orchestra/04 COSMIC SURFIN'.wav", "/data/audio/mirdata/synthpop/Yellow Magic Orchestra/05 Computer Game _Theme From The Invader_.wav", "/data/audio/mirdata/synthpop/Yellow Magic Orchestra/06 tong poo.wav", "/data/audio/mirdata/synthpop/Yellow Magic Orchestra/07 la femme chinoise.wav", "/data/audio/mirdata/synthpop/Yellow Magic Orchestra/08 Bridge Over Troubled Music.wav", "/data/audio/mirdata/synthpop/Yellow Magic Orchestra/09 Mad Pierrot.wav", "/data/audio/mirdata/synthpop/Yellow Magic Orchestra/10 Acrobat.wav"];
13 | )
14 |
15 |
16 | //extract features and summarise whole tracks by feature means (crude, but let's just try)
17 | (
18 | {
19 | ~featuredata = [~dm,~bowie,~ymo].collect{|array,i|
20 |
21 | array.collect{|filename|
22 | var file;
23 |
24 | file = SCMIRAudioFile(filename, [[MFCC, 20],SpecCentroid]);
25 |
26 | //shortcut versions also work, defaults will be applied for MFCC (10 coeffs) and Chromagram (12TET)
27 | //e = SCMIRAudioFile("/Applications/SuperCollider/SuperCollider3.4/sounds/a11wlk01.wav",[MFCC,Chromagram]);
28 |
29 | file.extractFeatures();
30 |
31 | file.gatherFeaturesBySegments([0.0]);
32 |
33 | file.featuredata;
34 |
35 | };
36 |
37 | }
38 | }.fork
39 | )
40 |
41 |
42 | ~featuredata[0][0].size //21
43 |
44 | ~featuredata.size //3 artists
45 |
46 |
47 |
48 | //training based on 7 from each class, test on 10
49 |
50 | //use binary with
51 | NeuralNet.pathToNeuralNetBinary = "/data/sussex/code/SC/sclangextensions/neuralnetSCtoC/NeuralNet/build/Release/";
52 |
53 | n = NeuralNet(21,21,3); //fast learning rate and initial weights -1.0 to 1.0
54 |
55 | d= ~featuredata.collect{|array,i| var output = 0.0!(~featuredata.size); output[i]= 1.0; array[0..6].collect{|instance| [instance.asArray,output]} }.flatten;
56 |
57 | n.trainExt(d,0.01,10000); //train up, with status updates, max of 1000 epochs or total error
58 |
59 | d.size
60 |
61 | //testing
62 | n.calculate(~featuredata[0][0]);
63 |
64 |
65 | //confusion matrix
66 | ~featuredata.collect{|array| array.collect{|song| n.calculate(song).maxIndex; }}
67 |
68 | //result:
69 | [ [ 0, 0, 0, 0, 0, 0, 0, 0, 1, 2 ], [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 0 ], [ 2, 2, 2, 2, 2, 2, 2, 2, 2, 0 ] ]
70 | //for example, any second now by DM 1981 confused with yellow magic orchestra; last three in each weren't seen before, and that's where errors arise, so not generalising very well. And this only from one fold.
71 |
72 |
73 |
--------------------------------------------------------------------------------
/SCMIRExtensions/examples/batchextractinparallel.scd:
--------------------------------------------------------------------------------
1 | //testing batch extraction in parallel
2 |
3 | (
4 | //wavs
5 | ~filenames = PathName("/data/audio/mirdata/EDM/Dance Decade later 1990s/").entries.collect{|p| p.fullPath};
6 |
7 | //MP3s
8 | //~filenames = ["Radiohead","Kirsty MacColl"].collect{|name| PathName("/data/audio/mirdata/careercomparison/"++name++"/").entries.collect{|album| album.entries.collect{|p| p.fullPath}}.flatten; }.flatten;
9 |
10 | // ~filenames = ["Radiohead","REM","Coldplay","Buddy Holly","PatsyCline","Kirsty MacColl"].collect{|name| PathName("/data/audio/mirdata/careercomparison/"++name++"/").entries.collect{|album| album.entries.collect{|p| p.fullPath}}.flatten; }.flatten;
11 |
12 | //if needed and precalculated
13 | //SCMIR.loadGlobalFeatureNorms("/data/audio/mirdata/careercomparison/norms.scmirZ");
14 |
15 | //may take a little longer than the completion function call due to sub forked processes
16 | //default segmentation (feature means within each song)
17 | SCMIR.batchExtractParallel(~filenames,[[MFCC, 13], [Chromagram, 12]],false, false, 0, oncompletion:{|durations, output| ~durations = durations; ~featuremeans = output; });
18 |
19 | //if using global normalisation via SCMIR.globalfeaturenorms
20 | // ~normmode = 0;
21 | // SCMIR.batchExtractParallel(~filenames,[[MFCC, 13], [Chromagram, 12]],true, true, ~normmode, oncompletion:{|durations, output| ~durations = durations; ~featuremeans = output; });
22 |
23 | )
24 |
25 | ~durations.sum/60 //total duration of audio touched in minutes
26 | ~featuremeans.size //one per song
27 | ~filenames.size
28 | ~featuremeans[2][0].plot //plot mean feature vector for first song (only segmentation, hence [0])
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 | //global normalisation calculation
38 | (
39 | ~normmode = 0; //max-min normalisation
40 |
41 | //wavs
42 | ~filenames = PathName("/data/audio/mirdata/EDM/Dance Decade later 1990s/").entries.collect{|p| p.fullPath};
43 |
44 | SCMIR.findGlobalFeatureNormsParallel(~filenames,[[MFCC, 13], [Chromagram, 12]],~normmode);
45 |
46 | )
47 |
48 |
49 | SCMIR.globalfeaturenorms
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 | //prototyping, now wrapped in global function in SCMIR class
60 | //global normalisation calculation
61 | (
62 | ~normmode = 0; //max-min normalisation
63 |
64 | //wavs
65 | ~filenames = PathName("/data/audio/mirdata/EDM/Dance Decade later 1990s/").entries.collect{|p| p.fullPath};
66 |
67 | /* *batchExtractParallel {|filenames, features, normalize=true, useglobalnormalization=false,normalizationmode=0, whichchannel, segmentations, oncompletion|*/
68 |
69 | // 1 max, 2 min so [[[0.0],1],[[0.0],2]] does two segmentations, taking overall max and min for each feature within a given song
70 | //segmentations:[[[0.0],1],[[0.0],2]]
71 | SCMIR.batchExtractParallel(~filenames,[[MFCC, 13], [Chromagram, 12]],true, false, ~normmode,segmentations:[nil], oncompletion:{|durations, output, framesum|
72 |
73 | [\durations,durations].postln;
74 | [\output,output].postln;
75 |
76 | //to remove array of segmentations since didn't need it
77 | //output = output.collect{|val| val[0]};
78 |
79 | ~durations = durations;
80 | ~output = output.collect{|val| val[0]};
81 | ~framesum = framesum;
82 |
83 | //~norms = SCMIR.findGlobalFeatureNorms([0,~output,~durations,framesum]);
84 | SCMIR.findGlobalFeatureNorms([0,~output,~durations,~framesum],nil,~normmode);
85 |
86 | },normstats:true);
87 |
88 |
89 | )
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
--------------------------------------------------------------------------------
/SCMIRExtensions/examples/customfeature.scd:
--------------------------------------------------------------------------------
1 | //\CustomFeature
2 |
3 |
4 | //write your own custom feature extractor (this must be a closed function if you expect saving to work!)
5 |
6 | //format: [\CustomFeature,{arg input; /*insert feature extraction here operating on input*/},numberofoutputs]
7 | //e.g. [\CustomFeature,{arg input; [RunningSum.ar(input,1024), (input.abs)>0.5]},2]
8 | //numberofoutputs defaults to 1; if your function returns a different number of outputs you'll get into trouble and SCMIR will likely crash on array processing
9 | //note that the output will be re-sampled to the SCMIR frame rate (SCMIR.framerate), so you will lose finer detailed variation and potentially get aliasing; it is suggested you apply your own window based processing via FFT, RunningSum etc
10 |
11 | //control periods per second/frames per second = control periods per frame
12 | //(s.sampleRate/s.options.blockSize)/SCMIR.framerate = 16 (1024/64)
13 |
14 | //one custom feature
15 | e = SCMIRAudioFile(Platform.resourceDir +/+ "sounds/a11wlk01.wav", [[\CustomFeature,{arg input; Median.kr(16,Pitch.kr(input)[0].cpsmidi)}]]);
16 |
17 | {e.extractFeatures(normalize:true)}.fork
18 |
19 | e.plotFeatures(400,600,40, true); //line based plot
20 |
21 |
22 |
23 | (
24 | //4 features extracted + MFCCs
25 | e = SCMIRAudioFile(Platform.resourceDir +/+ "sounds/a11wlk01.wav", [
26 | [MFCC, 13], //MFCCs standard
27 | [\CustomFeature,
28 | {arg input;
29 |
30 | [
31 | input, //input is audio rate, so output will be like a downsampling with aliasing (no low pass filtering applied, could add some; you can do what you like inside a CustomFeature function!)
32 | SinOsc.kr(10), //you can synthesise and ignore analysis...
33 | RunningSum.rms(input,1024),
34 | if(input.abs>0.5,DC.kr(1),DC.kr(0))
35 | ]
36 | }
37 | ,4]]
38 | );
39 | )
40 |
41 | {e.extractFeatures(normalize:true)}.fork
42 |
43 | e.plotFeatures(400,600,40, true); //line based plot
44 |
45 | m = e.similarityMatrix(); //m is an SCMIRSimilarityMatrix object
46 |
47 | m.plot(4,5) //oscillation over image is the synthesized sine's effect, most likely
48 |
49 | //shortcut
50 | e.plotSelfSimilarity(1,4); //unit = 1, stretch = 4, params as above
51 |
52 |
53 |
54 |
55 |
56 | //check equivalence: same parameters for in built Loudness feature extractor and via custom function
57 |
58 | e = SCMIRAudioFile(Platform.resourceDir +/+ "sounds/a11wlk01.wav", [\Loudness, [\CustomFeature,{arg input; Loudness.kr(FFT(LocalBuf(1024),input,1,wintype:1),0.25,1)},1]]);
59 |
60 | {e.extractFeatures(normalize:true)}.fork
61 |
62 | //exactly same results
63 | e.plotFeatures(300,300,40);
64 | e.plotFeatures(400,600,40, true); //line based plot
65 |
66 | e.featuredata
67 | e.numfeatures
68 |
69 | //test save/load
70 | e.save("/tmp/a11wlk01.scmirZ") //save .scmirZ files using ZArchive; binary storage of feature data and other instance variables
71 |
72 | e.load("/tmp/a11wlk01.scmirZ")
73 | //if no filenames are provided, will be placed in temp directory
74 |
75 | e.featureinfo
76 |
77 |
78 |
79 |
80 | //check SCMIRLive
81 |
82 | (
83 | e = SCMIRAudioFile(Platform.resourceDir +/+ "sounds/a11wlk01.wav", [
84 | [\CustomFeature,
85 | {arg input;
86 |
87 | [
88 | LPF.ar(input,20), //low pass this time
89 | SinOsc.kr(10), //you can synthesise and ignore analysis...
90 | RunningSum.rms(input,1024),
91 | if(input.abs>0.25,DC.kr(1),DC.kr(0))
92 | ]
93 | }
94 | ,4]]
95 | );
96 | )
97 |
98 |
99 | //if need proper normalizing need to have not already normalized!
100 | {e.extractFeatures(normalize:false)}.fork
101 |
102 | l = SCMIRLive(e)
103 |
104 | l.dump
105 | l.featureinfo
106 | l.normalizationinfo
107 | [ [ -0.010306200943887, -0.99988812208176, 0.018318351358175, 0 ], [ 0.016430662944913, 0.9999874830246, 0.54670751094818, 1 ] ]
108 |
109 | s.boot; //ready to go live
110 |
111 | //create SynthDef for this feature set with normalization
112 | l.createSynthDef.add
113 |
114 | l.createResponder({arg input; input.postln }); //create a language side responder for this feature extractor
115 |
116 | //run Synth with the SynthDef already added, input bus is 8, which is first audio input on machine
117 | a = l.run(8)
118 |
119 | l.synth
120 |
121 | l.removeResponder
122 |
123 |
124 | //store for future use (assumes saved SynthDef as well, or at least will createSynthDef on reload before live use
125 | l.save("/tmp/scmirlive1.scmirZ");
126 |
127 | l.load("/tmp/scmirlive1.scmirZ");
128 |
129 | l.createSynthDef.add
130 |
131 | a = l.run(8)
132 |
133 | //set up responder again to print outputs; note that normalisation was from previous file and this is now operating on general live input so you may see output values outside of the [0,1] normed range
134 | l.createResponder({arg input; input.postln }); //create a language side responder for this feature extractor
135 |
136 |
137 | //short cut creation
138 | l = SCMIRLive.newFromFile("/tmp/scmirlive1.scmirZ");
139 |
140 |
141 |
142 |
--------------------------------------------------------------------------------
/SCMIRExtensions/examples/differentiatesoundclasseslive.scd:
--------------------------------------------------------------------------------
1 | //classify voice vs solo piano (but could be anything else)
2 |
3 | (
4 | ~filenames = [
5 | "/data/teachingmaterials/electronicmusic/samplingandcopyright/plunderphonics/69:96/27. case of death pt1.mp3",
6 | "/data/teachingmaterials/electronicmusic/samplingandcopyright/plunderphonics/69:96/29. case of death pt.2.mp3", "/data/audio/classical/improv/improv1.wav","/data/audio/classical/improv/20150227_Graphical_Score_2.wav"
7 | ];
8 |
9 | ~class = [0,0,1,1];
10 |
11 | //could use more features, will need to update num inputs and hidden units for neural net below
12 | ~features = [SpecCentroid,SpectralEntropy,AttackSlope,[\CustomFeature,{arg input; //low, mid, high energy comparisons
13 |
14 | [
15 | RunningSum.rms(BLowPass4.ar(input,400,0.5),1024),
16 | RunningSum.rms(BBandPass.ar(input,3000,1),1024),
17 | RunningSum.rms(BHiPass4.ar(input,6000,0.5),1024)
18 | ]
19 |
20 | },3
21 | ]];
22 |
23 |
24 | //for meaningful comparison between audio files, need to get global data on max and min values for features over all files
25 | {
26 | ~durations = SCMIR.findGlobalFeatureNorms(~filenames,~features);
27 |
28 | ~featurenorms = SCMIR.globalfeaturenorms;
29 | }.fork;
30 |
31 | )
32 |
33 |
34 |
35 | (
36 |
37 | var windowsize = 2.0;
38 | var stepsize = 1.0;
39 |
40 | ~featuremeansperwindow = nil!(~filenames.size);
41 |
42 | {
43 |
44 | //.copyRange(0,1)
45 | ~extractions = ~filenames.collect{|filename,i|
46 |
47 | var segmentsarray;
48 | [i,filename].postln;
49 |
50 | f = SCMIRAudioFile(filename,~features);
51 |
52 | //using global normalisation just set up
53 | f.extractFeatures(true,true);
54 |
55 | segmentsarray = Array.fill(((f.duration-windowsize - 0.001)/stepsize).floor,{|i| var pos = stepsize*i; [pos,pos+windowsize] });
56 |
57 | ~featuremeansperwindow[i] = f.gatherFeaturesBySegments(segmentsarray,false,0); //mean in each segment
58 |
59 | };
60 |
61 |
62 |
63 | }.fork;
64 |
65 | )
66 |
67 | //~featuremeansperwindow.size
68 |
69 | //now have the data to train a classifier
70 |
71 | (
72 | ~featuremeansperwindow2 = ~featuremeansperwindow.collect{|array| array.clump(6)};
73 |
74 | ~data = ~featuremeansperwindow2.collect{|array,i| array.collect{|features| [features,[~class[i]]]}}.flatten;
75 |
76 | ~data = ~data.scramble; //randomise order
77 |
78 | //basic 50/50 split
79 | ~trainingdata = ~data.copyRange(0,~data.size.div(2)-1);
80 | ~testdata = ~data.copyRange(~data.size.div(2),~data.size);
81 |
82 | //6 features, 2 classes
83 | //n = NaiveBayes(6,2);
84 |
85 | n = NeuralNet(6,6,1);
86 |
87 | n.trainExt(~trainingdata,0.02,1000);
88 | )
89 |
90 |
91 | //how well does the classifier perform?
92 | (
93 | ~trainingscore = 0;
94 |
95 | ~trainingdata.do{|val| var output = n.calculate(val[0]).round(1).asInteger;
96 |
97 | if(val[1]==output){~trainingscore = ~trainingscore + 1; };};
98 |
99 | [\trainingscore,~trainingscore,~trainingdata.size,~trainingscore/~trainingdata.size].postln;
100 |
101 | ~testscore = 0;
102 |
103 | ~testdata.do{|val| var output = n.calculate(val[0]).round(1).asInteger;
104 |
105 | if(val[1]==output){~testscore = ~testscore + 1; };};
106 |
107 | [\testscore,~testscore,~testdata.size,~testscore/~testdata.size].postln;
108 |
109 | ""
110 | )
111 |
112 |
113 | //now deploy live
114 |
115 |
116 | (
117 | s.waitForBoot({
118 | l = SCMIRLive(SCMIRAudioFile(~filenames[0],~features),~featurenorms);
119 |
120 | l.createSynthDef.add;
121 |
122 | s.sync;
123 |
124 | ~halfstoresize = SCMIR.framerate.floor;
125 | ~storesize = ~halfstoresize * 2;
126 | ~store = {0!6}!~storesize;
127 | ~storecounter = 0;
128 |
129 | l.createResponder({arg input;
130 | ~storecounter= (~storecounter+1)%~storesize;
131 | ~store[~storecounter] = input.copyRange(3,8);
132 |
133 | if(~storecounter%~halfstoresize==0) {
134 |
135 | //recalculate mean, use this for decision
136 | ~meannow = ~store.mean;
137 |
138 | ["speech","piano"].at(n.calculate(~meannow).round(1).asInteger).postln;
139 |
140 | };
141 |
142 | }); //create a language side responder for this feature extractor
143 |
144 |
145 | //run Synth with the SynthDef already added, input bus is 8, which is first audio input on machine
146 | a = l.run(8)
147 |
148 | });
149 | )
150 |
151 |
152 | //use SCMIR.saveArchive or save on the neural net or SCMIRLive object to save data for future recall
153 |
154 | //store for future use (assumes saved SynthDef as well, or at least will createSynthDef on reload before live use)
155 | l.save("/tmp/scmirlive1.scmirZ");
156 |
157 | l.load("/tmp/scmirlive1.scmirZ");
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
--------------------------------------------------------------------------------
/SCMIRExtensions/examples/genrerecognition.scd:
--------------------------------------------------------------------------------
1 | //Creating ARFF file mixing instances from multiple audio file feature extractions
2 |
3 | //make sure you are using the post 3.4 MFCC UGen fix
4 |
5 | (
6 | var featureinfo = [[MFCC,13]];
7 | var arff;
8 | var sources;
9 |
10 | //array of arrays, one array for each genre class/category
11 | sources = [
12 | ["/data/audio/mirdata/synthpop/Speak & Spell/01 New Life.wav", "/data/audio/mirdata/synthpop/Speak & Spell/02 I Sometimes Wish I Was Dead (2006 Digital Remaster).wav", "/data/audio/mirdata/synthpop/Speak & Spell/03 Puppets (2006 Digital Remaster).wav", "/data/audio/mirdata/synthpop/Speak & Spell/04 Boys Say Go! (2006 Digital Remaster).wav", "/data/audio/mirdata/synthpop/Speak & Spell/05 Nodisco (2006 Digital Remaster).wav", "/data/audio/mirdata/synthpop/Speak & Spell/06 What's Your Name_.wav", "/data/audio/mirdata/synthpop/Speak & Spell/07 Photographic (2006 Digital Remaster).wav", "/data/audio/mirdata/synthpop/Speak & Spell/08 Tora! Tora! Tora! (2006 Digital Remaster).wav", "/data/audio/mirdata/synthpop/Speak & Spell/09 Big Muff (2006 Digital Remaster).wav", "/data/audio/mirdata/synthpop/Speak & Spell/10 Any Second Now (Voices) (2006 Digital Remaster).wav"],
13 | //10 David Bowie tracks
14 | ["/data/audio/mirdata/synthpop/Lodger/01 Fantastic Voyage.wav", "/data/audio/mirdata/synthpop/Lodger/02 African Night Flight.wav", "/data/audio/mirdata/synthpop/Lodger/03 Move On.wav", "/data/audio/mirdata/synthpop/Lodger/04 Yassassin (Long Live).wav", "/data/audio/mirdata/synthpop/Lodger/05 Red Sails.wav", "/data/audio/mirdata/synthpop/Lodger/06 D.J..wav", "/data/audio/mirdata/synthpop/Lodger/07 Look Back In Anger.wav", "/data/audio/mirdata/synthpop/Lodger/08 Boys Keep Swinging.wav", "/data/audio/mirdata/synthpop/Lodger/09 Repetition.wav", "/data/audio/mirdata/synthpop/Lodger/10 Red Money.wav"]
15 | ];
16 |
17 | //two types
18 | arff = SCMIR.createARFF("/tmp/genre.arff",13,(0..(sources.size-1)));
19 |
20 | {
21 | sources.collect{|array,i|
22 |
23 | array.collect{|filename|
24 | var file;
25 |
26 | file = SCMIRAudioFile(filename,featureinfo);
27 |
28 | file.extractFeatures();
29 |
30 | //take average features over whole files
31 | file.gatherFeaturesBySegments([0.0]);
32 |
33 | file.exportARFFInstances(arff,i);
34 |
35 | 0.1.wait;
36 | };
37 |
38 | };
39 |
40 | arff.close;
41 | }.fork;
42 |
43 | )
44 |
45 |
46 |
47 | //now train using weka
48 | (
49 | //change for your machine
50 | ~wekapath = "/Applications/weka-3-6-1/";
51 |
52 | ~jarpath = ~wekapath ++ "weka.jar";
53 |
54 | //SuperCollider application folder location
55 | ~templocation = "/tmp/"; // /Applications/SuperCollider/SuperCollider3.4/";
56 |
57 | ~callstart = "java -classpath"+ ~jarpath;
58 |
59 | ~arffname = ~templocation++"genre.arff";
60 |
61 | (~callstart+"weka.classifiers.bayes.NaiveBayes -t"+~arffname+"-d bayes.wekamodel").unixCmd
62 |
63 | )
64 | //80% correct, 20% misclassified when I test
65 |
66 |
67 |
68 | //test a novel entry
69 | (
70 | var featureinfo = [[MFCC,13]];
71 | var arff;
72 | var sources;
73 |
74 | //array of arrays, one array for each genre class/category
75 | sources = [["/data/audio/mirdata/synthpop/Yellow Magic Orchestra/01 Computer Game _Theme From The Circus_.wav"]];
76 |
77 | //two types as before
78 | arff = SCMIR.createARFF(~arffname,13,[0,1]);
79 |
80 | {
81 | sources.collect{|array,i|
82 |
83 | array.collect{|filename|
84 | var file;
85 |
86 | file = SCMIRAudioFile(filename,featureinfo);
87 |
88 | file.extractFeatures();
89 |
90 | //take average features over whole files
91 | file.gatherFeaturesBySegments([0.0]);
92 |
93 | file.exportARFFInstances(arff,1); //FORCE IT TO BE CLASS 1 RATHER THAN 0
94 |
95 | 0.1.wait;
96 | };
97 |
98 | };
99 |
100 | arff.close;
101 | }.fork;
102 | )
103 |
104 | //all wrong; was closer to DM than Bowie
105 | (~callstart+"weka.classifiers.bayes.NaiveBayes -p 0 -T"+~arffname+"-l bayes.wekamodel").unixCmd
106 |
107 |
108 |
109 |
110 |
--------------------------------------------------------------------------------
/SCMIRExtensions/examples/gmmtest.scd:
--------------------------------------------------------------------------------
1 |
2 | e = SCMIRAudioFile("/data/audio/mirdata/pixiesivebeentired.wav", [[Loudness],[SpecCentroid],[SpecPcile, 0.95],[SpecPcile, 0.8],[SpecFlatness],[FFTSpread],[FFTSlope]]);
3 |
4 | e.extractFeatures()
5 |
6 | e.numfeatures
7 | e.numframes //9766, lots of training for a GMM
8 |
9 | e.featuredata
10 |
11 | //gather data by seconds
12 |
13 | a= (0.0,1.0..(e.duration))
14 |
15 | //averaging for each second
16 | e.gatherFeaturesBySegments(a);
17 |
18 | e.numframes //181
19 |
20 | g = GMM(7); //7 states
21 |
22 | d = e.featuredata.clump(e.numfeatures)
23 |
24 | d.size
25 |
26 | g.train(d)
27 |
28 | g.loadModel;
29 |
30 | d.do{|val| g.test(val).postln; }
31 |
32 |
--------------------------------------------------------------------------------
/SCMIRExtensions/examples/interfilesimilaritycomparison.scd:
--------------------------------------------------------------------------------
1 | //Demonstrates getting an overall file comparison score
2 |
3 |
4 | //frame rate will be about 86Hz
5 | SCMIR.setFrameHop(512);
6 |
7 |
8 |
9 | //trying naively to match up all file pairs via similarity matrix
10 |
11 | (
12 | var featurelist = [[MFCC, 10],[Loudness],[SpecCentroid],[SpecPcile, 0.95],[SpecPcile, 0.8],[SpecFlatness],[FFTCrest],[FFTCrest, 0, 2000], [FFTCrest, 2000, 10000],[FFTSpread],[FFTSlope],[SensoryDissonance],[Onsets,\rcomplex]];
13 |
14 | var files = ["/data/audio/mirdata/pixiesivebeentired.wav","/data/audio/mirdata/Yellow Submarine.wav"];
15 |
16 | var comparisonmatrix;
17 | var audioanalysis;
18 |
19 | {
20 | audioanalysis = files.collect{|filename,i|
21 |
22 | e = SCMIRAudioFile(filename,featurelist);
23 | e.extractFeatures();
24 | e
25 | };
26 |
27 | //check all pairings
28 |
29 | comparisonmatrix = Array.fill(files.size-1,{|i|
30 | var comparisons = List[];
31 | var audio1 = audioanalysis[i];
32 |
33 | if((i+1) < (files.size) ) {
34 | for(i+1,files.size-1,{|j,count|
35 |
36 | var audio2 = audioanalysis[j];
37 |
38 | m = audio1.similarityMatrix(40,0,other:audio2);
39 |
40 | comparisons.add(m);
41 | });
42 | };
43 |
44 | comparisons
45 | });
46 |
47 |
48 | c = comparisonmatrix;
49 |
50 | }.fork;
51 | )
52 |
53 |
54 | c
55 |
56 | d= c[0][0].dtw(40) //leeway of 40 either side, 1 frame is around 0.5 seconds, so allows up to 20 seconds ahead or behind
57 |
58 |
59 | //use DTW, get best path match score:
60 |
61 | d[0]; //match score
62 |
63 | c[0][0].plot(2,2, path:d) //show path on the similarity matrix plot
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 | //this next calculation takes a while; could speed up by reducing search area
72 |
73 | // by testing one file against another, one ten second block at a time, finding closest block in other file; trying to get overall DTW based comparison score
74 | //in general need normalisation by duration of audio1.duration of all distances between audio1 and others
75 | (
76 | var featurelist = [[MFCC, 10],[Loudness],[SpecCentroid],[SpecPcile, 0.95],[SpecPcile, 0.8],[SpecFlatness],[FFTCrest],[FFTCrest, 0, 2000], [FFTCrest, 2000, 10000],[FFTSpread],[FFTSlope],[SensoryDissonance],[Onsets,\rcomplex]];
77 |
78 | var files = ["/data/audio/mirdata/pixiesivebeentired.wav","/data/audio/mirdata/Yellow Submarine.wav","/data/audio/mirdata/synthpopexclude/buggles/10 Technopop.wav"];
79 |
80 | var comparisonmatrix;
81 | var audioanalysis;
82 | var score= 0.0;
83 | var numfit;
84 | var temp;
85 | var segmentsize= 10.0; //Short Term Memory size
86 |
87 | {
88 |
89 | audioanalysis = files.collect{|filename,i|
90 |
91 | e = SCMIRAudioFile(filename,featurelist);
92 |
93 | e.extractFeatures();
94 |
95 | };
96 |
97 |
98 | numfit= audioanalysis.collect{|val| val.duration};
99 |
100 | numfit = (numfit/segmentsize).asInteger;
101 |
102 | numfit.postln; //number of segments within each
103 |
104 | //check all pairings
105 |
106 | comparisonmatrix = Array.fill(files.size-1,{|i|
107 |
108 | var comparisons = List[];
109 | var audio1 = audioanalysis[i];
110 | var numfit1 = numfit[i];
111 |
112 | if((i+1) < (files.size) ) {
113 | for(i+1,files.size-1,{|j,count|
114 |
115 | var audio2 = audioanalysis[j];
116 | var numfit2 = numfit[j];
117 |
118 | ("comparing"+i+"to"+j).postln;
119 |
120 | score= 0.0;
121 |
122 | numfit1.do{|segment1|
123 |
124 | var minval = 999999999999.9;
125 | //var minindex=0;
126 | var excerpt1,excerpt2,matrix;
127 |
128 | [\segment1, segment1].postln;
129 |
130 | excerpt1 = SCMIRAudioFile.newFromRange(audio1,segment1*segmentsize,(segment1+1)*segmentsize);
131 |
132 | numfit2.do{|segment2|
133 |
134 | [\segment2, segment2].postln;
135 |
136 | excerpt2 = SCMIRAudioFile.newFromRange(audio2,segment2*segmentsize,(segment2+1)*segmentsize);
137 |
138 | 0.01.wait;
139 |
140 | matrix = excerpt1.similarityMatrix(2, 2, other:excerpt2);
141 |
142 | 0.01.wait;
143 |
144 | //actually 6 either side in original frame terms
145 | d = matrix.dtw(3);
146 |
147 | //d.postln;
148 |
149 | if(d[0]0.85){~lowpoweronsets.add(e.onsetdata[i])}; };
24 |
25 | ~lowpoweronsets.postln; //times, multiply by 44100 for samples
26 |
27 | }.fork;
28 | )
29 |
--------------------------------------------------------------------------------
/SCMIRExtensions/examples/simpleclustering.scd:
--------------------------------------------------------------------------------
1 | (
2 | ~filenames =
3 | [
4 | "/data/audio/percussion/linndrum/cabasa.wav",
5 | "/data/audio/percussion/linndrum/chh.wav",
6 | "/data/audio/percussion/linndrum/chhl.wav",
7 | "/data/audio/percussion/linndrum/chhs.wav",
8 | "/data/audio/percussion/linndrum/clap.wav",
9 | "/data/audio/percussion/linndrum/conga.wav",
10 | "/data/audio/percussion/linndrum/congah.wav",
11 | "/data/audio/percussion/linndrum/congahh.wav",
12 | "/data/audio/percussion/linndrum/congal.wav",
13 | "/data/audio/percussion/linndrum/congall.wav",
14 | "/data/audio/percussion/linndrum/congalll.wav",
15 | "/data/audio/percussion/linndrum/cowb.wav",
16 | "/data/audio/percussion/linndrum/crash.wav",
17 | "/data/audio/percussion/linndrum/kick.wav",
18 | "/data/audio/percussion/linndrum/kickme.wav",
19 | "/data/audio/percussion/linndrum/ohh.wav",
20 | "/data/audio/percussion/linndrum/reallinn.wav",
21 | "/data/audio/percussion/linndrum/ride.wav",
22 | "/data/audio/percussion/linndrum/sd.wav",
23 | "/data/audio/percussion/linndrum/sdh.wav",
24 | "/data/audio/percussion/linndrum/sdl.wav",
25 | "/data/audio/percussion/linndrum/sst.wav",
26 | "/data/audio/percussion/linndrum/ssth.wav",
27 | "/data/audio/percussion/linndrum/sstl.wav",
28 | "/data/audio/percussion/linndrum/tamb.wav",
29 | "/data/audio/percussion/linndrum/tom.wav",
30 | "/data/audio/percussion/linndrum/tomh.wav",
31 | "/data/audio/percussion/linndrum/tomhh.wav",
32 | "/data/audio/percussion/linndrum/toml.wav",
33 | "/data/audio/percussion/linndrum/tomll.wav"
34 | ];
35 |
36 | //could add \Transient if have WT_Transient UGen from Wavelets
37 | ~features = [Loudness, SensoryDissonance, SpectralEntropy, SpecCentroid, [FFTCrest,10000], [SpecPcile,0.75], [FFTCrest,5000], [SpecPcile,0.25],\ZCR]; //,[MFCC,10] avoid making space too high dimensional; probably only need 2-4 features really
38 |
39 | )
40 |
41 |
42 | //not necessarily needed but helps; get global max and min for all features for normalization
43 | (
44 | {
45 | SCMIR.findGlobalFeatureNorms(~filenames,~features);
46 |
47 | SCMIR.saveGlobalFeatureNorms("/Users/nickcollins/Desktop/clusterdemonormalization.scmirZ");
48 | }.fork;
49 | )
50 |
51 | SCMIR.globalfeaturenorms.postcs
52 | //[ [ 1.2942938804626, 0, 0.50518840551376, 231.87857055664, 4.5598764419556, 21.512195587158, 6.4267835617065, 21.512195587158, 42.15869140625 ], [ 63.035041809082, 1, 195.57659912109, 9162.2685546875, 188.20750427246, 12541.610351562, 193.12126159668, 6260.048828125, 10718.27734375 ] ]
53 |
54 |
55 | (
56 | SCMIR.loadGlobalFeatureNorms("/Users/nickcollins/Desktop/clusterdemonormalization.scmirZ");
57 |
58 | {
59 | ~featuredata = ~filenames.collect{|filename|
60 |
61 | a = SCMIRAudioFile(filename,~features);
62 |
63 | a.extractFeatures();
64 |
65 | //will average feature values over whole sound file, dropping each sound file's representation to a single mean feature vector
66 | a.gatherFeaturesBySegments([0.0],false);
67 | }
68 |
69 | }.fork;
70 |
71 | )
72 |
73 | ~featuredata.size //30 files in this case so 30 output points
74 | ~featuredata[0].size //9 features per file
75 | //note that only 30 points in 9D space pretty sparse! Should really use less features but working with demo
76 |
77 | //requires KMeans quark, get via Quarks.gui if necessary
78 | k = KMeans(3) //assume 3 clusters
79 |
80 | ~featuredata.do{|featurevector| k.add(featurevector)};
81 |
82 | k.update
83 |
84 | k.centroids
85 |
86 | //what cluster is each file sent to?
87 | k.assignments
88 |
89 | //more explicit
90 | ~featuredata.do{|featurevector,i| [~filenames[i],k.classify(featurevector)].postln};
91 |
92 | //can now use trained kMeans classifier on new sound files too, including in live situations via SCMIRLive etc
93 |
94 | //my result, not meant to be definitive!:
95 | [ /data/audio/percussion/linndrum/cabasa.wav, 2 ]
96 | [ /data/audio/percussion/linndrum/chh.wav, 2 ]
97 | [ /data/audio/percussion/linndrum/chhl.wav, 2 ]
98 | [ /data/audio/percussion/linndrum/chhs.wav, 0 ]
99 | [ /data/audio/percussion/linndrum/clap.wav, 2 ]
100 | [ /data/audio/percussion/linndrum/conga.wav, 1 ]
101 | [ /data/audio/percussion/linndrum/congah.wav, 1 ]
102 | [ /data/audio/percussion/linndrum/congahh.wav, 0 ]
103 | [ /data/audio/percussion/linndrum/congal.wav, 1 ]
104 | [ /data/audio/percussion/linndrum/congall.wav, 1 ]
105 | [ /data/audio/percussion/linndrum/congalll.wav, 1 ]
106 | [ /data/audio/percussion/linndrum/cowb.wav, 0 ]
107 | [ /data/audio/percussion/linndrum/crash.wav, 2 ]
108 | [ /data/audio/percussion/linndrum/kick.wav, 1 ]
109 | [ /data/audio/percussion/linndrum/kickme.wav, 1 ]
110 | [ /data/audio/percussion/linndrum/ohh.wav, 2 ]
111 | [ /data/audio/percussion/linndrum/reallinn.wav, 0 ]
112 | [ /data/audio/percussion/linndrum/ride.wav, 0 ]
113 | [ /data/audio/percussion/linndrum/sd.wav, 2 ]
114 | [ /data/audio/percussion/linndrum/sdh.wav, 0 ]
115 | [ /data/audio/percussion/linndrum/sdl.wav, 2 ]
116 | [ /data/audio/percussion/linndrum/sst.wav, 0 ]
117 | [ /data/audio/percussion/linndrum/ssth.wav, 0 ]
118 | [ /data/audio/percussion/linndrum/sstl.wav, 0 ]
119 | [ /data/audio/percussion/linndrum/tamb.wav, 2 ]
120 | [ /data/audio/percussion/linndrum/tom.wav, 1 ]
121 | [ /data/audio/percussion/linndrum/tomh.wav, 1 ]
122 | [ /data/audio/percussion/linndrum/tomhh.wav, 1 ]
123 | [ /data/audio/percussion/linndrum/toml.wav, 1 ]
124 | [ /data/audio/percussion/linndrum/tomll.wav, 1 ]
125 |
126 |
127 |
--------------------------------------------------------------------------------
/SCMIRExtensions/examples/withweka.scd:
--------------------------------------------------------------------------------
1 | //for weka command line help, see
2 | //http://weka.wikispaces.com/How+to+run+WEKA+schemes+from+commandline
3 | //http://weka.wikispaces.com/Primer
4 | //http://weka.wikispaces.com/Using+cluster+algorithms
5 |
6 |
7 | (
8 | //change for your machine
9 | ~wekapath = "/Applications/weka-3-6-1/";
10 |
11 | ~jarpath = ~wekapath ++ "weka.jar";
12 |
13 | //SuperCollider application folder location
14 | ~templocation = "/tmp/"; // "/data/gitprojects/supercollider/common/build/";
15 |
16 | ~callstart = "java -classpath"+ ~jarpath;
17 |
18 | ~arffname = ~templocation++"testfile.arff";
19 |
20 | )
21 |
22 |
23 | //test can call weka; ask for help. you may need to view messages in the Console app on OS X
24 | (~callstart+"weka.classifiers.trees.J48 -h").unixCmd
25 |
26 |
27 | //analyze file and create ARFF output
28 |
29 |
30 | f = SCMIRAudioFile("sounds/a11wlk01.wav", [[MFCC, 13], [Chromagram, 12]]);
31 |
32 | {f.extractFeatures(); }.fork(SystemClock);
33 |
34 | f.exportARFF(~arffname);
35 |
36 |
37 | //validate ARFF
38 | (~callstart+"weka.core.Instances"+~arffname).unixCmd
39 |
40 |
41 |
42 | //train with basic classifiers; will be perfect here, since no class to predict
43 | (~callstart+"weka.classifiers.rules.ZeroR -t"+~arffname).unixCmd
44 |
45 | //can't handle numeric class, nothing to predict
46 | (~callstart+"weka.classifiers.trees.J48 -t"+~arffname).unixCmd
47 |
48 | //test on existing data set from weka package
49 | (~callstart+"weka.classifiers.trees.J48 -t"+(~wekapath++"data/soybean.arff")).unixCmd
50 |
51 | //-d save model, -l load model
52 | (~callstart+"weka.classifiers.bayes.NaiveBayes -t"+(~wekapath++"data/soybean.arff")+"-d bayes.wekamodel").unixCmd
53 |
54 | //test using saved model
55 | (~callstart+"weka.classifiers.bayes.NaiveBayes -T"+(~wekapath++"data/soybean.arff")+"-l bayes.wekamodel").unixCmd
56 |
57 |
58 |
59 |
60 |
61 | //run clustering on instances
62 |
63 | (~callstart+"weka.clusterers.SimpleKMeans -h").unixCmd
64 |
65 | //find 5 clusters -N option is for number of desired clusters
66 | (~callstart+"weka.clusterers.SimpleKMeans -N 5 -t"+~arffname).unixCmd
67 |
68 |
69 | //find 10 clusters and save
70 | (~callstart+"weka.clusterers.SimpleKMeans -N 10 -t"+~arffname+"-d kmeans.wekamodel").unixCmd
71 |
72 |
73 | //use cluster model on novel input
74 |
75 | //MUST have same features extracted in same order
76 | g = SCMIRAudioFile("sounds/yourmum.wav", [[MFCC, 13], [Chromagram, 12]]);
77 |
78 | {g.extractFeatures(); }.fork(SystemClock);
79 |
80 | ~arffname2 = ~templocation++"testfile2.arff";
81 |
82 | g.exportARFF(~arffname2);
83 |
84 | (~callstart+"weka.clusterers.SimpleKMeans -T"+~arffname2+"-l kmeans.wekamodel").unixCmd
85 |
86 |
87 |
88 | //next challenge; how to get a specific instance clustered and get data back into SC; via piped output from weka, or at least scan of text file out?
89 | (~callstart+"weka.clusterers.SimpleKMeans -T"+~arffname2+"-p 0 -l kmeans.wekamodel").unixCmd
90 |
91 | //using Pipe to get data a line at a time
92 | (
93 | var p, l;
94 | var count = 0;
95 | p = Pipe.new((~callstart+"weka.clusterers.SimpleKMeans -T"+~arffname2+"-p 0 -l kmeans.wekamodel"), "r"); // list directory contents in long format
96 | l = p.getLine; // get the first line
97 | while({l.notNil}, {
98 | //[count,l].postln;
99 | if (l.size>0,{(l.split($ )[1]).postln;}); //post cluster assignment decision
100 | count = count +1;
101 | l = p.getLine;
102 | }); // post until l = nil
103 | p.close; // close the pipe to avoid that nasty buildup
104 | )
105 |
106 | //if getting a single cluster result, just put one feature instance in the output file.
107 |
108 | //another way may be to write an OSC based java program making weka calls, or link in to Rebecca Fiebrink's wekinator (though SC's feature extraction required)
109 |
110 |
111 |
112 |
113 | //classify onset-cued segments
114 | g = SCMIRAudioFile("/data/audio/mirdata/pixiesivebeentired.wav", [[MFCC, 20]]);
115 |
116 | (
117 | {
118 | g.extractFeatures();
119 | g.extractOnsets();
120 | }.fork(SystemClock);
121 | )
122 |
123 | (
124 | g.gatherFeaturesByOnsets;
125 |
126 | ~arffname3 = ~templocation++"testfile3.arff";
127 |
128 | //output onset collected features as instances in an ARFF file
129 | g.exportARFF(~arffname3);
130 | )
131 |
132 | //train clusterer, 3 clusters
133 | (~callstart+"weka.clusterers.SimpleKMeans -N 3 -t"+~arffname3+"-d kmeansonsets.wekamodel").unixCmd
134 |
135 | //could now compare on another file, or just use these clusters in selective resynthesis, etc
136 |
137 | (
138 | var p, l;
139 | var count = 0;
140 | var onsetclassification = List[];
141 |
142 | p = Pipe.new((~callstart+"weka.clusterers.SimpleKMeans -T"+~arffname3+"-p 0 -l kmeansonsets.wekamodel"), "r"); // list directory contents in long format
143 | l = p.getLine; // get the first line
144 | while({l.notNil}, {
145 | //[count,l].postln;
146 | if (l.size>0,{onsetclassification.add( (l.split($ )[1]).asInteger);}); //post cluster assignment decision
147 | count = count +1;
148 | l = p.getLine;
149 | }); // post until l = nil
150 | p.close; // close the pipe to avoid that nasty buildup
151 | ~solution = onsetclassification.postcs
152 | )
153 |
154 |
155 | s.boot;
156 |
157 | (
158 | b = Buffer.read(s,"/data/audio/mirdata/pixiesivebeentired.wav"); //load whole thing for ease of use
159 |
160 | SynthDef(\scmirplaybacksegment,{arg bufnum, pos= 0.0, duration=1.0;
161 |
162 | Out.ar(0, PlayBuf.ar(2,bufnum,BufRateScale.ir(bufnum),startPos:pos*BufSampleRate.ir(bufnum))*EnvGen.ar(Env([0,1,1,0],[0.01,duration-0.02,0.01]),doneAction:2));
163 |
164 | }).add
165 | )
166 |
167 | //play back only those events tagged in cluster 0; misses out chorus parts, mainly verse!
168 | (
169 | {
170 | ~solution.do{|cluster, i|
171 | var onsettime, endtime, duration;
172 |
173 | if (cluster==0){
174 |
175 | onsettime = g.onsetdata[i];
176 |
177 | endtime = if(i<(g.numframes-1)){g.onsetdata[i+1]-0.001}{g.duration};
178 |
179 | duration = (endtime-onsettime).max(0.0);
180 |
181 | Synth(\scmirplaybacksegment,[\pos,onsettime,\duration,duration]);
182 |
183 | duration.wait;
184 |
185 | //0.1.wait; //short silence between each segment
186 |
187 | }; }
188 |
189 | }.fork;
190 | )
191 |
192 |
193 |
194 |
195 |
--------------------------------------------------------------------------------
/Source/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | #cmake -DCMAKE_OSX_ARCHITECTURES='i386;x86_64' ..
2 |
3 | cmake_minimum_required (VERSION 2.6)
4 | project (SCMIRexecutables)
5 |
6 | add_executable(noveltycurve noveltycurve/noveltycurve.cpp)
7 |
8 | add_executable(similaritymatrix similaritymatrix/similarity.cpp)
9 |
10 | #set to locations of your gsl headers and library folders
11 | set(LIBGSL /Users/nickcollins/Desktop/tosort/gsl_universal_1.14/libs)
12 | include_directories(/Users/nickcollins/Desktop/tosort/gsl_universal_1.14/gsl)
13 |
14 | file(GLOB gmmSources gmm/*cpp)
15 | add_executable(gmm ${gmmSources})
16 |
17 | file(GLOB hmmSources hmm/*cpp)
18 | add_executable(hmm ${hmmSources})
19 |
20 | target_link_libraries(gmm ${LIBGSL}/libgsl.a ${LIBGSL}/libgslcblas.a)
21 | #target_link_libraries(gmm ${LIBGSL}/libgsl.a ${LIBGSL}/libgslcblas.a ${LIBGSL}/libgsleigen.a ${LIBGSL}/libgslmatrix.a)
22 |
23 | file(GLOB nnSources NeuralNet/*cpp)
24 | add_executable(NeuralNet ${nnSources})
25 |
26 | INSTALL(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/noveltycurve ${CMAKE_CURRENT_BINARY_DIR}/similaritymatrix ${CMAKE_CURRENT_BINARY_DIR}/NeuralNet ${CMAKE_CURRENT_BINARY_DIR}/hmm ${CMAKE_CURRENT_BINARY_DIR}/gmm DESTINATION "${CMAKE_CURRENT_SOURCE_DIR}/../SCMIRExtensions/scmirexec/")
--------------------------------------------------------------------------------
/Source/NeuralNet/ffnet.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * ffnet.cpp
3 | * NeuralNet
4 | *
5 | * Created by Chris Kiefer on 05/11/2007.
6 | * Copyright 2007 __MyCompanyName__. All rights reserved.
7 | *
8 | */
9 |
10 | #include "ffnet.h"
11 | #include
12 | #include
13 | #include
14 | #include
15 | #include
16 |
17 | using namespace std;
18 |
19 | //generates a psuedo-random double between 0.0 and max
20 | inline double randdouble(double max)
21 | {
22 | return rand()/(double(RAND_MAX)+1) * max;
23 | }
24 |
25 | inline double sigmoid (double val) {
26 | return 1 / (1.0 + exp(-1 * val));
27 | }
28 |
29 | /*
30 | Initialise the network
31 | */
32 | void FeedForwardNetwork::init(int nIn, int nHidden, int nOut, double learningRate, double initWeight)
33 | {
34 | this->nIn = nIn;
35 | this->nHidden = nHidden;
36 | this->nOut = nOut;
37 | this->learningRate = learningRate;
38 | this->initWeight = initWeight;
39 |
40 | //reserve some memory
41 | biasH = new double[nHidden];
42 | outputH = new double[nHidden];
43 | dh = new double[nHidden];
44 | biasO = new double[nOut];
45 | outputO = new double[nOut];
46 | dk = new double[nOut];
47 | weightsH = new double* [nHidden];
48 | weightsO = new double* [nOut];
49 |
50 | //init weights etc
51 | srand((unsigned)(time(0)));
52 | int i,j;
53 | for(i=0; i= maxEpochs) || (error < errorTarget)) {
174 | break;
175 | }
176 | }
177 | }
178 |
179 | /*
180 | For testing
181 | */
182 | void FeedForwardNetwork::viewNN()
183 | {
184 | cout << "Weights: ";
185 | for(int i=0;i
13 | #include
14 | #include
15 | #include "ffnet.h"
16 |
17 | using namespace std;
18 |
19 | FeedForwardNetwork *net;
20 |
21 | //string to int, see http://www.codeguru.com/forum/showthread.php?t=231054
22 | template
23 | bool from_string(T& t,
24 | const std::string& s,
25 | std::ios_base& (*f)(std::ios_base&))
26 | {
27 | std::istringstream iss(s);
28 | return !(iss >> f >> t).fail();
29 | }
30 |
31 | void init(int getInitWeight) throw(int)
32 | {
33 | string input;
34 | int nIn, nHidden, nOut;
35 | double learningRate, initWeight = 1.0;
36 | //num inputs
37 | cin >> input;
38 | if(!from_string(nIn, input, std::dec)) {throw(-1); }
39 | cin >> input;
40 | if(!from_string(nHidden, input, std::dec)) {throw(-1); }
41 | cin >> input;
42 | if(!from_string(nOut, input, std::dec)) {throw(-1); }
43 | cin >> input;
44 | if(!from_string(learningRate, input, std::dec)) {throw(-1); }
45 | if (getInitWeight == 1) {
46 | cin >> input;
47 | if(!from_string(initWeight, input, std::dec)) {throw(-1); }
48 | };
49 |
50 | net->init(nIn, nHidden, nOut, learningRate, initWeight);
51 |
52 | cout << "initialised\n";
53 | }
54 |
55 | void calculate() throw(int)
56 | {
57 | string input;
58 | double *inputs = new double[net->nIn];
59 | for(int i = 0; i < net->nIn; i++) {
60 | cin >> input;
61 | if(!from_string(inputs[i], input, std::dec)) {throw(-1); }
62 | }
63 | cout << "calculating...\n";
64 |
65 | double* result = net->calculate(inputs);
66 |
67 | cout << "Result: [";
68 | for(int i = 0; i < net->nOut; i++) {
69 | cout << (i>0?",":"");
70 | cout << result[i];
71 | }
72 | cout << "]" << endl;
73 | }
74 |
75 | void train1() throw(int)
76 | {
77 | string input;
78 | double *inputs = new double[net->nIn];
79 | for(int i = 0; i < net->nIn; i++) {
80 | cin >> input;
81 | if(!from_string(inputs[i], input, std::dec)) {throw(-1); }
82 | }
83 | double *targets = new double[net->nOut];
84 | for(int i = 0; i < net->nOut; i++) {
85 | cin >> input;
86 | if(!from_string(targets[i], input, std::dec)) {throw(-1); }
87 | }
88 |
89 | cout << "training...\n";
90 | net->train1(inputs,targets);
91 | cout << "trained\n";
92 |
93 | }
94 |
95 | void train() throw(int)
96 | {
97 | string input;
98 | int sets, maxEpochs;
99 | double errorTarget;
100 | cin >> input;
101 | if(!from_string(maxEpochs, input, std::dec)) {throw(-1);}
102 | cin >> input;
103 | if(!from_string(errorTarget, input, std::dec)) {throw(-1);}
104 | cin >> input;
105 | if(!from_string(sets, input, std::dec)) {throw(-1);}
106 |
107 | double **inputs = new double* [sets];
108 | double **targets = new double* [sets];
109 |
110 | for(int set = 0; set < sets; set++) {
111 | double *inputVector = new double[net->nIn];
112 | inputs[set] = inputVector;
113 | for(int i = 0; i < net->nIn; i++) {
114 | cin >> input;
115 | if(!from_string(inputVector[i], input, std::dec)) {throw(-1); }
116 | }
117 |
118 | double *targetVector = new double[net->nOut];
119 | targets[set] = targetVector;
120 | for(int i = 0; i < net->nOut; i++) {
121 | cin >> input;
122 | if(!from_string(targetVector[i], input, std::dec)) {throw(-1); }
123 | }
124 | }
125 |
126 | cout << "training...\n";
127 | net->train(inputs, targets, sets, errorTarget, maxEpochs);
128 | cout << "trained\n";
129 | }
130 |
131 | /*
132 | Specify the weights and biases
133 | */
134 | void set() throw (int)
135 | {
136 | init(0);
137 | string input;
138 | double val;
139 | cout << "Loading network values\n";
140 | for(int i = 0; i < net->nHidden; i++) {
141 | for(int j = 0; j < net->nIn; j++) {
142 | cin >> input;
143 | if(!from_string(val, input, std::dec)) {throw(-1); }
144 | net->weightsH[i][j] = val;
145 | cout << val << endl;
146 | }
147 | }
148 | for(int i = 0; i < net->nHidden; i++) {
149 | cin >> input;
150 | if(!from_string(val, input, std::dec)) {throw(-1); }
151 | net->biasH[i] = val;
152 | cout << val << endl;
153 | }
154 | for(int i = 0; i < net->nOut; i++) {
155 | for(int j = 0; j < net->nHidden; j++) {
156 | cin >> input;
157 | if(!from_string(val, input, std::dec)) {throw(-1); }
158 | net->weightsO[i][j] = val;
159 | cout << val << endl;
160 | }
161 | }
162 | for(int i = 0; i < net->nOut; i++) {
163 | cin >> input;
164 | if(!from_string(val, input, std::dec)) {throw(-1); }
165 | net->biasO[i] = val;
166 | cout << val << endl;
167 | }
168 | cout << "Loaded\n";
169 | net->viewNN();
170 | }
171 |
172 | int main (int argc, char * const argv[]) {
173 | // insert code here...
174 | std::cout << "Neural Network v0.1\n";
175 | net = new FeedForwardNetwork;
176 |
177 | string cmd;
178 | int loop=1;
179 |
180 | //process commands
181 | while(loop) {
182 | cin >> cmd;
183 | if (cmd.compare("quit") == 0) {
184 | loop=0;
185 | }
186 | else
187 | if (cmd.compare("init") == 0) {
188 | init(1);
189 | }
190 | else
191 | if (cmd.compare("calc") == 0) {
192 | calculate();
193 | }
194 | else
195 | if (cmd.compare("train1") == 0) {
196 | train1();
197 | }
198 | else
199 | if (cmd.compare("train") == 0) {
200 | train();
201 | }
202 | else
203 | if (cmd.compare("set") == 0) {
204 | set();
205 | }
206 | else
207 | if (cmd.compare("view") == 0) {
208 | net->viewNN();
209 | }
210 | else
211 | if (cmd.compare("dump") == 0) {
212 | net->dumpNN();
213 | }
214 | else
215 | {
216 | cout << "Sorry i don't understand\n";
217 | }
218 | };
219 |
220 | return 0;
221 | }
222 |
223 |
--------------------------------------------------------------------------------
/Source/gmm/Macros.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (c) 2008 Eric Sauser,
3 | LASA Lab, EPFL, CH-1015 Lausanne, Switzerland,
4 | http://lasa.epfl.ch
5 | */
6 |
7 | #include "Macros.h"
8 |
--------------------------------------------------------------------------------
/Source/gmm/Macros.h:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (c) 2008 Eric Sauser,
3 | LASA Lab, EPFL, CH-1015 Lausanne, Switzerland,
4 | http://lasa.epfl.ch
5 | */
6 |
7 | #ifndef __MATHMACROS_H_
8 | #define __MATHMACROS_H_
9 |
10 | #include
11 |
12 | #ifndef PIf
13 | #define PIf 3.14159265358979323846f
14 | #endif
15 |
16 | #ifndef DEG2RAD
17 | #define DEG2RAD(x) ((x)*(PIf/180.0f))
18 | #endif
19 |
20 | #ifndef RAD2DEG
21 | #define RAD2DEG(x) ((x)*(180.0f/PIf))
22 | #endif
23 |
24 | #ifndef RND
25 | #define RND(x) (float((x)*((double)rand())/((double)(RAND_MAX+1.0))))
26 | #endif
27 |
28 | #ifndef MIN
29 | #define MIN(x,y) (((x)<(y))?(x):(y))
30 | #endif
31 |
32 | #ifndef MAX
33 | #define MAX(x,y) (((x)>(y))?(x):(y))
34 | #endif
35 |
36 | #ifndef TRUNC
37 | #define TRUNC(x,mn,mx) (MIN(MAX((x),(mn)),(mx)))
38 | #endif
39 |
40 | #ifndef SIGN2
41 | #define SIGN(x) (((x)<0.0f)?(-1.0f):(1.0f))
42 | #endif
43 |
44 | #ifndef SIGN2
45 | #define SIGN2(a,b) ((b) >= 0.0f ? fabs(a) : -fabs(a))
46 | #endif
47 |
48 | #ifndef ROUND
49 | #define ROUND(x) (floor((x)+0.5f))
50 | #endif
51 |
52 | #ifndef EPSILON
53 | #define EPSILON (1e-6)
54 | #endif
55 |
56 |
57 | inline float hypot_s(float a, float b){
58 | float r;
59 | if (fabs(a) > fabs(b)) {
60 | r = b / a;
61 | r = fabs(a) * sqrtf(1.0f + r * r);
62 | } else if (b != 0.0f) {
63 | r = a / b;
64 | r = fabs(b) * sqrtf(1.0f + r * r);
65 | } else {
66 | r = 0.0f;
67 | }
68 | return r;
69 | }
70 |
71 | #endif
72 |
--------------------------------------------------------------------------------
/Source/gmm/MathLib.h:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (c) 2008 Eric Sauser,
3 | LASA Lab, EPFL, CH-1015 Lausanne, Switzerland,
4 | http://lasa.epfl.ch
5 | */
6 |
7 | #ifndef __MATHLIB_H__
8 | #define __MATHLIB_H__
9 |
10 | //#define USE_T_EXTENSIONS
11 |
12 | #include
13 | #include "Macros.h"
14 | #include "Vector.h"
15 | #ifdef USE_T_EXTENSIONS
16 | #include "TVector.h"
17 | #include "Vector3.h"
18 | #endif
19 | #include "Matrix.h"
20 | #ifdef USE_T_EXTENSIONS
21 | #include "TMatrix.h"
22 | #include "Matrix3.h"
23 | #include "Matrix4.h"
24 | #include "Referential.h"
25 | #endif
26 |
27 | #endif
28 |
--------------------------------------------------------------------------------
/Source/gmm/Matrix.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (c) 2008 Eric Sauser,
3 | LASA Lab, EPFL, CH-1015 Lausanne, Switzerland,
4 | http://lasa.epfl.ch
5 | */
6 |
7 | #include "Matrix.h"
8 |
9 | int Matrix::bInverseOk = true;
10 |
--------------------------------------------------------------------------------
/Source/gmm/README:
--------------------------------------------------------------------------------
1 | To compile the gmm executable you will need gsl (http://www.gnu.org/software/gsl/) and specifically to link to :
2 | libgsl
3 | libgslcblas
4 | libgsleigen
5 | libgslmatrix
6 |
7 | I used the universal 1.14 version of gsl for Mac.
8 |
9 | I also used the gmm-gmr project code:
10 | http://sourceforge.net/projects/gmm-gmr/
11 |
12 | All under GNU GPL
13 |
14 |
--------------------------------------------------------------------------------
/Source/gmm/Vector.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (c) 2008 Eric Sauser,
3 | LASA Lab, EPFL, CH-1015 Lausanne, Switzerland,
4 | http://lasa.epfl.ch
5 | */
6 |
7 | #include "Vector.h"
8 |
9 | float Vector::undef = 0.0f;
10 |
11 |
12 |
--------------------------------------------------------------------------------
/Source/gmm/gmr.h:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (c) 2008 Florent D'halluin , Sylvain Calinon,
3 | LASA Lab, EPFL, CH-1015 Lausanne, Switzerland,
4 | http://www.calinon.ch, http://lasa.epfl.ch
5 |
6 | The program is free for non-commercial academic use.
7 | Please acknowledge the authors in any academic publications that have
8 | made use of this code or part of it. Please use this BibTex reference:
9 |
10 | @article{Calinon07SMC,
11 | title="On Learning, Representing and Generalizing a Task in a Humanoid
12 | Robot",
13 | author="S. Calinon and F. Guenter and A. Billard",
14 | journal="IEEE Transactions on Systems, Man and Cybernetics, Part B.
15 | Special issue on robot learning by observation, demonstration and
16 | imitation",
17 | year="2007",
18 | volume="37",
19 | number="2",
20 | pages="286--298"
21 | }
22 | */
23 |
24 | #include "Matrix.h"
25 |
26 | class GaussianMixture {
27 | /* GMM class, see main.cpp to see some sample code */
28 | public : //was private, but otherwise no way to get back model params when loading from a file!
29 | int nState,dim;
30 | Matrix mu;
31 | Matrix *sigma;
32 | float *priors;
33 |
34 | public :
35 | GaussianMixture(){};
36 |
37 | // Load the dataset from a file
38 | Matrix loadDataFile(const char filename []);
39 |
40 | // Save the dataset to a file
41 | bool saveDataFile(const char filename [], Matrix data);
42 |
43 | // Save the result of a regression
44 | bool saveRegressionResult(const char fileMu[], const char fileSigma[], Matrix inData, Matrix outData, Matrix outSigma[]);
45 |
46 | bool loadParams(const char filename[]);
47 | /* Load the means, priors probabilies and covariances matrices
48 | stored in a file .. (see saveParams )*/
49 |
50 | void saveParams(const char filename []);
51 | /* save current parameters in a file */
52 |
53 | void debug(void);
54 | /* display on screen current parameters */
55 |
56 |
57 | Matrix doRegression( Matrix in,
58 | Matrix * SigmaOut,
59 | Vector inComponents,
60 | Vector outComponents);
61 | /* do a regression with current parameters :
62 | - output a matrix of size nb row of in * nb components in outComponents
63 | - the SigmaOut pointer will point to an array of nb row of in or out
64 | covariances matrices
65 | - inComponents and outComponents are the index of the dimensions
66 | represented in the in and out matrices */
67 |
68 | float pdfState(Vector v,Vector Components,int state);
69 | /* Compute probabilty of vector v ( corresponding to dimension given
70 | in the Components vector) for the given state. */
71 |
72 | float pdfState(Vector v,int state);
73 | // same as above but v is of same dimension as the GMM
74 |
75 | /* Spline fitting to rescale trajectories. */
76 | Matrix HermitteSplineFit(Matrix& inData, int nbSteps, Matrix& outData);
77 |
78 | void initEM_TimeSplit(int nState,Matrix Dataset);
79 | /* init the GaussianMixture by splitting the dataset into
80 | time (first dimension) slices and computing variances
81 | and means for each slices.
82 | once initialisation has been performed, the nb of state is set */
83 |
84 | int doEM(Matrix DataSet);
85 | /* performs Expectation Maximization on the Dataset,
86 | in order to obtain a nState GMM
87 | Dataset is a Matrix(nSamples,nDimensions) */
88 |
89 | };
90 |
91 |
--------------------------------------------------------------------------------
/Source/gmm/main.cpp:
--------------------------------------------------------------------------------
1 | //integrates gmm-gmr
2 | //Copyright (c) 2008 Florent D'halluin , Sylvain Calinon,
3 | //LASA Lab, EPFL, CH-1015 Lausanne, Switzerland,
4 | //http://www.calinon.ch, http://lasa.epfl.ch
5 | //The program is free for non-commercial academic use.
6 |
7 | //This code is part of an extension set for SuperCollider 3 (http://supercollider.sourceforge.net/). We follow the same license terms for SuperCollider 3, releasing under GNU GPL 3
8 | //all non-gmm-gmr code here by Nick Collins http://www.cogs.susx.ac.uk/users/nc81/index.html
9 |
10 |
11 | //#include
12 |
13 | #include "MathLib.h"
14 | #include "gmr.h"
15 |
16 |
17 |
18 | //arguments
19 | //mode 0 train:
20 | //0, numstates, input filename, out model filename
21 | //mode 1 test:
22 | //1, model filename, input filename, output filename
23 |
24 |
25 | int main (int argc, char * const argv[]) {
26 | // insert code here...
27 |
28 | GaussianMixture g;
29 |
30 | Matrix dataset;
31 | //unsigned int nbData=0;
32 | char filename[256];
33 |
34 | int calltype = atoi(argv[1]);
35 |
36 | int numstates;
37 |
38 | if (calltype ==0) {
39 |
40 | numstates = atoi(argv[2]);
41 |
42 | //for (unsigned int i = 0; i < NBSAMPLES; i++){
43 | sprintf(filename,argv[3]); //"/Applications/SuperCollider/SuperCollider3.4/gmminput.temp");
44 | dataset = g.loadDataFile(filename);
45 |
46 | std::cout << "Learning the GMM model" << std::endl;
47 | g.initEM_TimeSplit(numstates,dataset); // initialize the model; fitting NBSTATES Gaussians to the data, which is one instance per row (counter for time in first colummn)
48 | g.doEM(dataset); // performs EM
49 | std::cout << "saving the result to " << filename << std::endl;
50 |
51 | //NEED TO PASS FILENAME
52 | sprintf(filename,argv[4]); //"/Applications/SuperCollider/SuperCollider3.4/gmminput.temp");
53 |
54 | g.saveParams(filename);
55 | std::cout << "ok" << std::endl;
56 | }
57 |
58 | else {
59 |
60 | int i;
61 |
62 | sprintf(filename,argv[2]);
63 |
64 | g.loadParams(filename);
65 |
66 | numstates = g.nState;
67 |
68 | int numfeatures = g.dim;
69 |
70 | //std::cout << "loaded model " << numstates <<" " << numfeatures << std::endl;
71 |
72 |
73 | //sprintf(filename,argv[3]);
74 |
75 | FILE * fpinput;
76 | FILE * fpoutput;
77 |
78 | fpinput = fopen(argv[3], "rb");
79 |
80 | float * inputdata = new float[numfeatures];
81 |
82 | fread(inputdata, sizeof(float), numfeatures, fpinput);
83 |
84 | // for (i=0; imaxprob) {
108 | maxprob = prob;
109 | minindex = i;
110 | }
111 |
112 | //std::cout << i << " " << prob << std::endl;
113 | }
114 |
115 | fpoutput = fopen(argv[4], "wb");
116 |
117 | //std::cout << "\n " << argv[3] << " ";
118 | fwrite(&minindex, sizeof(int), 1, fpoutput);
119 |
120 | fwrite(probs, sizeof(float), numstates, fpoutput);
121 |
122 | fclose(fpoutput);
123 |
124 | delete [] probs;
125 |
126 | //float test[10] = { 2.2368574142456, 2.0947415828705, 2.4590134620667, 1.9261083602905, 2.7772326469421, 1.9098069667816, 2.1310169696808, 1.6191737651825, 1.7091503143311, 2.1181514263153 };
127 |
128 |
129 |
130 | }
131 |
132 |
133 | //calls to compare probability of different states
134 |
135 |
136 |
137 |
138 |
139 |
140 | return 0;
141 | }
142 |
--------------------------------------------------------------------------------
/Source/hmm/dhmm.cpp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sicklincoln/SCMIR/59ae32b9c2b331b1a561865def98b491a74bccb9/Source/hmm/dhmm.cpp
--------------------------------------------------------------------------------
/Source/hmm/dhmm.h:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sicklincoln/SCMIR/59ae32b9c2b331b1a561865def98b491a74bccb9/Source/hmm/dhmm.h
--------------------------------------------------------------------------------
/Source/hmm/main.cpp:
--------------------------------------------------------------------------------
1 | //This code is part of an extension set for SuperCollider 3 (http://supercollider.sourceforge.net/). We follow the same license terms for SuperCollider 3, releasing under GNU GPL 3
2 | //all non-GHMM code here by Nick Collins http://www.cogs.susx.ac.uk/users/nc81/index.html
3 | //GHMM code from http://home.gna.org/dhmm/ and by Daniel Roggen, modified BSD, license at top of the dhmm.h and .cpp files
4 |
5 |
6 | #include
7 | #include
8 | #include
9 | #include "dhmm.h"
10 |
11 |
12 |
13 | //arguments
14 | //mode 0 create and train
15 | //0, int numstates, int numsymbols, input filename (training sequences), out model filename
16 |
17 | //mode 1 generate state sequence:
18 | //1, model filename, output filename
19 |
20 | //mode 2 most likely state sequence given observation sequence:
21 | //2, model filename, input filename, output filename
22 |
23 | //mode 3 observation sequence probability
24 | //3, model filename, input filename, output filename
25 |
26 |
27 | //can also calculate probability of a given state sequence; may need to pass a set of them, to find most likely
28 |
29 |
30 | int main (int argc, char * const argv[]) {
31 |
32 | int i,j;
33 |
34 | DHMM *hmm;
35 |
36 | //unsigned int nbData=0;
37 | //char filename[256];
38 |
39 | int calltype = atoi(argv[1]);
40 |
41 | int numstates; //number hidden states
42 | int numsymbols; //number of observable symbols (assumes discrete observation data, eg vector quantised feature vectors into single nonnegative integers)
43 |
44 | if (calltype ==0) {
45 |
46 | numstates = atoi(argv[2]);
47 | numsymbols = atoi(argv[3]);
48 |
49 | //std::cout << "numstates" << numstates << " " << "numsymbols" << numsymbols << std::endl;
50 |
51 |
52 | hmm = new DHMM(numstates, numsymbols);
53 |
54 |
55 | //hmm->Print();
56 |
57 |
58 | //sprintf(filename,argv[3]); //"/Applications/SuperCollider/SuperCollider3.4/gmminput.temp");
59 |
60 | std::cout << "Learning the HMM model" << std::endl;
61 |
62 | //dataset = g.loadDataFile(filename);
63 |
64 | //LOAD TRAINING DATA
65 |
66 | //sprintf(filename,argv[4]); //"/Applications/SuperCollider/SuperCollider3.4/gmminput.temp");
67 |
68 | FILE * fpinput;
69 |
70 | fpinput = fopen(argv[4], "rb");
71 |
72 | //float * inputdata = new int[numfeatures];
73 |
74 | int numsequences;
75 |
76 | fread(&numsequences, sizeof(int), 1, fpinput);
77 |
78 | vector obsinput;
79 |
80 | for (i=0; i vec;
92 |
93 | vec.assign(pint, pint + seqlength);
94 |
95 | obsinput.push_back(vec);
96 |
97 | delete [] pint;
98 |
99 | };
100 |
101 | fclose(fpinput);
102 |
103 |
104 | // cout << obsinput << nl;
105 |
106 | // vector::iterator itr2;
107 | //
108 | // for ( itr2 = obsinput.begin(); itr2 < obsinput.end(); ++itr2 ) {
109 | //
110 | // Observation::iterator itr;
111 | //
112 | // for ( itr = (*itr2).begin(); itr < (*itr2).end(); ++itr ) {
113 | //
114 | // int val = *itr;
115 | //
116 | // cout << val << " ";
117 | //
118 | // }
119 | //
120 | // cout << std::endl;
121 | //
122 | // };
123 |
124 |
125 | int numstates = hmm->GetNumStates();
126 |
127 | for(int k=0; kGetState(k).Initialise();
129 |
130 | int numiterations = atoi(argv[6]);
131 |
132 | hmm->BaumWelch(obsinput,numiterations);
133 |
134 | //hmm->test();
135 |
136 | //hmm->Print();
137 |
138 | //need to have vector
139 | //sprintf(filename,argv[5]); //"/Applications/SuperCollider/SuperCollider3.4/gmminput.temp");
140 |
141 | std::cout << "saving the result to " << argv[5] << std::endl;
142 |
143 | hmm->Save(argv[5]);
144 | std::cout << "ok" << std::endl;
145 | }
146 |
147 | else if (calltype ==1) {
148 |
149 | //sprintf(filename,argv[2]);
150 |
151 | hmm = new DHMM(1,1);
152 |
153 | hmm->Load(argv[2]);
154 |
155 | //hmm->Print();
156 |
157 | int steps = atoi(argv[3]);
158 |
159 | //typedef vector Observation;
160 |
161 | Observation obs = hmm->GenerateSequence(steps);
162 |
163 | FILE * fpoutput = fopen(argv[4], "wb");
164 |
165 | int size = obs.size();
166 |
167 | fwrite(&size, sizeof(int), 1, fpoutput);
168 |
169 | Observation::iterator itr;
170 |
171 | for ( itr = obs.begin(); itr < obs.end(); ++itr ) {
172 |
173 | int val = *itr;
174 |
175 | fwrite(&val, sizeof(int), 1, fpoutput);
176 |
177 | }
178 |
179 | //std::cout << "\n " << argv[3] << " ";
180 | //fwrite(&minindex, sizeof(int), 1, fpoutput);
181 |
182 | //fwrite(probs, sizeof(float), numstates, fpoutput);
183 |
184 | fclose(fpoutput);
185 |
186 | }
187 |
188 | //Viterbi, find best matching hidden state sequence
189 | else if (calltype ==2) {
190 |
191 |
192 | hmm = new DHMM(1,1);
193 |
194 | hmm->Load(argv[2]);
195 |
196 | FILE * fpinput;
197 |
198 | fpinput = fopen(argv[3], "rb");
199 |
200 | int seqlength;
201 |
202 | fread(&seqlength, sizeof(int), 1, fpinput);
203 |
204 | unsigned int * pint = new unsigned int[seqlength];
205 |
206 | fread(pint, sizeof(unsigned int), seqlength, fpinput);
207 |
208 | vector vec;
209 |
210 | vec.assign(pint, pint + seqlength);
211 |
212 | delete [] pint;
213 |
214 | fclose(fpinput);
215 |
216 | //typedef vector Observation;
217 |
218 | Observation obs = hmm->GetProbableStateSequence(vec);
219 |
220 | FILE * fpoutput = fopen(argv[4], "wb");
221 |
222 | int size = obs.size();
223 |
224 | fwrite(&size, sizeof(int), 1, fpoutput);
225 |
226 | Observation::iterator itr;
227 |
228 | for ( itr = obs.begin(); itr < obs.end(); ++itr ) {
229 |
230 | int val = *itr;
231 |
232 | fwrite(&val, sizeof(int), 1, fpoutput);
233 |
234 | }
235 |
236 | }
237 |
238 | else if (calltype ==3) {
239 |
240 | hmm = new DHMM(1,1);
241 |
242 | hmm->Load(argv[2]);
243 |
244 | FILE * fpinput;
245 |
246 | fpinput = fopen(argv[3], "rb");
247 |
248 | int seqlength;
249 |
250 | fread(&seqlength, sizeof(int), 1, fpinput);
251 |
252 | unsigned int * pint = new unsigned int[seqlength];
253 |
254 | fread(pint, sizeof(unsigned int), seqlength, fpinput);
255 |
256 | vector vec;
257 |
258 | vec.assign(pint, pint + seqlength);
259 |
260 | delete [] pint;
261 |
262 | fclose(fpinput);
263 |
264 | //typedef vector Observation;
265 |
266 | //or should be log prob?
267 | double prob = hmm->GetObservationSequenceProbability(vec);
268 |
269 | FILE * fpoutput = fopen(argv[4], "wb");
270 |
271 | fwrite(&prob, sizeof(double), 1, fpoutput);
272 |
273 | fclose(fpoutput);
274 |
275 | }
276 |
277 |
278 | //calls to compare probability of different states
279 |
280 |
281 | delete hmm;
282 |
283 |
284 |
285 | return 0;
286 | }
287 |
--------------------------------------------------------------------------------
/Source/noveltycurve/noveltycurve.cpp:
--------------------------------------------------------------------------------
1 | //This code is part of an extension set for SuperCollider 3 (http://supercollider.sourceforge.net/). We follow the same license terms for SuperCollider 3, releasing under GNU GPL 3
2 | //all code here by Nick Collins http://www.cogs.susx.ac.uk/users/nc81/index.html
3 |
4 |
5 |
6 | #include
7 | #include
8 | #include
9 |
10 | int main (int argc, char * const argv[]) {
11 | // insert code here...
12 | std::cout << "Calculating Novelty Curve\n";
13 |
14 | float * matrix; //if fvs2 NULL,
15 | int matrixsize, numcols;
16 |
17 | FILE * fpinput;
18 | FILE * fpoutput;
19 |
20 | fpinput = fopen(argv[2], "rb");
21 |
22 | fread(&matrixsize, sizeof(int), 1, fpinput);
23 | fread(&numcols, sizeof(int), 1, fpinput);
24 |
25 | matrix = new float[matrixsize];
26 |
27 | fread(matrix, sizeof(float), matrixsize, fpinput);
28 |
29 | fclose(fpinput);
30 |
31 | int kernelsize = atoi(argv[1]);
32 | int halfkernelsize = kernelsize/2;
33 |
34 |
35 | //std::cout << halfkernelsize << " " << kernelsize <<" " << matrixsize << " " << numcols << "\n";
36 |
37 |
38 | // if(numcols<=kernelsize) {
39 | //
40 | // std::cout << "noveltycurve: \n";
41 | //
42 | // return 1;
43 | //
44 | // }
45 |
46 | float * output;
47 |
48 | output = new float[numcols];
49 |
50 | //initial and final zeroes when no room for curve?
51 |
52 | int i,j,k;
53 |
54 | int top = numcols-halfkernelsize-1;
55 |
56 | float sum, mult;
57 | int colbaseindex;
58 |
59 | for (i=0; i=halfkernelsize) && (i<=top)) {
62 |
63 | sum = 0.0;
64 |
65 | //only need to calculate half, and above diagonal
66 | for (j=(i-halfkernelsize); j<(i+halfkernelsize); ++j) {
67 |
68 | colbaseindex = j * numcols;
69 |
70 | for (k=(j+1); k<=(i+halfkernelsize);++k) {
71 |
72 | //hard checkerboard for now
73 | mult = ( (ki) ) ? 1: (-1);
74 |
75 | sum += matrix[colbaseindex+k];
76 |
77 | }
78 |
79 | }
80 |
81 |
82 | // //self consistency of sections either side of i ignored; only care about cross comparisons old section to new
83 | // for (j=(i-halfkernelsize); ji) ) ? 1: (-1);
91 | //
92 | // sum += matrix[colbaseindex+k];
93 | //
94 | // }
95 | //
96 | // }
97 |
98 |
99 | output[i] = sum;
100 |
101 |
102 | }
103 |
104 | else
105 | output[i] = 0.0; //if no room to apply checkerboard, no sensible measurement
106 |
107 |
108 | //std::cout << output[i] << " " ;
109 | }
110 |
111 |
112 | fpoutput = fopen(argv[3], "wb");
113 |
114 | //std::cout << "\n " << argv[3] << " ";
115 |
116 | fwrite(output, sizeof(float), numcols, fpoutput);
117 |
118 | fclose(fpoutput);
119 |
120 | delete [] matrix;
121 |
122 | delete [] output;
123 |
124 | std::cout << "Calculated Novelty Curve\n";
125 |
126 | return 0;
127 | }
128 |
--------------------------------------------------------------------------------