[go: up one dir, main page]

Menu

[r53]: / ref1 / src / ai / An.java  Maximize  Restore  History

Download this file

409 lines (329 with data), 10.5 kB

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
/*
* Created on 13/02/2006
*
* @author Rene Wooller
*/
package ai;
import java.util.TreeMap;
import jm.music.data.Part;
import jm.music.data.Phrase;
import jm.music.tools.Mod;
import ren.tonal.TonalComposite;
import ren.util.AD;
import ren.util.ODouble;
import com.lemu.music.LPart;
/**
* has analysis functions and others
* @author wooller
*
*13/02/2006
*
* Copyright JEDI/Rene Wooller
*
*/
public class An {
/**
* returns the average
*
*/
public static double avpi(Part p) {
TonalComposite tc = new TonalComposite();
tc.addPart(p);
return tc.getMeanPitch();
}
/**
* finds the fundamental pitch (the root pitch that is nearest to the
* pitch centroid).
* This is the pitch with pitch class of the key, and most central register
* @param lp
* @return
*/
public static int fun(LPart lp) {
if (!lp.isDEPA()) {
int root = lp.getTonalManager().getRoot(); // root key
double avpi = An.avpi(lp.getPart()); // pitch centroid
int spo = lp.getTonalManager().getStepsPerOctave();
int oct = (int) (avpi / spo);
int fund = 0; // nearest fundamental key pitch to pitch centroid
fund = (oct * spo + root);
return fund;
} else {
int root = 0; //root is always 0 in DEPA mode
double avpi = An.avpi(lp.getPart()); // pitch centroid
// PO.p("an. av pitch = " + avpi);
int spo = lp.getTonalManager().getPassingSteps()*
lp.getTonalManager().getScale().length;
int oct = (int) (avpi / spo);
// nearest fundamental key pitch to pitch centroid
int fund = (oct * spo + root);
return fund;
}
}
/**
* find the phrase with the specified start time
* @param p
* @param st
* @param dev the acceptable level of deviation (for it to be the same)
* @return
*/
public static Phrase getSt(Part p, double st, double dev) {
// PO.p("st to get = " + st);
for(int i=0; i< p.length(); i++) {
// PO.p("p.getPhrase(i).getStartTime() = " + p.getPhrase(i).getStartTime());
if(p.getPhrase(i).getStartTime() > st - AD.DFACC - dev&&
p.getPhrase(i).getStartTime() < st + AD.DFACC + dev) {
return p.getPhrase(i);
}
}
return null;
}
/**
* if the lp is empty, returns scope + quantise (as if there was a note
* just outside the scope.
*
* @param lp
* @return
*/
public static double avio(LPart lp) {
Part p = lp.getPart().copyRT(0, lp.getScope().getValue());
if(p.getSize() == 0)
return lp.getScope().getValue() + lp.getQuantise().getValue();
if(p.getSize() == 1)
return lp.getScope().getValue();
Mod.quickSort(p);
//start with the first interval
double first = p.getPhrase(0).getStartTime();
double prev = first;
double next = p.getPhrase(1).getStartTime();
// thwart double accuracy errors
final double ac = 0.0001;
int v = (int)((next-prev)/ac + 0.5);
// if there are more than two, iterate through
for(int i=2; i< p.getSize(); i++) {
prev = next;
next = p.getPhrase(i).getStartTime();
v += (int)((next-prev)/ac + 0.5);
}
// include the interval betweeen the last one and the first
prev = next;
next = first + lp.getScope().getValue();
v += (int)((next-prev)/ac + 0.5);
// normalise from the number of intervals and return
return (v*ac)/(p.getSize()*1.0);
}
/**
* finds the nearest according to onset alone) notephrase to the one
* specified and returns the key that points to it
* returns null if the tree is empty
* @param t
* @param k
* @return
*/
public static ODouble nnonset(TreeMap t, ODouble k, double scope) {
// deal with unusual cases
if(t.containsKey(k))
return k;
if(t.isEmpty())
return null;
if(t.size() == 1)
return (ODouble) t.firstKey();
ODouble pre = (ODouble)((t.headMap(k)).lastKey());
// if it loops around, these are activated to get the correct distance
double pref = 0;
double nexf = 0;
// if there is nothing before it, loop around to the end
if(pre == null) {
pre = (ODouble) t.lastKey();
pref = -scope;
}
ODouble nex = (ODouble) ((t.tailMap(k)).firstKey());
// if there is nothing after it, loop to the beginning
if(nex == null) {
nex = (ODouble) t.firstKey();
nexf = scope;
}
// if pre is nearest neighbour, return it, otherwise, return nex
if((Math.abs(pre.v+pref - k.v) < Math.abs(nex.v + nexf - k.v)))
return pre;
else return nex;
}
/**
* nearest neighbour using all parameters
* goes through all the phrases in t, and finds the most similar
* one to k. Puts the index (in t) of this phrase into index[0] for
* reference, and returns the distance that was recorded.
*
* if there isn't anything in t, it returns NaN (there are no nearest
* neighbour).
*
* @param t
* @param k
* @param acom
* @param index
* @param sco (optional) scope. put <0 if not wanting to use it
* @return the distance between 0 and 1 of k and it's nn in t (0 is same)
*/
public static double nnall(Part t, Phrase k, AllCompare acom,
int [] index, double sco) {
// if there isn't anything in it to be nearest to, return NaN
if(t.length() == 0)
return Double.NaN;
double mindis = Double.MAX_VALUE; // the current smallest distance
// go through, finding the distances from each phrase in t
for(int i=0; i< t.length(); i++) {
// current distance is inverted similarity (0 is most similar)
double currdis = 1- acom.comp(t.getPhrase(i), k, sco);
// if the current distance is smaller (they are similar), record it
if(currdis < mindis) {
index[0] = i;
mindis = currdis;
}
}
// return the smallest distance
return mindis;
}
/**
* Average Pitch Interval from Fundamental
* @param p
* @see #fun(LPart)
* @return the average interval from fundamental
*/
public static double avpinfu(LPart p) {
int fu = An.fun(p);
int accints = 0;
for(int i=0; i< p.getPart().length(); i++) {
accints += Math.abs(p.getPart().getPhrase(i).getNote(0).getPitch()-
fu);
}
return ((accints*1.0)/(p.getPart().size()*1.0));
}
/**
* go through the part and find the pitch range and the lowest pitch
* @param p
* @return int [] [0] = range [1] = lowest pitch [2] = highest
*/
public static int[] pitchRange(Part p) {
if(p.size() == 0)
return null;
int [] ret = new int [3];
int lv = Integer.MAX_VALUE;
int hv = Integer.MIN_VALUE;
for(int i=0; i< p.length(); i++) {
ret[0] = p.getPhrase(i).getNote(0).getPitch(); // store the pitch
if(ret[0] < lv) // if it is smaller than the record
lv = ret[0]; // record it
if(ret[0] > hv) //if it is bigger, record it
hv = ret[0];
}
ret[0] = hv-lv; // range
ret[1] = lv; // low
ret[2] = hv; // high
return ret;
}
public static Phrase getSame(Part part, Phrase phr) {
for(int i=0; i< part.length(); i++) {
if(part.getPhrase(i).getStartTime() > phr.getStartTime() - 0.0001 &&
part.getPhrase(i).getStartTime() < phr.getStartTime() + 0.0001 &&
part.getPhrase(i).getNote(0).getPitch() == phr.getNote(0).getPitch()) //&&
// part.getPhrase(i).getNote(0).getDuration() > phr.getNote(0).getDuration() - 0.0001 &&
// part.getPhrase(i).getNote(0).getDuration() < phr.getNote(0).getDuration() + 0.0001 &&
// part.getPhrase(i).getNote(0).getDynamic() == phr.getNote(0).getDynamic()) {
{
return part.getPhrase(i);
}
}
return null;
}
/**
* takes a part, and records the intervals between notes that have the same
* start time.
*
* eg 0:60 0:65 1:62 1:59
* becomes
* {{0, 5}, {3, 0}}
*
* @param p
* @return
*/
public static int [][] harmonicClumps(final Part p, double er) {
int [][] harda = new int [p.getSize()][];
int cluc = 0; // counts the number of clumpings
for(int i=0; i< p.length();) {
//PO.p("cluc " + cluc);
int [] temp = new int [p.length()]; // holds the intervals
Phrase curr = p.getPhrase(i);
int j=0;
temp[j++] = 0;
int [] loint = new int [] {0, 0}; // value, index of lowest interval
// PO.p("curr pitch = " + curr.getNote(0).getPitch());
while(i+j < p.getSize() &&
p.getPhrase(i+j).getStartTime() >= curr.getStartTime() - er &&
p.getPhrase(i+j).getStartTime() <= curr.getStartTime() + er) {//&&
// record the interval
temp[j] = p.getPhrase(i+j).getNote(0).getPitch() -
curr.getNote(0).getPitch();
if(temp[j] < loint[0]) {
loint[0] = temp[j];
loint[1] = j;
}
j++;
}
harda[i-cluc] = new int [j];
for(int k = 0; k < j; k++) {
//if((i-cluc) < 0)
// PO.p("error " + i + " cluc " + cluc);
// PO.p("i-cluc" + (i-cluc));
// PO.p("was " + harda[i-cluc][k]);
harda[i-cluc][k] = temp[k] + loint[0]*-1;
// PO.p("now " + harda[i-cluc][k]);
}
i = i+j;
cluc += j-1;
}
return harda;
}
public static double avhain(final Part p, double er) {
int [][] harc = An.harmonicClumps(p, er);
// PO.p("part = \n", p, 1);
// PO.p("harc = \n", harc);
return avhain(harc, er);
}
public static double avhain(int [][] harc, double er) {
double ain = 0;
int noc = 0;
for(int i=0; i<harc.length; i++) {
if(harc[i] == null) // the tail is always null
break;
//temporary storage of the average interval
double tain = 0;
for(int j=0; j< harc[i].length; j++) {
tain += harc[i][j];
}
// if there is an interval,
if(harc[i].length-1 > 0) {
// take the average of all the intervals in this chunk
tain = tain/(harc[i].length-1);
}
// accumulate the average interval
ain += tain;
noc ++;
}
return ain/noc*1.0;
}
/**
* the average number of notes in each harmonic clump
* @param fh
* @return
*/
public static double avhasize(int[][] fh) {
double av = 0;
int count = 0;
for(int i=0; i< fh.length; i++) {
if(fh[i] == null)
break;
av += fh[i].length;
count++; // becauase fh is likely to have null entries
}
return av/count;
}
}