Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

vmsg

Package Overview
Dependencies
Maintainers
1
Versions
13
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

vmsg - npm Package Compare versions

Comparing version 0.1.2 to 0.2.0

6

package.json
{
"name": "vmsg",
"version": "0.1.2",
"version": "0.2.0",
"description": "Library for creating voice messages",

@@ -32,6 +32,6 @@ "main": "vmsg.js",

"devDependencies": {
"autoprefixer": "^8.0.0",
"babel-plugin-transform-class-properties": "^6.24.1",
"babel-plugin-transform-object-rest-spread": "^6.26.0",
"babel-preset-react": "^6.24.1",
"parcel-bundler": "^1.6.0",
"parcel-bundler": "^1.6.2",
"parcel-plugin-disable-loaders": "^1.0.1",

@@ -38,0 +38,0 @@ "react": "^16.2.0",

@@ -8,6 +8,6 @@ # vmsg [![npm](https://img.shields.io/npm/v/vmsg.svg)](https://www.npmjs.com/package/vmsg)

standard program, upload to file hosting and share the link. But why
bother with all of that boredom stuff if you can do the same in browser
bother with all of that tedious stuff if you can do the same in browser
with a few clicks.
:fireworks: :tada: **[DEMO](https://kagami.github.io/vmsg/)** :tada: :fireworks:
:confetti_ball: :tada: **[DEMO](https://kagami.github.io/vmsg/)** :tada: :confetti_ball:

@@ -23,10 +23,7 @@ ## Features

* Chrome 57+
* Firefox 53+
* Chrome 32+
* Firefox 27+
* Safari 11+
* Edge 16+
* Edge 12+
Note that this haven't been extensively tested yet. Feel free to open
issue in case of any errors.
## Usage

@@ -42,8 +39,8 @@

someButton.onclick = function() {
record(/* {wasmURL: "/path/to/vmsg.wasm"} */).then(file => {
console.log("Recorded MP3", file);
record(/* {wasmURL: "/static/js/vmsg.wasm"} */).then(blob => {
console.log("Recorded MP3", blob);
// Can be used like this:
//
// const form = new FormData();
// form.append("file[]", file);
// form.append("file[]", blob, "record.mp3");
// fetch("/upload.php", {

@@ -60,5 +57,7 @@ // credentials: "include",

That's it! Don't forget to include [vmsg.css](vmsg.css) and
[vmsg.wasm](vmsg.wasm) in your project.
[vmsg.wasm](vmsg.wasm) in your project. For browsers without WebAssembly
support you need to also include
[wasm-polyfill.js](https://github.com/Kagami/wasm-polyfill.js).
See also [demo](demo) directory for a more feasible example.
See [demo](demo) directory for a more feasible example.

@@ -109,2 +108,8 @@ ## Development

## But you can use e.g. ogv.js polyfill!
* It make things more complicated, now you need both encoder and decoder
* Opus gives you ~2x bitrate win but for 500kb per minute files it's not that much
* MP3 is much more widespread, so even while compression is not best compatibility matters
## License

@@ -111,0 +116,0 @@

@@ -16,7 +16,17 @@ function pad2(n) {

function fetchAndInstantiate(url, imports) {
const req = fetch(url, {credentials: "same-origin"});
return WebAssembly.instantiateStreaming
? WebAssembly.instantiateStreaming(req, imports)
: req.then(res => res.arrayBuffer())
.then(buf => WebAssembly.instantiate(buf, imports));
if (WebAssembly.instantiateStreaming) {
const req = fetch(url, {credentials: "same-origin"});
return WebAssembly.instantiateStreaming(req, imports);
} else {
return new Promise((resolve, reject) => {
const req = new XMLHttpRequest();
req.open("GET", url);
req.responseType = "arraybuffer";
req.onload = () => {
resolve(WebAssembly.instantiate(req.response, imports));
};
req.onerror = reject;
req.send();
});
}
}

@@ -28,6 +38,3 @@

const WASM_PAGE_SIZE = 64 * 1024;
const memory = new WebAssembly.Memory({
initial: TOTAL_MEMORY / WASM_PAGE_SIZE,
maximum: TOTAL_MEMORY / WASM_PAGE_SIZE,
});
let memory = null;
let dynamicTop = TOTAL_STACK;

@@ -41,3 +48,3 @@ // TODO(Kagami): Grow memory?

// TODO(Kagami): LAME calls exit(-1) on internal error. Would be nice
// to provide custom DEBUGF/ERRORF for easier debugging. By the moment
// to provide custom DEBUGF/ERRORF for easier debugging. Currenty
// those functions do nothing.

@@ -47,14 +54,2 @@ function exit(status) {

}
const Runtime = {
memory: memory,
pow: Math.pow,
exit: exit,
powf: Math.pow,
exp: Math.exp,
sqrtf: Math.sqrt,
cos: Math.cos,
log: Math.log,
sin: Math.sin,
sbrk: sbrk,
};

@@ -80,7 +75,7 @@ let FFI = null;

const mp3 = new Uint8Array(memory.buffer, mp3_ref, size);
const file = new File([mp3], "rec.mp3", {type: "audio/mpeg"});
const blob = new Blob([mp3], {type: "audio/mpeg"});
FFI.vmsg_free(ref);
ref = null;
pcm_l = null;
return file;
return blob;
}

@@ -92,6 +87,29 @@

case "init":
fetchAndInstantiate(msg.data, {env: Runtime}).then(wasm => {
const { wasmURL, shimURL } = msg.data;
Promise.resolve().then(() => {
if (!self.WebAssembly) {
importScripts(shimURL);
}
memory = new WebAssembly.Memory({
initial: TOTAL_MEMORY / WASM_PAGE_SIZE,
maximum: TOTAL_MEMORY / WASM_PAGE_SIZE,
});
return {
memory: memory,
pow: Math.pow,
exit: exit,
powf: Math.pow,
exp: Math.exp,
sqrtf: Math.sqrt,
cos: Math.cos,
log: Math.log,
sin: Math.sin,
sbrk: sbrk,
};
}).then(Runtime => {
return fetchAndInstantiate(wasmURL, {env: Runtime})
}).then(wasm => {
FFI = wasm.instance.exports;
postMessage({type: "init", data: null});
}, err => {
}).catch(err => {
postMessage({type: "init-error", data: err.toString()});

@@ -107,5 +125,5 @@ });

case "stop":
const file = vmsg_flush();
if (!file) return postMessage({type: "error", data: "vmsg_flush"});
postMessage({type: "stop", data: file});
const blob = vmsg_flush();
if (!blob) return postMessage({type: "error", data: "vmsg_flush"});
postMessage({type: "stop", data: blob});
break;

@@ -120,3 +138,4 @@ }

// https://stackoverflow.com/a/22582695
this.wasmURL = new URL(opts.wasmURL || "vmsg.wasm", location).href;
this.wasmURL = new URL(opts.wasmURL || "/static/js/vmsg.wasm", location).href;
this.shimURL = new URL(opts.shimURL || "/static/js/wasm-polyfill.js", location).href;
this.pitch = opts.pitch || 0;

@@ -138,4 +157,4 @@ this.resolve = resolve;

this.workerURL = null;
this.file = null;
this.fileURL = null;
this.blob = null;
this.blobURL = null;
this.tid = 0;

@@ -194,3 +213,3 @@ this.start = 0;

stopBtn.style.display = "none";
stopBtn.textContent = "◼";
stopBtn.textContent = "■";
stopBtn.addEventListener("click", () => this.stopRecording());

@@ -206,4 +225,4 @@ recordRow.appendChild(stopBtn);

if (audio.paused) {
if (this.fileURL) {
audio.src = this.fileURL;
if (this.blobURL) {
audio.src = this.blobURL;
}

@@ -221,3 +240,3 @@ } else {

saveBtn.disabled = true;
saveBtn.addEventListener("click", () => this.close(this.file));
saveBtn.addEventListener("click", () => this.close(this.blob));
recordRow.appendChild(saveBtn);

@@ -278,3 +297,3 @@

}
close(file) {
close(blob) {
if (this.audio) this.audio.pause();

@@ -285,7 +304,7 @@ if (this.encNode) this.encNode.disconnect();

if (this.workerURL) URL.revokeObjectURL(this.workerURL);
if (this.fileURL) URL.revokeObjectURL(this.fileURL);
if (this.blobURL) URL.revokeObjectURL(this.blobURL);
if (this.tid) clearTimeout(this.tid);
this.backdrop.remove();
if (file) {
this.resolve(file);
if (blob) {
this.resolve(blob);
} else {

@@ -304,19 +323,28 @@ this.reject(new Error("No record made"));

initAudio() {
if (!navigator.mediaDevices.getUserMedia) {
const err = new Error("getUserMedia is not implemented in this browser");
return Promise.reject(err);
}
return navigator.mediaDevices.getUserMedia({audio: true}).then(stream => {
const audioCtx = this.audioCtx = new AudioContext();
const getUserMedia = navigator.mediaDevices && navigator.mediaDevices.getUserMedia
? function(constraints) {
return navigator.mediaDevices.getUserMedia(constraints);
}
: function(constraints) {
const oldGetUserMedia = navigator.webkitGetUserMedia || navigator.mozGetUserMedia;
if (!oldGetUserMedia) {
return Promise.reject(new Error("getUserMedia is not implemented in this browser"));
}
return new Promise(function(resolve, reject) {
oldGetUserMedia.call(navigator, constraints, resolve, reject);
});
};
return getUserMedia({audio: true}).then(stream => {
const audioCtx = this.audioCtx = new (window.AudioContext
|| window.webkitAudioContext)();
const sourceNode = audioCtx.createMediaStreamSource(stream);
const gainNode = this.gainNode = audioCtx.createGain();
const gainNode = this.gainNode = (audioCtx.createGain
|| audioCtx.createGainNode).call(audioCtx);
sourceNode.connect(gainNode);
const pitchFX = this.pitchFX = new Jungle(audioCtx);
const encNode = this.encNode = audioCtx.createScriptProcessor(0, 1, 1);
encNode.onaudioprocess = (e) => {
const samples = e.inputBuffer.getChannelData(0);
this.worker.postMessage({type: "data", data: samples});
};
const encNode = this.encNode = (audioCtx.createScriptProcessor
|| audioCtx.createJavaScriptNode).call(audioCtx, 0, 1, 1);
pitchFX.output.connect(encNode);

@@ -332,3 +360,4 @@ });

const worker = this.worker = new Worker(workerURL);
worker.postMessage({type: "init", data: this.wasmURL});
const { wasmURL, shimURL } = this;
worker.postMessage({type: "init", data: {wasmURL, shimURL}});
return new Promise((resolve, reject) => {

@@ -350,4 +379,4 @@ worker.onmessage = (e) => {

case "stop":
this.file = msg.data;
this.fileURL = URL.createObjectURL(msg.data);
this.blob = msg.data;
this.blobURL = URL.createObjectURL(msg.data);
this.recordBtn.style.display = "";

@@ -364,5 +393,5 @@ this.stopBtn.style.display = "none";

this.audio.pause();
this.file = null;
if (this.fileURL) URL.revokeObjectURL(this.fileURL);
this.fileURL = null;
this.blob = null;
if (this.blobURL) URL.revokeObjectURL(this.blobURL);
this.blobURL = null;
this.start = now();

@@ -374,2 +403,6 @@ this.updateTime();

this.worker.postMessage({type: "start", data: this.audioCtx.sampleRate});
this.encNode.onaudioprocess = (e) => {
const samples = e.inputBuffer.getChannelData(0);
this.worker.postMessage({type: "data", data: samples});
};
this.encNode.connect(this.audioCtx.destination);

@@ -382,2 +415,3 @@ }

this.encNode.disconnect();
this.encNode.onaudioprocess = null;
this.worker.postMessage({type: "stop", data: null});

@@ -399,5 +433,8 @@ }

* @param {Object=} opts - Options
* @param {string=} opts.wasmURL - URL of the module ("vmsg.wasm" by default)
* @param {number=} opts.pitch - Initial pitch shift (0 by default)
* @return {Promise.<File>} A promise that contains recorded file when fulfilled.
* @param {string=} opts.wasmURL - URL of the module
* ("/static/js/vmsg.wasm" by default)
* @param {string=} opts.shimURL - URL of the WebAssembly polyfill
* ("/static/js/wasm-polyfill.js" by default)
* @param {number=} opts.pitch - Initial pitch shift ([-1, 1], 0 by default)
* @return {Promise.<Blob>} A promise that contains recorded blob when fulfilled.
*/

@@ -419,5 +456,10 @@ export function record(opts) {

/**
* All available public items.
*/
export default { record };
// Borrowed from and slightly modified:
// https://github.com/cwilso/Audio-Input-Effects/blob/master/js/jungle.js
//
// Copyright 2012, Google Inc.

@@ -519,4 +561,4 @@ // All rights reserved.

// Create nodes for the input and output of this "module".
var input = context.createGain();
var output = context.createGain();
var input = (context.createGain || context.createGainNode).call(context);
var output = (context.createGain || context.createGainNode).call(context);
this.input = input;

@@ -542,7 +584,7 @@ this.output = output;

// for switching between oct-up and oct-down
var mod1Gain = context.createGain();
var mod2Gain = context.createGain();
var mod3Gain = context.createGain();
var mod1Gain = (context.createGain || context.createGainNode).call(context);
var mod2Gain = (context.createGain || context.createGainNode).call(context);
var mod3Gain = (context.createGain || context.createGainNode).call(context);
mod3Gain.gain.value = 0;
var mod4Gain = context.createGain();
var mod4Gain = (context.createGain || context.createGainNode).call(context);
mod4Gain.gain.value = 0;

@@ -556,7 +598,7 @@

// Delay amount for changing pitch.
var modGain1 = context.createGain();
var modGain2 = context.createGain();
var modGain1 = (context.createGain || context.createGainNode).call(context);
var modGain2 = (context.createGain || context.createGainNode).call(context);
var delay1 = context.createDelay();
var delay2 = context.createDelay();
var delay1 = (context.createDelay || context.createDelayNode).call(context);
var delay2 = (context.createDelay || context.createDelayNode).call(context);
mod1Gain.connect(modGain1);

@@ -578,4 +620,4 @@ mod2Gain.connect(modGain2);

var mix1 = context.createGain();
var mix2 = context.createGain();
var mix1 = (context.createGain || context.createGainNode).call(context);
var mix2 = (context.createGain || context.createGainNode).call(context);
mix1.gain.value = 0;

@@ -582,0 +624,0 @@ mix2.gain.value = 0;

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc