├── test.html ├── test.js ├── package.json ├── .gitignore ├── index.js └── readme.md /test.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | var Render = require('./'); 2 | var Generator = require('audio-generator'); 3 | var Sink = require('stream-sink'); 4 | var Canvas = require('drawille-canvas'); 5 | var isBrowser = require('is-browser'); 6 | 7 | var canvas = new Canvas(120, 80); 8 | 9 | Generator().pipe(Render({ 10 | channel: 0, 11 | canvas: canvas, 12 | render: function (canvas) { 13 | var data = this.getTimeData(); 14 | 15 | var ctx = canvas.getContext('2d'); 16 | 17 | ctx.clearRect(0,0,canvas.width, canvas.height); 18 | 19 | if (!data.length) return; 20 | 21 | //get average magnitude 22 | var w = 5; 23 | var magnitude = 0; 24 | for (var i = 1; i <= w; i++) { 25 | magnitude += data[data.length - i]; 26 | } 27 | 28 | magnitude /= w; 29 | magnitude = magnitude / 2 + 0.5; 30 | 31 | ctx.fillRect(0,0, magnitude * canvas.width, 20); 32 | ctx.fillText('Magnitude: ' + magnitude, 0, 40); 33 | 34 | } 35 | })) 36 | .on('render', function (canvas) { 37 | if (!isBrowser) { 38 | process.stdout.write(canvas._canvas.frame()); 39 | } 40 | }) 41 | .pipe(Sink()); 42 | 43 | if (isBrowser) { 44 | document.documentElement.appendChild(canvas); 45 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "audio-render", 3 | "version": "2.0.1", 4 | "description": "Class for rendering audio stream data for frequency/time domains. Browser/node.", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "node test", 8 | "watch": "watchify ./test.js -d -o ./bundle.js" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/audio-lab/render" 13 | }, 14 | "keywords": [ 15 | "audio", 16 | "pcm", 17 | "stream", 18 | "render", 19 | "canvas", 20 | "drawille", 21 | "sound", 22 | "spectrum", 23 | "spectrogram", 24 | "waveform", 25 | "frequency", 26 | "fft", 27 | "stft" 28 | ], 29 | "author": "Deema Yvanow ", 30 | "license": "MIT", 31 | "bugs": { 32 | "url": "https://github.com/audio-lab/render/issues" 33 | }, 34 | "homepage": "https://github.com/audio-lab/render", 35 | "dependencies": { 36 | "audio-analyser": "^1.0.1", 37 | "drawille-canvas": "^1.0.0", 38 | "inherits": "^2.0.1", 39 | "is-browser": "^2.0.1" 40 | }, 41 | "devDependencies": { 42 | "audio-generator": "^1.0.5", 43 | "stream-sink": "^1.2.0", 44 | "watchify": "^3.6.0" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | //this will affect all the git repos 2 | git config --global core.excludesfile ~/.gitignore 3 | 4 | 5 | //update files since .ignore won't if already tracked 6 | git rm --cached 7 | 8 | # Compiled source # 9 | ################### 10 | *.com 11 | *.class 12 | *.dll 13 | *.exe 14 | *.o 15 | *.so 16 | 17 | # Packages # 18 | ############ 19 | # it's better to unpack these files and commit the raw source 20 | # git has its own built in compression methods 21 | *.7z 22 | *.dmg 23 | *.gz 24 | *.iso 25 | *.jar 26 | *.rar 27 | *.tar 28 | *.zip 29 | 30 | # Logs and databases # 31 | ###################### 32 | *.log 33 | *.sql 34 | *.sqlite 35 | 36 | # OS generated files # 37 | ###################### 38 | .DS_Store 39 | .DS_Store? 40 | ._* 41 | .Spotlight-V100 42 | .Trashes 43 | # Icon? 44 | ehthumbs.db 45 | Thumbs.db 46 | .cache 47 | .project 48 | .settings 49 | .tmproj 50 | *.esproj 51 | nbproject 52 | 53 | # Numerous always-ignore extensions # 54 | ##################################### 55 | *.diff 56 | *.err 57 | *.orig 58 | *.rej 59 | *.swn 60 | *.swo 61 | *.swp 62 | *.vi 63 | *~ 64 | *.sass-cache 65 | *.grunt 66 | *.tmp 67 | 68 | # Dreamweaver added files # 69 | ########################### 70 | _notes 71 | dwsync.xml 72 | 73 | # Komodo # 74 | ########################### 75 | *.komodoproject 76 | .komodotools 77 | 78 | # Node # 79 | ##################### 80 | node_modules 81 | 82 | # Bower # 83 | ##################### 84 | bower_components 85 | 86 | # Folders to ignore # 87 | ##################### 88 | .hg 89 | .svn 90 | .CVS 91 | intermediate 92 | publish 93 | .idea 94 | .graphics 95 | _test 96 | _archive 97 | uploads 98 | tmp 99 | 100 | # Vim files to ignore # 101 | ####################### 102 | .VimballRecord 103 | .netrwhist 104 | 105 | bundle.* 106 | 107 | _demo -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Rendering stream. 3 | * 4 | * It does not transform audio to text 5 | * because rendering may require repetition of the same data multiple times. 6 | * It just plots captured amount of data to a canvas in user-defined way. 7 | * 8 | * Separation by node/browser files is also unnecessary, 9 | * we can detect environment in-place without serious size overdraft. 10 | * All the required modules provide short browser versions. 11 | * 12 | * Any routines which may vary, like sliding window behaviour, 13 | * realtime data binding, etc are left to user. 14 | * 15 | * It is a transform stream with exception where there are no piped outputs, 16 | * it releases data, i. e. behaves like a writable. 17 | * 18 | * @module audio-render 19 | */ 20 | 21 | 22 | var inherits = require('inherits'); 23 | var Analyser = require('audio-analyser'); 24 | var isBrowser = require('is-browser'); 25 | var Canvas = require('drawille-canvas'); 26 | 27 | 28 | //detect rendering scheduler 29 | var raf, cancel; 30 | if (isBrowser && (raf = requestAnimationFrame)) { 31 | cancel = cancelAnimationFrame; 32 | } 33 | else { 34 | raf = setTimeout; 35 | cancel = clearTimeout; 36 | } 37 | 38 | 39 | /** 40 | * @constructor 41 | */ 42 | function RenderStream (options) { 43 | if (!(this instanceof RenderStream)) return new RenderStream(options); 44 | 45 | var self = this; 46 | 47 | if (options instanceof Function) { 48 | options = { 49 | render: options 50 | }; 51 | } 52 | 53 | Analyser.call(self, options); 54 | 55 | 56 | //ensure canvas 57 | if (!self.canvas) { 58 | self.canvas = new Canvas(); 59 | } 60 | 61 | //set throttling 62 | self.throttle = 1000 / self.framesPerSecond; 63 | 64 | 65 | //plan rendering 66 | self._id = raf(update, self.throttle); 67 | 68 | //stop on end 69 | self.on('end', function () { 70 | cancel(self._id); 71 | }); 72 | 73 | function update () { 74 | self._id = raf(update, self.throttle); 75 | 76 | try { 77 | self.render(self.canvas, self._data); 78 | } catch (e) { 79 | throw e; 80 | } 81 | 82 | try { 83 | self.emit('render', self.canvas); 84 | } catch (e) { 85 | throw e; 86 | } 87 | } 88 | } 89 | 90 | 91 | /** It should be duplex not to block pipe if there is no output sink */ 92 | inherits(RenderStream, Analyser); 93 | 94 | 95 | 96 | /** How often to update */ 97 | RenderStream.prototype.framesPerSecond = 20; 98 | 99 | 100 | /** 101 | * Default rendering method, does nothing 102 | * @override 103 | */ 104 | RenderStream.prototype.render = function (canvas, data) { 105 | 106 | }; 107 | 108 | 109 | module.exports = RenderStream; -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | _Audio-render_ is a pass-through audio stream, providing structure for rendering stream audio data. 2 | 3 | It resolves common routines like frequency analysis (fft), buffering data, reading pcm format, providing unified API for rendering both in node/browser, events, options, hooks etc. Creating new rendering components based on _audio-render_ is as simple as creating them from scratch, but times more reliable. It is also useful for creating quick debuggers. 4 | 5 | 6 | ## Usage 7 | 8 | [![$ npm install audio-render](http://nodei.co/npm/audio-render.png?mini=true)](http://npmjs.org/package/audio-render) 9 | 10 | ```js 11 | myAudioStream 12 | .pipe(Render(function (canvas) { 13 | var data = this.getFloatTimeDomainData(); 14 | 15 | //draw volume, spectrum, spectrogram, waveform — any data you need 16 | })) 17 | .pipe(Speaker()); 18 | ``` 19 | 20 | ## API 21 | 22 | ```js 23 | var Generator = require('audio-generator'); 24 | var Speaker = require('audio-speaker'); 25 | var RenderStream = require('audio-render'); 26 | var isBrowser = require('is-browser'); 27 | 28 | 29 | //create rendering stream from passed options 30 | var renderer = RenderStream({ 31 | //custom rendering function, can be passed instead of options 32 | render: function (canvas) { 33 | //see audio-analyser for API 34 | var fdata = this.getFrequencyData(); 35 | var waveform = this.getTimeData(size); 36 | 37 | //or use web-audio-api AnalyserNode methods here 38 | this.getFloatFrequencyData(new Float32Array(self.frequencyBinCount)); 39 | this.getFloatTimeDomainData(new Float32Array(self.fftSize)); 40 | }, 41 | 42 | //channel number to render, 0 - L, 1 - R, ... 43 | channel: 0, 44 | 45 | //FPS (node only) 46 | framesPerSecond: 20, 47 | 48 | //max amount of data to store, number of samples 49 | bufferSize: 44100, 50 | 51 | //custom canvas (optinal), if you need to render along with other renderer 52 | canvas: undefined, 53 | 54 | //Analysis options 55 | 56 | //Magnitude diapasone, in dB 57 | minDecibels: -100, 58 | maxDecibels: 0, 59 | 60 | // Number of points to grab for fft 61 | fftSize: 1024, 62 | 63 | // Number of points to plot for fft 64 | frequencyBinCount: 1024/2, 65 | 66 | // Smoothing, or the priority of the old data over the new data 67 | smoothingTimeConstant: 0.2 68 | 69 | //...any pcm format options, if required. See pcm-util below. 70 | }); 71 | 72 | 73 | //Depending on the enviromnent, expose canvas 74 | isBrowser && document.body.appendChild(renderer.canvas); 75 | 76 | renderer.on('render', function (canvas, data) { 77 | process.stdout.write(canvas._canvas.frame()); 78 | }); 79 | 80 | 81 | //If renderer is not piped, it works as a sink, else - as pass-through 82 | Generator().pipe(renderer).pipe(Speaker()); 83 | ``` 84 | 85 | ## Related 86 | 87 | > [audio-analyser](https://npmjs.org/package/audio-analyser) — audio analyser stream.
88 | > [audio-spectrum](https://npmjs.org/package/audio-spectrum) — render audio spectrum.
89 | > [audio-spectrogram](https://npmjs.org/package/audio-spectrogram) — render audio spectrogram.
90 | > [audio-waveform](https://npmjs.org/package/audio-waveform) — render audio waveform.
91 | > [audio-stat](https://npmjs.org/package/audio-stat) — render any kind of audio info: waveform, spectrogram etc.
92 | > [audio-spiral](https://npmjs.org/package/audio-spiral) — render spiral spectrogram, based on audio-render.
93 | > [drawille-canvas](https://github.com/madbence/node-drawille-canvas) — node/browser canvas class.
94 | > [pcm-util](https://npmjs.org/package/pcm-util) — utils for work with pcm-streams.
--------------------------------------------------------------------------------