Skip to content

Commit

Permalink
Merge pull request #1 from picasocro1/encode-after-record
Browse files Browse the repository at this point in the history
Encode after record
  • Loading branch information
HumbertoL authored Feb 28, 2023
2 parents e92d0aa + dc7aa88 commit a91256c
Show file tree
Hide file tree
Showing 6 changed files with 280 additions and 90 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
.DS_Store
node_modules

/.idea
157 changes: 118 additions & 39 deletions dist/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -15816,20 +15816,25 @@ var MicRecorder = function () {
// 128 or 160 kbit/s – mid-range bitrate quality
bitRate: 128,

deviceId: null,
// Encode to mp3 after finish recording
// Encoding during recording may result in distorted audio
// This could be crucial on mobile devices
encodeAfterRecord: true,
// There is a known issue with some macOS machines, where the recording
// will sometimes have a loud 'pop' or 'pop-click' sound. This flag
// prevents getting audio from the microphone a few milliseconds after
// the begining of the recording. It also helps to remove the mouse
// the beginning of the recording. It also helps to remove the mouse
// "click" sound from the output mp3 file.
startRecordingAt: 300,
deviceId: null
startRecordingAt: 300
};

this.activeStream = null;
this.context = null;
this.microphone = null;
this.processor = null;
this.startTime = 0;
this.rawChunksBuffer = null;

Object.assign(this.config, config);
}
Expand Down Expand Up @@ -15864,26 +15869,118 @@ var MicRecorder = function () {
return;
}

// Send microphone data to LAME for MP3 encoding while recording.
_this.lameEncoder.encode(event.inputBuffer.getChannelData(0));
var rawChunk = event.inputBuffer.getChannelData(0);

if (_this.config.encodeAfterRecord) {
// Save copy of raw chunk for future encoding
_this.rawChunksBuffer.push(Object.assign([], rawChunk));
} else {
// Send microphone data to LAME for MP3 encoding while recording.
_this.lameEncoder.encode(rawChunk);
}
};

