UNPKG

chromaprint-fixed

Version:

A JavaScript implementation of AcoustID Chromaprint

400 lines (289 loc) 18.6 kB
<!DOCTYPE html> <html> <head> <title>calculator.coffee</title> <meta http-equiv="content-type" content="text/html; charset=UTF-8"> <meta name="viewport" content="width=device-width, target-densitydpi=160dpi, initial-scale=1.0; maximum-scale=1.0; user-scalable=0;"> <link rel="stylesheet" media="all" href="../docco.css" /> </head> <body> <div id="container"> <div id="background"></div> <ul id="jump_to"> <li> <a class="large" href="javascript:void(0);">Jump To &hellip;</a> <a class="small" href="javascript:void(0);">+</a> <div id="jump_wrapper"> <div id="jump_page_wrapper"> <div id="jump_page"> <a class="source" href="algorithms.html"> src/algorithms.coffee </a> <a class="source" href="calculator.html"> src/calculator.coffee </a> <a class="source" href="chroma.html"> src/chroma.coffee </a> <a class="source" href="fpcalc.html"> src/fpcalc.coffee </a> <a class="source" href="silenceRemover.html"> src/silenceRemover.coffee </a> </div> </div> </li> </ul> <ul class="sections"> <li id="title"> <div class="annotation"> <h1>calculator.coffee</h1> </div> </li> <li id="section-1"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-1">&#182;</a> </div> </div> <div class="content"><div class='highlight'><pre>@chromaprint ?= {}</pre></div></div> </li> <li id="section-2"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-2">&#182;</a> </div> <p>Creating images comes down to choosing a ‘shade of grey’ from here.</p> </div> <div class="content"><div class='highlight'><pre> GRAYCODE = [ <span class="hljs-number">0</span>, <span class="hljs-number">1</span>, <span class="hljs-number">3</span>, <span class="hljs-number">2</span> ] @chromaprint.GRAYCODE = GRAYCODE</pre></div></div> </li> <li id="section-3"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-3">&#182;</a> </div> <h1 id="images">Images</h1> </div> </li> <li id="section-4"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-4">&#182;</a> </div> <p>Chromaprint does its work on “images” - 2 dimensional arrays which are calculated from an input stream.</p> </div> <div class="content"><div class='highlight'><pre><span class="hljs-function"><span class="hljs-title">integral</span> = <span class="hljs-params">(image)</span> -&gt;</span> result = [] image.forEach (row, i) -&gt; result[i] ?= [] row.forEach (n, j) -&gt; result[i][j] = n + (result[i ]?[j<span class="hljs-number">-1</span>] ? <span class="hljs-number">0</span>) + (result[i<span class="hljs-number">-1</span>]?[j ] ? <span class="hljs-number">0</span>) - (result[i<span class="hljs-number">-1</span>]?[j<span class="hljs-number">-1</span>] ? <span class="hljs-number">0</span>) result <span class="hljs-function"> <span class="hljs-title">area</span> = <span class="hljs-params">(image, x1, y1, x2, y2)</span> -&gt;</span> x1 ?= <span class="hljs-number">0</span> y1 ?= <span class="hljs-number">0</span> x2 ?= image.length y2 ?= image[x2]?.length result = image[x2]?[y2] <span class="hljs-keyword">return</span> <span class="hljs-number">0</span> <span class="hljs-keyword">if</span> x2 &lt; x1 <span class="hljs-keyword">or</span> y2 &lt; y1 <span class="hljs-keyword">if</span> x1 &gt; <span class="hljs-number">0</span> result -= image[x1<span class="hljs-number">-1</span>][y2] result += image[x1<span class="hljs-number">-1</span>][y1<span class="hljs-number">-1</span>] <span class="hljs-keyword">if</span> y1 &gt; <span class="hljs-number">0</span> result -= image[x2][y1<span class="hljs-number">-1</span>] <span class="hljs-keyword">if</span> y1 &gt; <span class="hljs-number">0</span> result <span class="hljs-function"> <span class="hljs-title">measure</span> = <span class="hljs-params">(image)</span> -&gt;</span> h: image.length w: image[<span class="hljs-number">0</span>].length ? <span class="hljs-number">0</span> @chromaprint.Image = { integral, area, measure }</pre></div></div> </li> <li id="section-5"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-5">&#182;</a> </div> <h1 id="filters">Filters</h1> </div> </li> <li id="section-6"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-6">&#182;</a> </div> <p>A filter in chromaprint modifies a portion of an image. There are 5 different filters available. The <code>filter</code> function takes as arguments:</p> <ol> <li>the type of filter (0-4)</li> <li>the y offset from where to apply the comparison</li> <li>the x offset from where to apply the comparison</li> <li>the width upon which to run the comparison</li> <li>the height upon which to run the comparison</li> <li>the actual comparison function (currently always subtraction, but log subtraction is also available.</li> </ol> </div> <div class="content"><div class='highlight'><pre><span class="hljs-function"><span class="hljs-title">filter</span> = <span class="hljs-params">(type, y, w, h, cmp)</span> -&gt;</span> cmp ?= filter.compare.subtractLog <span class="hljs-function"> <span class="hljs-title">f</span> = <span class="hljs-params">(img, x)</span> -&gt;</span> filter[type](img, x, y, w, h, cmp) f.w = w f.h = h f.y = y f filter[<span class="hljs-number">0</span>] = <span class="hljs-function"><span class="hljs-params">(img, x, y, w, h, cmp)</span> -&gt;</span> <span class="hljs-comment"># oooooooooooo</span> a = area(img, x, y, x + w - <span class="hljs-number">1</span>, y + h - <span class="hljs-number">1</span>) <span class="hljs-comment"># oooooooooooo</span> b = <span class="hljs-number">0</span> <span class="hljs-comment"># oooooooooooo</span> cmp(a, b) <span class="hljs-comment"># oooooooooooo</span></pre></div></div> </li> <li id="section-7"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-7">&#182;</a> </div> <p>oooooooooooo oooooooooooo</p> </div> <div class="content"><div class='highlight'><pre> filter[<span class="hljs-number">1</span>] = <span class="hljs-function"><span class="hljs-params">(img, x, y, w, h, cmp)</span> -&gt;</span> <span class="hljs-comment"># ............</span> h2 = h / <span class="hljs-number">2</span> <span class="hljs-comment"># ............</span> a = area(img, x, y + h2, x + w - <span class="hljs-number">1</span>, y + h - <span class="hljs-number">1</span>) <span class="hljs-comment"># ............</span> b = area(img, x, y , x + w - <span class="hljs-number">1</span>, y + h2 - <span class="hljs-number">1</span>) <span class="hljs-comment"># oooooooooooo</span> cmp(a, b) <span class="hljs-comment"># oooooooooooo</span></pre></div></div> </li> <li id="section-8"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-8">&#182;</a> </div> <p>oooooooooooo</p> </div> <div class="content"><div class='highlight'><pre> filter[<span class="hljs-number">2</span>] = <span class="hljs-function"><span class="hljs-params">(img, x, y, w, h, cmp)</span> -&gt;</span> <span class="hljs-comment"># ......oooooo</span> w2 = w / <span class="hljs-number">2</span> <span class="hljs-comment"># ......oooooo</span> a = area(img, x + w2, y, x + w - <span class="hljs-number">1</span>, y + h - <span class="hljs-number">1</span>) <span class="hljs-comment"># ......oooooo</span> b = area(img, x , y, x + w2 - <span class="hljs-number">1</span>, y + y - <span class="hljs-number">1</span>) <span class="hljs-comment"># ......oooooo</span> cmp(a, b) <span class="hljs-comment"># ......oooooo</span></pre></div></div> </li> <li id="section-9"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-9">&#182;</a> </div> <p>……oooooo</p> </div> <div class="content"><div class='highlight'><pre> filter[<span class="hljs-number">3</span>] = <span class="hljs-function"><span class="hljs-params">(img, x, y, w, h, cmp)</span> -&gt;</span> <span class="hljs-comment"># ......oooooo</span> [w2, h2] = [w / <span class="hljs-number">2</span>, h / <span class="hljs-number">2</span>] <span class="hljs-comment"># ......oooooo</span> a = area(img, x , y + h2, x + w2 - <span class="hljs-number">1</span>, y + h - <span class="hljs-number">1</span>) + <span class="hljs-comment"># ......oooooo</span> area(img, x + w2, y , x + w - <span class="hljs-number">1</span>, y + h2 - <span class="hljs-number">1</span>) <span class="hljs-comment"># oooooo......</span> b = area(img, x , y , x + w2 - <span class="hljs-number">1</span>, y + h2 - <span class="hljs-number">1</span>) + <span class="hljs-comment"># oooooo......</span> area(img, x + w2, y + h2, x + w - <span class="hljs-number">1</span>, y + h - <span class="hljs-number">1</span>) <span class="hljs-comment"># oooooo......</span> cmp(a, b) filter[<span class="hljs-number">4</span>] = <span class="hljs-function"><span class="hljs-params">(img, x, y, w, h, cmp)</span> -&gt;</span> <span class="hljs-comment"># ............</span> h3 = h / <span class="hljs-number">3</span> <span class="hljs-comment"># ............</span> a = area(img, x, y + h3, x + w - <span class="hljs-number">1</span>, y + <span class="hljs-number">2</span> * h3 - <span class="hljs-number">1</span>) <span class="hljs-comment"># oooooooooooo</span> b = area(img, x, y , x + w - <span class="hljs-number">1</span>, y + h3 - <span class="hljs-number">1</span>) + <span class="hljs-comment"># oooooooooooo</span> area(img, x, y + <span class="hljs-number">2</span> * h3, x + w - <span class="hljs-number">1</span>, y + h - <span class="hljs-number">1</span>) <span class="hljs-comment"># ............</span> cmp(a, b) <span class="hljs-comment"># ............</span> filter[<span class="hljs-number">5</span>] = <span class="hljs-function"><span class="hljs-params">(img, x, y, w, h, cmp)</span> -&gt;</span> <span class="hljs-comment"># ....oooo....</span> w3 = w / <span class="hljs-number">3</span> <span class="hljs-comment"># ....oooo....</span> a = area(img, x + w3, y, x + <span class="hljs-number">2</span> * w3 - <span class="hljs-number">1</span>, y + h - <span class="hljs-number">1</span>) <span class="hljs-comment"># ....oooo....</span> b = area(img, x , y, x + w3 - <span class="hljs-number">1</span>, y + h - <span class="hljs-number">1</span>) + <span class="hljs-comment"># ....oooo....</span> area(img, x + <span class="hljs-number">2</span> * w3, y, x + w - <span class="hljs-number">1</span>, y + h <span class="hljs-number">-1</span>) <span class="hljs-comment"># ....oooo....</span> cmp(a, b) <span class="hljs-comment"># ....oooo....</span> filter.compare = subtract: <span class="hljs-function"><span class="hljs-params">(a, b)</span> -&gt;</span> a - b subtractLog: <span class="hljs-function"><span class="hljs-params">(a, b)</span> -&gt;</span> Math.log(<span class="hljs-number">1</span> + a) - Math.log(<span class="hljs-number">1</span> + b) @chromaprint.filter = filter</pre></div></div> </li> <li id="section-10"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-10">&#182;</a> </div> <h1 id="quantizer">Quantizer</h1> </div> </li> <li id="section-11"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-11">&#182;</a> </div> <p>Makes a very simple function to return 0, 1, 2, or 3 depending on where the value falls within the three values given.</p> </div> <div class="content"><div class='highlight'><pre><span class="hljs-function"><span class="hljs-title">quantizer</span> = <span class="hljs-params">(t0, t1, t2)</span> -&gt;</span> (value) -&gt; <span class="hljs-keyword">if</span> value &lt; t1 <span class="hljs-keyword">return</span> <span class="hljs-number">0</span> <span class="hljs-keyword">if</span> value &lt; t0 <span class="hljs-keyword">return</span> <span class="hljs-number">1</span> <span class="hljs-keyword">return</span> <span class="hljs-number">2</span> <span class="hljs-keyword">if</span> value &lt; t2 <span class="hljs-number">3</span> @chromaprint.quantizer = quantizer</pre></div></div> </li> <li id="section-12"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-12">&#182;</a> </div> <h1 id="classifiers">Classifiers</h1> </div> </li> <li id="section-13"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-13">&#182;</a> </div> <p>classifier is a higher order function that makes a filter and a quantizer work together on an image.</p> </div> <div class="content"><div class='highlight'><pre><span class="hljs-function"><span class="hljs-title">classifier</span> = <span class="hljs-params">(coefficients)</span> -&gt;</span> f = filter(coefficients.f...) q = quantizer(coefficients.q...) <span class="hljs-function"> <span class="hljs-title">fn</span> = <span class="hljs-params">(image, offset)</span> -&gt;</span> q(f(image, offset)) fn.filterWidth = f.w fn @chromaprint.classifier = classifier</pre></div></div> </li> <li id="section-14"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-14">&#182;</a> </div> <h1 id="calculator">Calculator</h1> </div> </li> <li id="section-15"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-15">&#182;</a> </div> <p>The calculator actually calculates the fingerprint of an image, by creating the right classifier, and passing the image into the resulting function in chunks.</p> </div> <div class="content"><div class='highlight'><pre><span class="hljs-function"><span class="hljs-title">calculator</span> = <span class="hljs-params">(coefficients...)</span> -&gt;</span> classifiers = coefficients.map (cc) -&gt; classifier(cc) max = classifiers.reduce (<span class="hljs-function"><span class="hljs-params">(w, c)</span> -&gt;</span> Math.max(w, c.filterWidth)), <span class="hljs-number">0</span> <span class="hljs-function"> <span class="hljs-title">subfingerprint</span> = <span class="hljs-params">(image, offset)</span> -&gt;</span> <span class="hljs-function"> <span class="hljs-title">classify</span> = <span class="hljs-params">(b, classifier)</span> -&gt;</span> (b &lt;&lt; <span class="hljs-number">2</span>) | GRAYCODE[classifier(image, offset)] classifiers.reduce classify, <span class="hljs-number">0</span> (image) -&gt; length = measure(image).h - max + <span class="hljs-number">1</span> img = integral(image) subfingerprint(img, offset) <span class="hljs-keyword">for</span> offset <span class="hljs-keyword">in</span> [<span class="hljs-number">0.</span>..length] @chromaprint.calculator = calculator</pre></div></div> </li> </ul> </div> </body> </html>