UNPKG

chromaprint-fixed

Version:

A JavaScript implementation of AcoustID Chromaprint

241 lines (212 loc) 6.95 kB
// Generated by CoffeeScript 2.2.3 (function() { var GRAYCODE, area, calculator, classifier, filter, integral, measure, quantizer; if (this.chromaprint == null) { this.chromaprint = {}; } // Creating images comes down to choosing a 'shade of grey' from here. GRAYCODE = [0, 1, 3, 2]; this.chromaprint.GRAYCODE = GRAYCODE; // Images // ====== // Chromaprint does its work on "images" - 2 dimensional arrays which are // calculated from an input stream. integral = function(image) { var result; result = []; image.forEach(function(row, i) { if (result[i] == null) { result[i] = []; } return row.forEach(function(n, j) { var ref, ref1, ref2, ref3, ref4, ref5; return result[i][j] = n + ((ref = (ref1 = result[i]) != null ? ref1[j - 1] : void 0) != null ? ref : 0) + ((ref2 = (ref3 = result[i - 1]) != null ? ref3[j] : void 0) != null ? ref2 : 0) - ((ref4 = (ref5 = result[i - 1]) != null ? ref5[j - 1] : void 0) != null ? ref4 : 0); }); }); return result; }; area = function(image, x1, y1, x2, y2) { var ref, ref1, result; if (x1 == null) { x1 = 0; } if (y1 == null) { y1 = 0; } if (x2 == null) { x2 = image.length; } if (y2 == null) { y2 = (ref = image[x2]) != null ? ref.length : void 0; } result = (ref1 = image[x2]) != null ? ref1[y2] : void 0; if (x2 < x1 || y2 < y1) { return 0; } if (x1 > 0) { result -= image[x1 - 1][y2]; if (y1 > 0) { result += image[x1 - 1][y1 - 1]; } } if (y1 > 0) { result -= image[x2][y1 - 1]; } return result; }; measure = function(image) { var ref; return { h: image.length, w: (ref = image[0].length) != null ? ref : 0 }; }; this.chromaprint.Image = {integral, area, measure}; // Filters // ======= // A filter in chromaprint modifies a portion of an image. There are 5 // different filters available. The `filter` function takes as arguments: // 1. the type of filter (0-4) // 2. the y offset from where to apply the comparison // 3. the x offset from where to apply the comparison // 4. the width upon which to run the comparison // 5. the height upon which to run the comparison // 6. the actual comparison function (currently always subtraction, but log // subtraction is also available. filter = function(type, y, w, h, cmp) { var f; if (cmp == null) { cmp = filter.compare.subtractLog; } f = function(img, x) { return filter[type](img, x, y, w, h, cmp); }; f.w = w; f.h = h; f.y = y; return f; }; filter[0] = function(img, x, y, w, h, cmp) { // oooooooooooo var a, b; a = area(img, x, y, x + w - 1, y + h - 1); // oooooooooooo b = 0; // oooooooooooo return cmp(a, b); // oooooooooooo }; // oooooooooooo // oooooooooooo filter[1] = function(img, x, y, w, h, cmp) { // ............ var a, b, h2; h2 = h / 2; // ............ a = area(img, x, y + h2, x + w - 1, y + h - 1); // ............ b = area(img, x, y, x + w - 1, y + h2 - 1); // oooooooooooo return cmp(a, b); // oooooooooooo }; // oooooooooooo filter[2] = function(img, x, y, w, h, cmp) { // ......oooooo var a, b, w2; w2 = w / 2; // ......oooooo a = area(img, x + w2, y, x + w - 1, y + h - 1); // ......oooooo b = area(img, x, y, x + w2 - 1, y + y - 1); // ......oooooo return cmp(a, b); // ......oooooo }; // ......oooooo filter[3] = function(img, x, y, w, h, cmp) { // ......oooooo var a, b, h2, w2; [w2, h2] = [ w / 2, h / 2 // ......oooooo ]; a = area(img, x, y + h2, x + w2 - 1, y + h - 1) + area(img, x + w2, y, x + w - 1, y + h2 - 1); // ......oooooo // oooooo...... b = area(img, x, y, x + w2 - 1, y + h2 - 1) + area(img, x + w2, y + h2, x + w - 1, y + h - 1); // oooooo...... // oooooo...... return cmp(a, b); }; filter[4] = function(img, x, y, w, h, cmp) { // ............ var a, b, h3; h3 = h / 3; // ............ a = area(img, x, y + h3, x + w - 1, y + 2 * h3 - 1); // oooooooooooo b = area(img, x, y, x + w - 1, y + h3 - 1) + area(img, x, y + 2 * h3, x + w - 1, y + h - 1); // oooooooooooo // ............ return cmp(a, b); // ............ }; filter[5] = function(img, x, y, w, h, cmp) { // ....oooo.... var a, b, w3; w3 = w / 3; // ....oooo.... a = area(img, x + w3, y, x + 2 * w3 - 1, y + h - 1); // ....oooo.... b = area(img, x, y, x + w3 - 1, y + h - 1) + area(img, x + 2 * w3, y, x + w - 1, y + h(-1)); // ....oooo.... // ....oooo.... return cmp(a, b); // ....oooo.... }; filter.compare = { subtract: function(a, b) { return a - b; }, subtractLog: function(a, b) { return Math.log(1 + a) - Math.log(1 + b); } }; this.chromaprint.filter = filter; // Quantizer // ========= // Makes a very simple function to return 0, 1, 2, or 3 depending on where the // value falls within the three values given. quantizer = function(t0, t1, t2) { return function(value) { if (value < t1) { if (value < t0) { return 0; } return 1; } if (value < t2) { return 2; } return 3; }; }; this.chromaprint.quantizer = quantizer; // Classifiers // =========== // classifier is a higher order function that makes a filter and a quantizer // work together on an image. classifier = function(coefficients) { var f, fn, q; f = filter(...coefficients.f); q = quantizer(...coefficients.q); fn = function(image, offset) { return q(f(image, offset)); }; fn.filterWidth = f.w; return fn; }; this.chromaprint.classifier = classifier; // Calculator // ========== // The calculator actually calculates the fingerprint of an image, by creating // the right classifier, and passing the image into the resulting function in // chunks. calculator = function(...coefficients) { var classifiers, max, subfingerprint; classifiers = coefficients.map(function(cc) { return classifier(cc); }); max = classifiers.reduce((function(w, c) { return Math.max(w, c.filterWidth); }), 0); subfingerprint = function(image, offset) { var classify; classify = function(b, classifier) { return (b << 2) | GRAYCODE[classifier(image, offset)]; }; return classifiers.reduce(classify, 0); }; return function(image) { var img, k, length, offset, ref, results; length = measure(image).h - max + 1; img = integral(image); results = []; for (offset = k = 0, ref = length; (0 <= ref ? k < ref : k > ref); offset = 0 <= ref ? ++k : --k) { results.push(subfingerprint(img, offset)); } return results; }; }; this.chromaprint.calculator = calculator; }).call(this);