Ignore:
Timestamp:
2015-05-18T16:42:17+12:00 (9 years ago)
Author:
davidb
Message:

Completed version of Krumhansl's algorithm. Plus adding in of About bar. Plus tidy up of the 'by xxx' template

File:
1 edited

Legend:

Unmodified
Added
Removed
  • main/trunk/model-sites-dev/respooled/collect/popup-video-respooled/js/key-detection.js

    r29892 r29893  
    11"use strict";
    22
    3 // From:
     3// Key detection algorithm, as described at:
    44//  http://rnhart.net/articles/key-finding/
     5
     6// (from this web site) ....
     7//  Krumhansl-Schmuckler key-finding algorithm (by Carol L. Krumhansl
     8//  and Mark A. Schmuckler). The profile numbers came from experiments
     9//  done by Krumhansl and Edward J. Kessler. The experiments consisted
     10//  of playing a set of context tones or chords, playing a probe tone,
     11//  and asking a listener to rate how well the probe tone fit with the
     12//  context. You can read about the experiments and the algorithm in
     13//  Krumhansl's book Cognitive Foundations of Musical Pitch. (The
     14//  experiments are described in Chapter 2. The key-finding algorithm
     15//  is described in Chapter 4.) You may be able to read portions of
     16//  the book on Google Books.
     17
     18
     19function khMean(vals)
     20{
     21    var len = vals.length;
     22    if (len==0) {
     23    return 0;
     24    }
     25
     26    var total = 0;
     27
     28    for (var i=0; i<len; i++) {
     29    total += vals[i];
     30    }
     31
     32    return total/len;
     33}
     34
     35function khUnbiased(vals)
     36{
     37    var unbiased_vals = [];
     38
     39    var len = vals.length;
     40    if (len==0) {
     41    return unbiased_vals;
     42    }
     43
     44    var avg = khMean(vals);
     45
     46    for (var i=0; i<len; i++) {
     47    unbiased_vals.push(vals[i] - avg)
     48    }
     49
     50    return unbiased_vals;
     51}
     52
     53
     54function khCorrelationCoefficientUnbiased(a1,a2)
     55{
     56    var a1_len = a1.length;
     57    var a2_len = a2.length;
     58   
     59    if (a1_len != a2_len) {
     60    throw "khCorrelationCoefficientUnbiased(): arrays should be of the same length (" + a1_len + " vs " + a2_len + ")";
     61    }
     62    var len = a1_len;
     63
     64    var a1_a2_pair_prod = 0;
     65    var a1_square = 0;
     66    var a2_square = 0;
     67
     68    for (var i=0; i<len; i++) {
     69    a1_a2_pair_prod += (a1[i]*a2[i]);
     70    a1_square += (a1[i]*a1[i]);
     71    a2_square += (a2[i]*a2[i]);
     72    }
     73
     74    return a1_a2_pair_prod / Math.sqrt(a1_square * a2_square);
     75}
     76
    577
    678//major profile
     
    1991var kh_minor_profile = [6.33, 2.68, 3.52, 5.38, 2.60, 3.53, 2.54, 4.75, 3.98, 2.69, 3.34, 3.17 ];
    2092
     93var kh_major_profile_unbiased = khUnbiased(kh_major_profile);
     94
     95var kh_minor_profile_unbiased = khUnbiased(kh_minor_profile);
     96
    2197
    2298function khCreatePairing(profile,chromatic_scale_durations,offset)
    2399{
    24     var pairing = [];
    25     for (var p=0; p<12; p++) {
    26     pairing.push({"p":profile[p],"c":chromatic_scale_durations[(p+s)%12]});
     100    var p = [];
     101    var c = [];
     102    for (var i=0; i<12; i++) {
     103    p.push(profile[i])
     104    c.push(chromatic_scale_durations[(i+offset)%12]);
    27105    }
    28106   
    29107
    30     return pairing;
    31 }
    32 
    33 
    34 function khCreateAllPairings(chromatic_scale_durations,major_profile,minor_profile)
     108    return {"p":p, "c":c};
     109}
     110
     111
     112function khCreateAllPairings(chromatic_scale_durations_unbiased,major_profile_unbiased,minor_profile_unbiased)
    35113{
    36114    // chromatic_scale_durations.length = 12
     
    45123    var scale = chromatic_keys[s];
    46124
    47     major_pairings[scale] = khCreatePairing(major_profile,chromatic_scale_durations,s);
    48     minor_pairings[scale] = khCreatePairing(minor_profile,chromatic_scale_durations,s);
     125    major_pairings[scale] = khCreatePairing(major_profile_unbiased,chromatic_scale_durations_unbiased,s);
     126    minor_pairings[scale] = khCreatePairing(minor_profile_unbiased,chromatic_scale_durations_unbiased,s);
    49127    }
    50128
    51129    return {"major" : major_pairings, "minor" : minor_pairings};
    52 
    53 }
    54 
    55 
     130}
     131
     132function khComputeKeyCorrelationCoefficients(key_pairings_unbiased)
     133{
     134    var correlations = {};
     135
     136    var keys = Object.keys(key_pairings_unbiased);
     137    var keys_len = keys.length;
     138
     139    for (var k=0; k<keys_len; k++) {
     140   
     141    var key = keys[k];
     142    var key_pairing_unbiased = key_pairings_unbiased[key];
     143
     144    var correlation_coeff = khCorrelationCoefficientUnbiased(key_pairing_unbiased.p,key_pairing_unbiased.c);
     145    correlations[key] = correlation_coeff;
     146    }
     147
     148    return correlations;
     149}
     150
     151
     152function khComputeAllCorrelationCoefficients(pairings_unbiased)
     153{
     154    var major_correlation = khComputeKeyCorrelationCoefficients(pairings_unbiased.major);
     155    var minor_correlation = khComputeKeyCorrelationCoefficients(pairings_unbiased.minor);
     156
     157    return {"major" : major_correlation, "minor" : minor_correlation};
     158}
     159
     160function khFindMaxCorrelation(alignments)
     161{
     162    var max_val = 0;
     163    var max_key = null;
     164
     165    var major_minor = [ "major", "minor" ];
     166
     167    for (var m=0; m<major_minor.length; m++) {
     168    var mm = major_minor[m];
     169    var mm_alignment = alignments[mm];
     170
     171    var mm_keys = Object.keys(mm_alignment);
     172
     173    for (var k=0; k<mm_keys.length; k++) {
     174        var key = mm_keys[k];
     175        var correlation_coeff = mm_alignment[key];
     176        if (correlation_coeff>max_val) {
     177        max_val = correlation_coeff;
     178        max_key = key + " (" + mm + ")";
     179        }
     180    }
     181    }   
     182   
     183    return { "key": max_key, "score": max_val };
     184}
     185
     186function khKeyDetection(chromatic_scale_durations)
     187{
     188    // Work out durations of MIDI events folded into octave (60=Middle-C)
     189    // (unbiased data)
     190
     191    //var chromatic_scale_durations = [ 432, 231, 0, 405, 12, 316, 4, 126, 612, 0, 191, 1];
     192    var chromatic_scale_durations_unbiased = khUnbiased(chromatic_scale_durations);
     193
     194    // Generate all (unbiased) pairings
     195    var kh_pairings_unbiased = khCreateAllPairings(chromatic_scale_durations_unbiased,kh_major_profile_unbiased,kh_minor_profile_unbiased)
     196
     197    // Compute Correlation Coefficients
     198    var kh_alignments = khComputeAllCorrelationCoefficients(kh_pairings_unbiased);
     199
     200    // Pick highest values
     201   
     202    var strongest_profile = khFindMaxCorrelation(kh_alignments);
     203
     204    console.log("Predicted Key: " + JSON.stringify(strongest_profile));
     205
     206    return strongest_profile;
     207
     208}
     209
     210
Note: See TracChangeset for help on using the changeset viewer.