mirror of
https://github.com/Tonejs/Tone.js
synced 2024-11-15 08:17:07 +00:00
component to record anything from it's input
This commit is contained in:
parent
0052540795
commit
5aba8dd6de
2 changed files with 163 additions and 0 deletions
152
Tone/component/Recorder.js
Normal file
152
Tone/component/Recorder.js
Normal file
|
@ -0,0 +1,152 @@
|
|||
define(["Tone/core/Tone", "Tone/core/Master"], function(Tone){
|
||||
|
||||
/**
|
||||
* Record an input into an array or AudioBuffer
|
||||
*
|
||||
* it is limited in that the recording length needs to be known beforehand
|
||||
*
|
||||
* @constructor
|
||||
* @extends {Tone}
|
||||
* @param {number} channels
|
||||
*/
|
||||
Tone.Recorder = function(channels){
|
||||
|
||||
Tone.call(this);
|
||||
|
||||
/**
|
||||
* the number of channels in the recording
|
||||
* @type {number}
|
||||
*/
|
||||
this.channels = this.defaultArg(channels, 1);
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {ScriptProcessorNode}
|
||||
*/
|
||||
this._jsNode = this.context.createScriptProcessor(this.bufferSize, this.channels, 1);
|
||||
this._jsNode.onaudioprocess = this._audioprocess.bind(this);
|
||||
|
||||
/**
|
||||
* Float32Array for each channel
|
||||
* @private
|
||||
* @type {Array<Float32Array>}
|
||||
*/
|
||||
this._recordBuffers = new Array(this.channels);
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {number}
|
||||
*/
|
||||
this._recordBufferOffset = 0;
|
||||
|
||||
//connect it up
|
||||
this.input.connect(this._jsNode);
|
||||
//pass thru audio
|
||||
this.input.connect(this.output);
|
||||
//so it doesn't get garbage collected
|
||||
this._jsNode.toMaster();
|
||||
//clear it to start
|
||||
this.clear();
|
||||
};
|
||||
|
||||
Tone.extend(Tone.Recorder);
|
||||
|
||||
/**
|
||||
* internal method called on audio process
|
||||
*
|
||||
* @private
|
||||
* @param {AudioProcessorEvent} event
|
||||
*/
|
||||
Tone.Recorder.prototype._audioprocess = function(event){
|
||||
if (this._recordBuffers[0] === null || this._recordBuffers[0].length - this._recordBufferOffset === 0){
|
||||
return;
|
||||
}
|
||||
var input = event.inputBuffer;
|
||||
var totalWrittenToBuffer = 0;
|
||||
var recordBufferLength = this._recordBuffers[0].length;
|
||||
for (var channelNum = 0; channelNum < input.numberOfChannels; channelNum++){
|
||||
var bufferOffset = this._recordBufferOffset;
|
||||
var channel = input.getChannelData(channelNum);
|
||||
var bufferLen = channel.length;
|
||||
if (recordBufferLength - bufferOffset > bufferLen){
|
||||
this._recordBuffers[channelNum].set(channel, bufferOffset);
|
||||
totalWrittenToBuffer += bufferLen;
|
||||
} else {
|
||||
for (var i = 0; i < bufferLen; i++) {
|
||||
if (recordBufferLength > bufferOffset){
|
||||
this._recordBuffers[channelNum][bufferOffset] = channel[i];
|
||||
bufferOffset++;
|
||||
totalWrittenToBuffer++;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
this._recordBufferOffset += totalWrittenToBuffer / input.numberOfChannels;
|
||||
};
|
||||
|
||||
/**
|
||||
* Record for a certain period of time
|
||||
*
|
||||
* will clear the internal buffer before starting
|
||||
*
|
||||
* @param {Tone.Time} time
|
||||
*/
|
||||
Tone.Recorder.prototype.record = function(time){
|
||||
this.clear();
|
||||
var recordBufferLength = this.toSamples(time);
|
||||
for (var i = 0; i < this.channels; i++){
|
||||
this._recordBuffers[i] = new Float32Array(recordBufferLength);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* clears the recording buffer
|
||||
*/
|
||||
Tone.Recorder.prototype.clear = function(){
|
||||
for (var i = 0; i < this.channels; i++){
|
||||
this._recordBuffers[i] = null;
|
||||
}
|
||||
this._recordBufferOffset = 0;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* true if there is nothing in the buffers
|
||||
* @return {boolean}
|
||||
*/
|
||||
Tone.Recorder.prototype.isEmpty = function(){
|
||||
return this._recordBuffers[0] === null;
|
||||
};
|
||||
|
||||
/**
|
||||
* @return {Array<Float32Array>}
|
||||
*/
|
||||
Tone.Recorder.prototype.getFloat32Array = function(){
|
||||
if (this.isEmpty()){
|
||||
return null;
|
||||
} else {
|
||||
return this._recordBuffers;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @return {AudioBuffer}
|
||||
*/
|
||||
Tone.Recorder.prototype.getAudioBuffer = function(){
|
||||
if (this.isEmpty()){
|
||||
return null;
|
||||
} else {
|
||||
var audioBuffer = this.context.createBuffer(this.channels, this._recordBuffers[0].length, this.context.sampleRate);
|
||||
for (var channelNum = 0; channelNum < audioBuffer.numberOfChannels; channelNum++){
|
||||
var channel = audioBuffer.getChannelData(channelNum);
|
||||
channel.set(this._recordBuffers[channelNum]);
|
||||
}
|
||||
return audioBuffer;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
return Tone.Recorder;
|
||||
});
|
|
@ -279,6 +279,17 @@
|
|||
return samples / audioContext.sampleRate;
|
||||
};
|
||||
|
||||
/**
|
||||
* convert a time into samples
|
||||
*
|
||||
* @param {Tone.time} time
|
||||
* @return {number}
|
||||
*/
|
||||
Tone.prototype.toSamples = function(time){
|
||||
var seconds = this.toSeconds(time);
|
||||
return seconds * audioContext.sampleRate;
|
||||
};
|
||||
|
||||
/**
|
||||
* convert Tone.Time to seconds
|
||||
*
|
||||
|
|
Loading…
Reference in a new issue