Menu

#40 Unexpected quantisation exceptions

open
nobody
5
2013-02-07
2011-01-27
Anonymous
No

When analysing some apparently well-formed MIDI files, the PhraseAnalysis.getStatisticsAsDoubles() method returns quantisation exceptions for these four statistics:

03 - Key Centeredness: QuantisationException
04 - Tonal Deviation: QuantisationException
13 - Note Density: QuantisationException
14 - Rest Density: QuantisationException

I'm pretty sure the reason this happens is that the statistics are using the PhraseAnalysis.isQuantised() method, and it regards a note of rhythm value 0.500000001 (or similar) as NOT quantised. See this code in PhraseAnalysis.isQuantised():

for (int i = 0; i < noteArray.length; i++) {
if (noteArray[i].getRhythmValue () % duration != 0.0) {
return false;
}
}

This will return false if the duration doesn't divide each note's rhythm value perfectly. Floats and doubles can't represent every number exactly, so sometimes 0.5f turns out to be 0.5000000001. I don't fully understand this, but in general it's dangerous to compare floating-point numbers using "==" and "!=". Maybe this code would be better?

for (int i = 0; i < noteArray.length; i++) {
// allow for floating-point error
if (noteArray[i].getRhythmValue () % duration > 0.0001) {
return false;
}
}

The MIDI file which gave me these slightly-off rhythm values is this one: http://www.glasspages.org/opening.mid

Also, I'll attach source code which performs the analysis, so you can see the exceptions as I pasted them above. It also prints out the rhythm values.

Thanks for your attention, and of course for making jMusic!

James McDermott

Discussion

  • Anonymous

    Anonymous - 2011-01-27

    Oops -- just noticed two extra things.

    First, the MIDI file I mentioned includes rhythm values like 0.3333333333 as well as like 0.25 and 0.500000001, so the correct quantum is (1/3)*(1/4) = 1/12. This is reflected in the new FileAnalysis.java, attached.

    Also, in addition to 0.50000001, it's possible to get 0.4999999999. This causes a similar error, but needs to be taken care of in the isQuantised() code also. This seems to work for me:

    for (int i = 0; i < noteArray.length; i++) {
    // allow for floating-point error
    double epsilon = 0.000001;
    double excessRV = noteArray[i].getRhythmValue () % duration;
    if (excessRV > epsilon && excessRV < duration - epsilon) {
    System.out.println("not quantised: rv = " + noteArray[i].getRhythmValue ());
    System.out.println("also: rv % dur = " + excessRV);
    return false;
    }
    }
    return true;

     

Log in to post a comment.

MongoDB Logo MongoDB