chromaprint-fixed
Version:
A JavaScript implementation of AcoustID Chromaprint
400 lines (289 loc) • 18.6 kB
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 …</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">¶</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">¶</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">¶</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">¶</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> -></span>
result = []
image.forEach (row, i) ->
result[i] ?= []
row.forEach (n, j) ->
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> -></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 < x1 <span class="hljs-keyword">or</span> y2 < y1
<span class="hljs-keyword">if</span> x1 > <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 > <span class="hljs-number">0</span>
result -= image[x2][y1<span class="hljs-number">-1</span>] <span class="hljs-keyword">if</span> y1 > <span class="hljs-number">0</span>
result
<span class="hljs-function">
<span class="hljs-title">measure</span> = <span class="hljs-params">(image)</span> -></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">¶</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">¶</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> -></span>
cmp ?= filter.compare.subtractLog
<span class="hljs-function"> <span class="hljs-title">f</span> = <span class="hljs-params">(img, x)</span> -></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> -></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">¶</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> -></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">¶</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> -></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">¶</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> -></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> -></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> -></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> -></span> a - b
subtractLog: <span class="hljs-function"><span class="hljs-params">(a, b)</span> -></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">¶</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">¶</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> -></span>
(value) ->
<span class="hljs-keyword">if</span> value < t1
<span class="hljs-keyword">return</span> <span class="hljs-number">0</span> <span class="hljs-keyword">if</span> value < 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 < 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">¶</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">¶</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> -></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> -></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">¶</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">¶</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> -></span>
classifiers = coefficients.map (cc) ->
classifier(cc)
max = classifiers.reduce (<span class="hljs-function"><span class="hljs-params">(w, c)</span> -></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> -></span>
<span class="hljs-function"> <span class="hljs-title">classify</span> = <span class="hljs-params">(b, classifier)</span> -></span> (b << <span class="hljs-number">2</span>) | GRAYCODE[classifier(image, offset)]
classifiers.reduce classify, <span class="hljs-number">0</span>
(image) ->
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>