From ec3bf0659a01819ff60cd6eb70da8f03981b3fb5 Mon Sep 17 00:00:00 2001 From: trquoccuong Date: Tue, 24 Jan 2017 14:32:43 +0700 Subject: [PATCH] update document --- .gitignore | 1 + README.md | 21 ++++++++ lib/render.js | 102 ++++++++++++++++++++---------------- package.json | 7 +-- test/out.png | Bin 5776 -> 5870 bytes test/{test.js => render.js} | 3 +- 6 files changed, 85 insertions(+), 49 deletions(-) rename test/{test.js => render.js} (64%) diff --git a/.gitignore b/.gitignore index 3aadc51..3868748 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,4 @@ out gen node_modules/ +npm-debug.log diff --git a/README.md b/README.md index e69de29..ed60445 100644 --- a/README.md +++ b/README.md @@ -0,0 +1,21 @@ +# Node wave +Render audio waveform like SoundCloud on backend + +## Requirement + This module base on `web-audio-api` and `canvas` modules. +## Attribute + +| Option | Desciption | Default | +| ------------- | ------------- | ------------- | +| width | Image width | 600px | +| height | Image height | 80px | +| barWidth | bar height | 3px | +| barGrap | space between 2 bar | 0.2 | +| waveColor | bar color | gray | +| waveAlpha | bar opacity | 1 | +| backgroundColor | Image background color | #fff | +| backgroundImage | setup image background | | +| baseline | center of waveform | 40 | +| padding | Top and bottom padding | 10 | +| baselineWidth | baseline width | 1 | +| baselineColor | baseline color | white | diff --git a/lib/render.js b/lib/render.js index 597d65e..73e8df6 100644 --- a/lib/render.js +++ b/lib/render.js @@ -1,58 +1,46 @@ -var AudioContext = require('web-audio-api').AudioContext; -var fs = require('fs'); +var AudioContext = require('web-audio-api').AudioContext +var fs = require('fs') var Canvas = require('canvas') -Array.prototype.max = function () { - return Math.max.apply(null, this); -}; - var defaultSetting = { width: 600, height: 80, barWidth: 3, barGap: 0.2, - waveColor: "blue", + waveColor: 'gray', waveAlpha: 1, backgroundColor: '#fff', - baseline: 60, -} - -function bufferMeasure(position, length, data) { - var sum = 0.0 - for (var i = position; i <= (position + length) - 1; i++) { - sum += Math.pow(data[i], 2) - } - return Math.sqrt(sum / data.length) + backgroundImage: '', + baseline: 40, + padding: 10, + baselineWidth: 0, + baselineColor: 'white' } -module.exports = function render(url, options, cb) { - var audioContext = new AudioContext - if (typeof options === "function") { +module.exports = function render (url, options, cb) { + var audioContext = new AudioContext() + if (typeof options === 'function') { cb = options options = {} } options = Object.assign({}, defaultSetting, options) if (options.baseline < 0 && options.height < 0 && options.width < 0) { - return cb( new Error('Size must be greater than 0')) + return cb(new Error('Size must be greater than 0')) } if (options.baseline > options.height) { - return cb( new Error('Baseline must be smaller than waveform height')) + return cb(new Error('Baseline must be smaller than waveform height')) } var canvas = new Canvas(options.width, options.height) var canvasContext = canvas.getContext('2d') - - fs.readFile("Canon.mp3", function (err, buffer) { - - if (err) { - return cb(err) - } + fs.readFile(url, function (err, buffer) { + if (err) return cb(err) audioContext.decodeAudioData(buffer, function (result) { var data = result.getChannelData(0) - var step = Math.floor(result.length / options.width) + var step = Math.floor(data.length / options.width) var ratio = options.baseline / options.height var vals = [] @@ -62,31 +50,55 @@ module.exports = function render(url, options, cb) { canvasContext.fillStyle = options.waveColor for (var i = 0; i < options.width; i += options.barWidth) { - vals.push(bufferMeasure(i * step, step, data) * 10000); + var position = i * step + var sum = 0.0 + for (var j = position; j <= (position + step) - 1; j++) { + sum += Math.pow(data[j], 2) + } + vals.push(Math.sqrt(sum / data.length) * 10000) } - for (var j = 0; j < options.width; j += options.barWidth) { - var scale = options.height / vals.max(); - var val = bufferMeasure(j * step, step, data) * 10000; - val *= scale; - val += 1; - var w = options.barWidth; + var maxValue = Math.max.apply(null, vals) + + vals.forEach(function (val, index) { + var scale = options.height / maxValue + val *= scale + var w = options.barWidth if (options.barGap !== 0) { - w *= Math.abs(1 - options.barGap); + w *= Math.abs(1 - options.barGap) + } + var x = index * options.barWidth + (w / 2) + + var lowerHeight = val * ratio + + if (lowerHeight < options.padding) { + lowerHeight = 1 + } else { + lowerHeight -= options.padding } - var x = j + (w / 2); - var lowerHeight = val * ratio; - var upperHeight = val * (1 - ratio); + var upperHeight = val * (1 - ratio) - if(options.waveAlpha < 1) { - canvasContext.clearRect(x, options.baseline, w, upperHeight); - canvasContext.clearRect(x, options.baseline, w, -lowerHeight); + if (upperHeight < options.padding) { + upperHeight = 1 + } else { + upperHeight -= options.padding } - canvasContext.fillRect(x, options.baseline, w, upperHeight); - canvasContext.fillRect(x, options.baseline, w, -lowerHeight); + + if (options.waveAlpha < 1) { + canvasContext.clearRect(x, options.baseline, w, upperHeight) + canvasContext.clearRect(x, options.baseline, w, -lowerHeight) + canvasContext.globalAlpha = options.waveAlpha + } + canvasContext.fillRect(x, options.baseline, w, upperHeight) + canvasContext.fillRect(x, options.baseline, w, -lowerHeight) + }) + + if (options.baselineWidth >= 1) { + canvasContext.fillStyle = options.baselineColor + canvasContext.fillRect(0, options.baseline - (options.baselineWidth / 2), options.width, options.baselineWidth) } cb(null, canvas.toBuffer()) }) - }); + }) } diff --git a/package.json b/package.json index 9001065..50f75f4 100644 --- a/package.json +++ b/package.json @@ -1,10 +1,10 @@ { "name": "node-wave", - "version": "0.0.1", - "description": "", + "version": "0.0.2", + "description": "Render waveform image like SoundCloud", "main": "index.js", "scripts": { - "test": "standard && node index.js" + "lint": "standard" }, "author": "Tran Quoc Cuong ", "license": "MIT", @@ -13,6 +13,7 @@ "web-audio-api": "^0.2.2" }, "devDependencies": { + "mocha": "^3.2.0", "standard": "^8.6.0" } } diff --git a/test/out.png b/test/out.png index 33091f0a8d69b50356ebfa414a7584c37c4c1590..015b09e7860046b5818a33531fe69bb54a5c8b0b 100644 GIT binary patch literal 5870 zcmb7|cT|&0x5iPipmY!rDbjoI{U{(+iUJ8p0IAYDNI%j$(nPueq!+0HAs|wu2$5bw zktQ|LYk=J0o_oHv?w{YwyArZi-pS0KXYctvJ4#1Oh3GErT|7KIA~jW|=XiM6q2Pa= zJGa1VSLwYE;O&;VhKdp%?&|L&vM3o3k1AhH>9O9MEX?ewJH_gQ+dEZ!8m(5R-S3@4 z+Qo~A2)KwLZ?CD7Cy8nn+71`kp5&`i&=y$=|8x;`?5X1-rKVO@lqA=bOwxSo>*(M6 z-fH%F(Arw|#K(HCic|yfd6c%y;@AdR^K`)6YQTk)fhcWUa3r`0lO4VAl)x23M8V4p zt`MR2t2beq|K=k!e(D-28JVuNq{kX5rSfBMZ;2xe+Qv)r-gKVE=Wh#wc|)8vwY7=a z1J~)vhMo+g(J>1PugVQ;Z#8>igk|XRd|rAGjQedPIcm0BT4XgfH9vm(bd!aJg@%#w zdyQ59=T{xKYCA;ZP^*fPbm3!mVFT78GFh<{QA&7thK7bceSPFr2?+@-ly{jF6d1d@ zx~#)&B$ahZ@8Ihg8^_kv2-kMNDZhAOXbX#qsx5oT>lzv|=!vzmrNf4XbjzXD*E7W( z$*Zvb@;`q3ctm>V0}bqqPR^H-gi*e;FO{d-bJGB}eQ_|N-Gy5aLCGd(;88SsZ!`GI zGpehr>%d@qCf-fPcW#CBpinBhx`|xX>5fBe&>F^|bF%2XM_@)FAt5@HH@%waR|m5@ zIy><*GBbBEi23Nb;lXT~0b;2YUq3(P>tEbf)KC27CFzKH4Qo2LehTNEonYs2@=iYI z+CgW@Z;%xgL?%_#D=RC-Wo0AvxU2EOXDchK;*t^}Y5!UIi-Q-ij%(iAbNAjR9v4;R$D|=9_Pph7Q$l0|Web^TdH9C7F!T40IO_!Ma>=#c0H1;A;iJ5G9dHJhdeYc~CtSq6juJvq+ z2yE+IfQKh!WIYyb?chMzZbo|Z=FQJ&48ifbTa%!;c=-Cd?d&dR^mS!j9YezsEVB1x zIaym!SU5O7p4cCEaqL>fq>{h@J5Y?H7BJ~tUS{3c+#DJj+MR5(u?#+3Ni+Sn;s!4% zA+R4W7xKp;oae*yI6oUz?D!&g-pKA?FKVX=2?@=tt#75KroMJ?5S6`!Y{mYOc_Yi9 z2Z7|5l=Qv}xWqUI#b9qp23^V;^HbA4eCWD5NFN&?pNB-!qvWv;-vavX*F2PPGV}GN z6%!MKe(|uKIfCn`vg*4XR+p7&%w%zMbCU;F8b?WaZcqZ5Lm<>%+jH4FNVnnqt|s?Y zr7ZDGBCzzZurL=%I)Zs@7quyC%i5KcfR@*`wmLgILmS^69vr-m7U(W+qMY~NCz^4R zu(P*!J=!v?)|P5N+p0xfg{z@KJoE{Zrj}M79Da=lQW@Kfs%pEWbwWJUDx%QW*Uu|@ zDC1>h(dx6$F3XVcd)`)%MoE-5tjqjumpNHg(E09n8U}{$QvEVP5s?rOdsqJNgW6RS zF)osXnFHZ1EiH}Mwji85ScIAyL8S?jyS2X()=V0uWL6Dz!Ay(oo>ja=Ty4+Mk=NLg z-}ao`$wgpFO3KI5Qtk!sl$+PDC9V~(Z*ObAY56nLbVFI!taeJ24a(@h^H|-vl7fO_ zG3x;xUFVMVR zewaThwM75V_wH^%S=sXD5P~1At?Lz&B~ot7cYv0weGl#Q^!20stTZ%Yn6MD{i!)Eo z=wLI!Mi=t(mk6iFx|C2}sxkb{?zo3Q7R9Bd!n_ObvK4|%&Q>v)`1`gK7)D=< zMwgfKHX8H0)=*JVZA7n{;?B|{IquIx#03TMly&j&^7Hd`C|9cO!i?uoC{%*qjd*>c z=H_M_Ht1f0;UE3}%e}O=PNeF^P6r9|gCvV29wD&r;0AIzlrd>}qpL@@Y{7>;-$x0_ z%U5R5UH{sf%+cjHQDrKS)N*+l7#kP&>U3{qveDII<~T>n{VCzAehI#`{;PtdsjY2c zX{mIMONft;&es4}7Izf~S0=xm zU)R8f+S-;#{z+8D_x>|OMquvb#9vrg_{&3jcR*r3GN6y3EF*%1k(u<)$IqWhsrijb zd$Y32U!P_($ootF@d2{G2_})Ao^FU)DKM4KT{$KuB8pn7{7n*cqBygCva_HF0-ljU zJGQ^Dk5bTn({Z_8l>2M{RL96jx{S8ssOc($BA3`WpnInuyRv1xl{{ZO+mf`S7yc4} zZBWfw&OEKQ_`b+M%||f4(x29K48%`SHUGs^!+(5zq=-l@>Gu}_R`N~brdg+Fn27N3 zMFY(ht@1ohR|=KW9G+yJ<16*QDEOU=J1=S+EKaO)Hp0Jt%{5?ib2FrW<%zqyh`)Sp zF@ujRL*uLyD5u6?Gdi<2@OctrH9yy_0m;7nwhI!|%-^4`qN1XfUynmOhf?h+FPG23 z$3A`h`g?UIS8?Gmu(7duKJVDNycf82U2Z~I)}Xq$qC)>ds4VJfKC{}zcZEwbL~tIY zd{qeYtNn|{3Fwc(_UY>da2J*y!D))e%r zVnI0UypWL4+wpM&<5qveiF#!%;n8BYal7TgRN5v*=H?(=YvXSJ^72cK`p(TmX7VUd zrOnOE{?hxoGM{rE1UAJq@YHH%(p=380)c!(1*OboUL4N{2_(siF$NqY7LWHV@Bg%` zj~-k|<$)}k@R+wftC-iQ>?v&mg@#f&T`cJShgqTbz1iI=2?4P)YLQ*ewNsr(#`Lze ze3SS*+2-y|ckkY{Mlx&$6#88bBjy8nw8}l$p$rKsgo$fx5Z(jd<5}<3i@9d+?dVRc z${YxHNw$Kp&)VBd?f#oEb`By)r7;;r1i}$bg+NY!j-=Wnwc+leS4}q-I|o8r%z^CD z35&TUB@!agaW0avGc(3x;?;2d`X(rzwSH{U?m`4(_cQj<-&r8i#(L1P(2iH&-n}g- zv?p6IkSW1&-#d0DQAb2hE)E=aZ;&Qq;yAErA36K7^!52t1M_?6Ez)OfJtXNqWMwH? zSg^e3d->kwwI367a%hCNde8R73kqZYG)Y57%=(XsL>3h)WAaBF`~d+0Z%%gLb~kN= z-mN~7vt2HD<>p4dw6p}uP$}ln;JH<=2TcUJ@P4^H#-T6u=n~iHI6pIUpXKrPyy?&V0JY3m?a$vG|VHf}EF+XG5Og`9-WU z09*qHS4i8uvlnN|<55P@_tw?-Jvlfy^wFJEsy{-Nm!5xWYhx_Z&h7S|53up#1d9;W ze(<7wn3;kqh6Mn!y5(awsCLHwD#`+EG!YSz8fJB4tjwi(*7##WWF+Ua4`x<^rweJl zCmUr|&{Ob;dVXwrh%y+c4l4Nn8QYcE;O;pfz3$Ob$}Fv-DL7#1M1l`ypT2@Dlq|`L zj9eq=W_kjC^*?wHCB~zZ_E06XB_bxysCfoPwM@}l7Wb8px&IHwui$+JVi}0P5kX8Z zEQ1doHDZ09h%R2?-u=&a{zd(XcJ;TOo8$2M`ul(`0IXU^D1s4fyYYE$iaSg`YQUbI zX~u>EyYS;H(-Sb%f2Q%TO8_glwYj$T_sF`EqN3vS*QVurPt2c%CLiV4$pg z6Icg8*?JMxH8nNEboytG({%(43Ae#1U>)%U%yK_Ph+tep0Lj|mJlA4t&iZ>}YfJQM z!Nc&6AMuoxl|lU#lcDT;^dQ-cZRzXV8z3=y`ujUn7y~T5Br{r07Gv(yS7#Ij2su~ME&$NyLRQlqNciW8}92-ll zQB4232beT-URP;N4LPt)V8;H;H~H>perEMM{&Rn6@p}Se0-9YWGr_qa43q6W_gIux ztS|le|d=8k(PnbDGeCFNKA-49e~UA5!Qq|A~=;R}BWso?>FP>WaP(t+6Bk zj=~Iq)_w6JBs?70wmuqQ$YKHq4PY>OurOUiL#fqZr1rNwWPz!c7O|U~8(>a7w5Wtc z$ASxQu}!A+x6{iRchmqDS=D;R{RB?x=I-vg+(ULFpBX9cG!qGM78oSfg;Ny1J=s)? zkGuLYZrZ+ziH^9#x)zQ=NPNn*O2MEJ7QhXFN!Es&Q*UNfApMS`G4>Oc=`?C*p5ERa z7_a<>1}dPWz&1tz?4_7A0n_-64ZG2aiA^oaFJHd=@@ii2kRHjfbeL%*1K%4P8!OFy z`0_wlKtTKWG6;*Fk9MqY`XJ-|hZ61R=m?4}P+C_6V;~LU;tI^=VDv}t{KXy{teNK|+C)2*2%3IJ?UQc{xsC!eR_12uqi2OQJQ zY<~_9Gy6K!0O+=~u_+4B`U;5t=g*(-j@p#cH?31ux5D*azAVGyxYL;`K53+F*yY~c zJIz0uT~Ts@>)m|rh?%Z`?dUj`)#>5w9fN6TXb8n19Hy8XfY&SFX*k@Rh{3q84zBxF zQSGtRE^qUj&Wv%Z8Brlql7}n@5sQn9p^jk7C>nqRB>Bhgy6i3Y`h4F%+pJ>w7ccNe zMn)1~2rHwUN9NT~u$c^*410V10w@_Ru}lf)?te^xUV?_MF4>kBHVao37Z=CL&z~bg zhA^rCCeGGSad%Y#C)Hvi(AU>z-L*fgA;3gKm1==rB0EaWd zQg8UEYFxtEEeND@aOiAOOuF{RKudV&%=*R~ugg(05w9SN0rFoV=JaeqI z`MsUG!(&)P$6FvP7{SO3Z8^3EaAJ2RlkgiizH^}w&0byt_NUrsTScC+PF7f>k`=hu z_SJoOU7g;DO>b|pQ7~sNiP2>);GtVp6EZd&uW*+aSLKSEJL3@lUj5+Wc*RTQZ}2dG zM_99$P%y3W3%ZXVx5)_J^{Mx(n%DVv_z+uP3^8O3jAI=6CFIs?)v zE-t>R^gcdKoT8TBJRju%vR)oWX6Px@RuvW977-E2fEeeNInZbjCmFh4+9+Vt8`D2! z_=^zZU&q^lZZC#Wn?z#g&dz*==v+XNY?M=Na{DTcwej1n0a!f+8Efnj#?>?p z!jMKmd1_vOKunB`B!7Mefuv2EG&rPB5{Er;b*|f!5Cxm9PyJWVWYBq7rnFVIIAXd6My`SLgkmo^kmqn+FMt_(F#c#oBl zQ;u`%DLZrG9bT&ba;5~3t-EQicHdtEQ?QueJPIls>a+=D_Z!gXSZY|`+M@if!JbQE zgUZpr*rGYj(#=OGmK#L*ucSgcr9>npi+Ko=lQpLHR{Fb+=8%5M%t?fD9cl?z(*-)$ zW5XXsDV|Cfi=&{9jpu1?=bt$5_P8D#9(K*5YEkk$4FV=DG>Eu44xXMiiF*6rV`UJq z(Xqhv8`3M(T3H6O&Hw!Z4w^|oY@m~L)sVV-?;fL{s*Q?@in_&54b0QjSRf}LM7j4T zz0_y=Wd`fhMY7u#|JwrUR|q_{EF;)5v&jsY2f- zLm0HX0e=upCHUtLfcD%xV0$^Ep8LZ~J7oS2x%2i6pv@6fhDpv@AmOju^tA>jfVG+wHC?ZG@TE7 ziZ?U2>L*M@{Bw<1eN#-IvO}NR+S&r|%w=b1_bxd(BrlH{G>$Af!ink6)HP1fx4+qn=Hrn6BwG}Q7T8?vL zi*f^vCR|b-&<&1XrWK}N#i`_2f7W>|g*)W$_~jz$;mK`j&3Ew#r2x1;`Xxrj<=5ZE z!u$qN3@d5(C7Ll9wL$%QcIGNy&!JdlE3rMm+1dscmIcplQ=x_>9xto U4}STefsLo8tff@>#4PxK0JKhI%>V!Z literal 5776 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 diff --git a/test/test.js b/test/render.js similarity index 64% rename from test/test.js rename to test/render.js index b25462a..2e38c82 100644 --- a/test/test.js +++ b/test/render.js @@ -2,5 +2,6 @@ var render = require('../lib/render') var fs = require('fs') render('Canon.mp3', function (err, buffer) { - fs.writeFileSync('out.png', buffer); + if (err) return err + fs.writeFileSync('out.png', buffer) })