// Begin retrieving microphone data.
this.microphone.connect(this.processor);
this.processor.connect(this.context.destination);
this.connectMicrophone();
}
}, {
key: 'stop',
key: 'initialize',


/**
* Requests access to the microphone and starts recording
* @return Promise
*/
value: function initialize() {
var _this2 = this;

var _config = this.config,
deviceId = _config.deviceId,
encodeAfterRecord = _config.encodeAfterRecord;

var AudioContext = window.AudioContext || window.webkitAudioContext;
this.context = new AudioContext();
this.config.sampleRate = this.context.sampleRate;
this.rawChunksBuffer = encodeAfterRecord ? [] : null;
this.lameEncoder = new Encoder(this.config);
this.i = 0;

var audio = deviceId ? { deviceId: { exact: deviceId } } : true;

return new Promise(function (resolve, reject) {
navigator.mediaDevices.getUserMedia({ audio: audio }).then(function (stream) {
_this2.addMicrophoneListener(stream);
resolve(stream);
}).catch(function (err) {
reject(err);
});
});
}
}, {
key: 'start',


/**
* Initializes or resumes recording
* @return Promise
*/
value: function start() {
if (!this.processor || !this.microphone) {
return this.initialize();
} else {
this.connectMicrophone();
return Promise.resolve();
}
}

/**
* Pause recording
* @return Promise
*/

}, {
key: 'pause',
value: function pause() {
this.disconnectMicrophone();
return Promise.resolve();
}
}, {
key: 'connectMicrophone',


/**
* Start retrieving microphone data
*/
value: function connectMicrophone() {
if (this.processor && this.microphone) {
this.microphone.connect(this.processor);
this.processor.connect(this.context.destination);
}
}

/**
* Stop retrieving microphone data
*/

}, {
key: 'disconnectMicrophone',
value: function disconnectMicrophone() {
if (this.processor && this.microphone) {
this.microphone.disconnect();
this.processor.disconnect();
}
}

/**
* Disconnect microphone, processor and remove activeStream
* @return MicRecorder
*/

}, {
key: 'stop',
value: function stop() {
if (this.processor && this.microphone) {
// Clean up the Web Audio API resources.
this.microphone.disconnect();
this.processor.disconnect();
this.disconnectMicrophone();

// If all references using this.context are destroyed, context is closed
// automatically. DOMException is fired when trying to close again
Expand All @@ -15897,48 +15994,30 @@ var MicRecorder = function () {
this.activeStream.getAudioTracks().forEach(function (track) {
return track.stop();
});
this.processor = null;
this.microphone = null;
}

return this;
}
}, {
key: 'start',


/**
* Requests access to the microphone and start recording
* @return Promise
*/
value: function start() {
var _this2 = this;

var AudioContext = window.AudioContext || window.webkitAudioContext;
this.context = new AudioContext();
this.config.sampleRate = this.context.sampleRate;
this.lameEncoder = new Encoder(this.config);

var audio = this.config.deviceId ? { deviceId: { exact: this.config.deviceId } } : true;

return new Promise(function (resolve, reject) {
navigator.mediaDevices.getUserMedia({ audio: audio }).then(function (stream) {
_this2.addMicrophoneListener(stream);
resolve(stream);
}).catch(function (err) {
reject(err);
});
});
}
}, {
key: 'getMp3',


/**
* Return Mp3 Buffer and Blob with type mp3
* @return {Promise}
* @return Promise
*/
value: function getMp3() {
var _this3 = this;

if (this.config.encodeAfterRecord) {
this.rawChunksBuffer.forEach(function (rawChunk) {
_this3.lameEncoder.encode(rawChunk);
});
this.rawChunksBuffer = null;
}

var finalBuffer = this.lameEncoder.finish();

return new Promise(function (resolve, reject) {
Expand Down
2 changes: 1 addition & 1 deletion dist/index.js.map

Large diffs are not rendered by default.

75 changes: 60 additions & 15 deletions samples/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,16 @@ <h1>Mic Recorder to Mp3 Example</h1>

<hr />

<button class="btn btn-primary">Start recording</button>
<div title="Warning: Disable this option can cause audio distortion">
<input id="encode-after-record" type="checkbox" checked>
Encode after record
</div>

<br />
<br />

<button id="start-pause" class="btn btn-primary"></button>
<button id="stop" class="btn btn-danger" disabled></button>

<br />
<br />
Expand All @@ -25,19 +34,52 @@ <h1>Mic Recorder to Mp3 Example</h1>
<!-- <script src="https://unpkg.com/mic-recorder-to-mp3"></script> -->
<script src="../dist/index.js"></script>
<script>
const button = document.querySelector('button');
const recorder = new MicRecorder({
bitRate: 128
});
const LABELS = {
START: 'Start recording',
PAUSE: 'Pause recording',
RESUME: 'Resume recording',
STOP: 'Stop recording'
};

const encodeAfterRecordCheck = document.getElementById('encode-after-record');
const startPauseButton = document.getElementById('start-pause');
const stopButton = document.getElementById('stop');
let recorder
initRecorder()

startPauseButton.textContent = LABELS.START;
stopButton.textContent = LABELS.STOP;

encodeAfterRecordCheck.addEventListener('change', initRecorder)
startPauseButton.addEventListener('click', startRecording);
stopButton.addEventListener('click', stopRecording);

button.addEventListener('click', startRecording);
function initRecorder() {
recorder = new MicRecorder({
bitRate: 128,
encodeAfterRecordCheck: encodeAfterRecordCheck.checked
});
}

function startRecording() {
recorder.start().then(() => {
button.textContent = 'Stop recording';
button.classList.toggle('btn-danger');
button.removeEventListener('click', startRecording);
button.addEventListener('click', stopRecording);
encodeAfterRecordCheck.disabled = true;
startPauseButton.textContent = LABELS.PAUSE;
startPauseButton.classList.add('btn-info');
stopButton.disabled = false;
startPauseButton.removeEventListener('click', startRecording);
startPauseButton.addEventListener('click', pauseRecording);
}).catch((e) => {
console.error(e);
});
}

function pauseRecording() {
recorder.pause().then(() => {
startPauseButton.textContent = LABELS.RESUME;
startPauseButton.classList.remove('btn-info');
startPauseButton.removeEventListener('click', pauseRecording);
startPauseButton.addEventListener('click', startRecording);
}).catch((e) => {
console.error(e);
});
Expand All @@ -46,6 +88,8 @@ <h1>Mic Recorder to Mp3 Example</h1>
function stopRecording() {
recorder.stop().getMp3().then(([buffer, blob]) => {
console.log(buffer, blob);
encodeAfterRecordCheck.disabled = false;
stopButton.disabled = true;
const file = new File(buffer, 'music.mp3', {
type: blob.type,
lastModified: Date.now()
Expand All @@ -57,14 +101,15 @@ <h1>Mic Recorder to Mp3 Example</h1>
li.appendChild(player);
document.querySelector('#playlist').appendChild(li);

button.textContent = 'Start recording';
button.classList.toggle('btn-danger');
button.removeEventListener('click', stopRecording);
button.addEventListener('click', startRecording);
startPauseButton.textContent = LABELS.START;
startPauseButton.classList.remove('btn-info');
startPauseButton.removeEventListener('click', startRecording);
startPauseButton.removeEventListener('click', pauseRecording);
startPauseButton.addEventListener('click', startRecording);
}).catch((e) => {
console.error(e);
});
}
</script>
</body>
</html>
</html>
2 changes: 1 addition & 1 deletion src/encoder.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,6 @@ class Encoder {

return this.dataBuffer;
}
};
}

export default Encoder;
Loading

0 comments on commit a91256c

Please sign in to comment.