I think versions will be OK.
Some people need just simple oscilloscope without any memory but performance onlyThe simplest version will be one function to draw audio input on screen:
http://uplight.ca/htms/Ocilloscope-Javascript.htm
Who are interested in code right click on page go to source and in js folder find js/UplOscilloscope1.js
I did inserted it on the bottom but without code formatting don't think it useful.
There are 2 classes (objects) in code Oscilloscope1 controlling user interactions and OcsDisplay drawing buffer on screen.
2 library used JQuery as interface to DOM and createjs(easeljs) as interface to canvas they are not critical to code and can be replaced with any other or use direct access.
According documentation of API createScriptProcessor :
bufferSize must be one of the following values: 256, 512, 1024, 2048, 4096, 8192, 16384.
This value controls how frequently the
audioprocess event is dispatched and how many sample-frames need to be processed each call. Lower values for bufferSize will result in a lower (better) latency. Higher values will be necessary to avoid audio breakup and glitches. It is recommended for authors to not specify this buffer size and allow the implementation to pick a good buffer size to balance between latency and audio quality.Main function is onAudioProcess what taking array from javascript node and draw it on screen it is triggered by
ScriptProcessorNode.audioprocess.
the data is Uint8Array pulled to javascript with analyser.getByteTimeDomainData(data) function holds in array of uint where values could be form 0 to 256.The nice surprise that .getByteTimeDomainData function forming data ready to visualization: 128 value is equal zero signal, 0 is +max 256 is -max;
all what you need go through array and draw each point on screen:
for (var i = 1; i < n; i++) g.lineTo(i , data[i]);
the problem what I'm facing is
when buffer 4096 and above only 2048 values in array are valid and all others are 0:
On first image oscillogramme with buffer size 4096 and next image 8192. How you can see after 2048 values its jumping to 0;
Javascript Oscilloscope source code:
var upl;
(function (upl) {
var c = createjs;
var Oscilloscope1 = (function () {
// private oscAnalyser:OscRecorder
function Oscilloscope1(prefix) {
var _this = this;
this.prefix = prefix;
this.bufferSize = 1024;
var view = $('#' + prefix + '-oscilloscope');
this.stage = new c.Stage(document.getElementById(prefix + '-screen1'));
this.select = view.find('[data-id=input_select]:first').on('change', null, function (evt) {
return _this.onInputChanged(evt);
});
var canvas = $('#' + prefix + '-screen1');
this.lineColor = canvas.data('color') || '#000000';
//this.width = canvas.width();
var audioContext = window.AudioContext || window.webkitAudioContext;
this.audioContext = new audioContext();
navigator.getUserMedia = (navigator.getUserMedia || navigator.webkitGetUserMedia);
if (navigator.getUserMedia)
setTimeout(function () {
return _this.getInputsList();
}, 200);
else
alert('browser doest support getUserMedia');
this.speed = document.getElementById(prefix + '-speed');
this.hrange = view.find('[data-id=h-range]').on('change', null, function (evt) {
return _this.onRangeChanged(evt);
}).on('input', null, function (evt) {
return _this.onRangeInput(evt);
});
;
this.h_indicator = view.find('[data-id=h_indicator]');
this.onRangeInput(null);
this.onRangeChanged(null);
}
Oscilloscope1.prototype.onRangeChanged = function (evt) {
var val = Number(this.hrange.val());
if (isNaN(val))
return;
val = Math.pow(2, val);
this.bufferSize = val;
if (this.display)
this.display.makeBuffer(val);
};
Oscilloscope1.prototype.onRangeInput = function (evt) {
var val = Number(this.hrange.val());
if (isNaN(val))
return;
val = Math.pow(2, val);
this.h_indicator.text(val);
};
Oscilloscope1.prototype.getInputsList = function () {
var _this = this;
MediaStreamTrack.getSources(function (info) {
return _this.gotSources(info);
});
};
Oscilloscope1.prototype.gotSources = function (souces) {
// console.log(souces);
var str = '';
for (var i = 0, n = souces.length; i < n; i++)
if (souces[i].kind == 'audio')
str += '<option value="' + souces[i].id + '">' + (souces[i].label || 'Input ' + i) + '</option>';
this.select.html(str);
this.getUserMedia({ audio: true, video: false });
};
Oscilloscope1.prototype.getUserMedia = function (constraints) {
var _this = this;
navigator.getUserMedia(constraints, function (stream) {
return _this.onConnect(stream);
}, function (e) {
alert('Error getting audio');
console.log(e);
});
};
Oscilloscope1.prototype.onInputChanged = function (evt) {
var select = evt.currentTarget;
var constraints = {
audio: {
optional: [{ sourceId: select.value }]
},
video: false
};
console.log('getting source ' + select.value);
this.getUserMedia(constraints);
};
Oscilloscope1.prototype.onConnect = function (stream) {
// this.mediaStriam = stream;
if (this.display)
this.display.destroy();
this.stage.removeAllChildren();
this.display = new OscDisplay(this.audioContext, stream, this.stage, this.speed, this.lineColor);
this.display.makeBuffer(this.bufferSize).connect();
};
return Oscilloscope1;
})();
upl.Oscilloscope1 = Oscilloscope1;
////////////////////////////-----------------------------------------------////////////////////////////////////////////////////
var OscDisplay = (function () {
function OscDisplay(context, stream, stage, speed, color) {
this.count = 0;
this.startn = 0;
this.speed = speed;
this.stage = stage;
this.color = color;
this.context = context;
this.stream = stream;
var sh = new c.Shape();
this.screen = sh;
stage.addChild(sh);
this.graphics = sh.graphics;
this.create();
}
OscDisplay.prototype.create = function () {
this.streamSource = this.context.createMediaStreamSource(this.stream);
this.analyser = this.context.createAnalyser();
};
OscDisplay.prototype.destroy = function () {
this.streamSource = null;
this.jsProcessor.onaudioprocess = null;
this.jsProcessor = null;
this.analyser = null;
this.isConnected = false;
};
OscDisplay.prototype.disconnect = function () {
this.analyser.disconnect();
this.streamSource.disconnect();
this.jsProcessor.disconnect();
this.jsProcessor.onaudioprocess = null;
this.isConnected = false;
return this;
};
OscDisplay.prototype.connect = function () {
var _this = this;
console.log('Connectiong with data length: ' + this.data.length);
this.isConnected = true;
this.jsProcessor.connect(this.analyser);
this.streamSource.connect(this.analyser);
this.startn = Date.now();
this.count = 0;
this.jsProcessor.onaudioprocess = function (evt) {
return _this.onAudioProcess(evt);
};
return this;
};
OscDisplay.prototype.makeBuffer = function (buffer) {
var connected = this.isConnected;
if (connected)
this.disconnect();
this.data = new Uint8Array(buffer);
this.jsProcessor = this.context.createScriptProcessor(buffer, 1, 1);
if (connected)
this.connect();
return this;
};
OscDisplay.prototype.onAudioProcess = function (evt) {
var data = this.data;
this.analyser.getByteTimeDomainData(data);
var g = this.graphics;
g.clear();
g.setStrokeStyle(1);
g.beginStroke(this.color);
g.moveTo(0, data[0]);
var n = data.length;
var dev = 1024 / n;
for (var i = 1; i < n; i++)
g.lineTo(i * dev, data[i]);
this.stage.update();
if (++this.count == 9) {
var end = Date.now();
this.count = 0;
this.speed.textContent = ((end - this.startn) / 10).toString();
this.startn = end;
}
};
return OscDisplay;
})();
upl.OscDisplay = OscDisplay;
})(upl || (upl = {}));


No comments:
Post a Comment