├── .babelrc
├── .browserslistrc
├── .github
└── FUNDING.yml
├── .gitignore
├── .gitmodules
├── .npmignore
├── .postcssrc
├── COPYING
├── Makefile
├── README.md
├── demo
├── index.css
├── index.html
├── index.js
└── ribbon.png
├── lame-svn.patch
├── package-lock.json
├── package.json
├── vmsg.c
├── vmsg.css
├── vmsg.d.ts
├── vmsg.js
└── vmsg.wasm
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["@babel/preset-env"],
3 | "plugins": ["@babel/plugin-proposal-class-properties"]
4 | }
5 |
--------------------------------------------------------------------------------
/.browserslistrc:
--------------------------------------------------------------------------------
1 | defaults
2 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | custom: https://www.blockchain.com/btc/payment_request?address=3LKKbbi34MHYRQSLV3ZiDGoKgUmCjhTumT&message=Kagami+open+source+projects+support
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /node_modules/
2 | /dist/
3 | /.cache/
4 | /_vmsg.*
5 | /vmsg.es5.js
6 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "lame-svn"]
2 | path = lame-svn
3 | url = https://github.com/Kagami/lame-svn.git
4 | ignore = dirty
5 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | *
2 | !/COPYING
3 | !/vmsg.css
4 | !/vmsg.js
5 | !/vmsg.es5.js
6 | !/vmsg.d.ts
7 | !/vmsg.wasm
8 |
--------------------------------------------------------------------------------
/.postcssrc:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": {
3 | "autoprefixer": true
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/COPYING:
--------------------------------------------------------------------------------
1 | Creative Commons Legal Code
2 |
3 | CC0 1.0 Universal
4 |
5 | CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
6 | LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
7 | ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
8 | INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
9 | REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
10 | PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
11 | THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
12 | HEREUNDER.
13 |
14 | Statement of Purpose
15 |
16 | The laws of most jurisdictions throughout the world automatically confer
17 | exclusive Copyright and Related Rights (defined below) upon the creator
18 | and subsequent owner(s) (each and all, an "owner") of an original work of
19 | authorship and/or a database (each, a "Work").
20 |
21 | Certain owners wish to permanently relinquish those rights to a Work for
22 | the purpose of contributing to a commons of creative, cultural and
23 | scientific works ("Commons") that the public can reliably and without fear
24 | of later claims of infringement build upon, modify, incorporate in other
25 | works, reuse and redistribute as freely as possible in any form whatsoever
26 | and for any purposes, including without limitation commercial purposes.
27 | These owners may contribute to the Commons to promote the ideal of a free
28 | culture and the further production of creative, cultural and scientific
29 | works, or to gain reputation or greater distribution for their Work in
30 | part through the use and efforts of others.
31 |
32 | For these and/or other purposes and motivations, and without any
33 | expectation of additional consideration or compensation, the person
34 | associating CC0 with a Work (the "Affirmer"), to the extent that he or she
35 | is an owner of Copyright and Related Rights in the Work, voluntarily
36 | elects to apply CC0 to the Work and publicly distribute the Work under its
37 | terms, with knowledge of his or her Copyright and Related Rights in the
38 | Work and the meaning and intended legal effect of CC0 on those rights.
39 |
40 | 1. Copyright and Related Rights. A Work made available under CC0 may be
41 | protected by copyright and related or neighboring rights ("Copyright and
42 | Related Rights"). Copyright and Related Rights include, but are not
43 | limited to, the following:
44 |
45 | i. the right to reproduce, adapt, distribute, perform, display,
46 | communicate, and translate a Work;
47 | ii. moral rights retained by the original author(s) and/or performer(s);
48 | iii. publicity and privacy rights pertaining to a person's image or
49 | likeness depicted in a Work;
50 | iv. rights protecting against unfair competition in regards to a Work,
51 | subject to the limitations in paragraph 4(a), below;
52 | v. rights protecting the extraction, dissemination, use and reuse of data
53 | in a Work;
54 | vi. database rights (such as those arising under Directive 96/9/EC of the
55 | European Parliament and of the Council of 11 March 1996 on the legal
56 | protection of databases, and under any national implementation
57 | thereof, including any amended or successor version of such
58 | directive); and
59 | vii. other similar, equivalent or corresponding rights throughout the
60 | world based on applicable law or treaty, and any national
61 | implementations thereof.
62 |
63 | 2. Waiver. To the greatest extent permitted by, but not in contravention
64 | of, applicable law, Affirmer hereby overtly, fully, permanently,
65 | irrevocably and unconditionally waives, abandons, and surrenders all of
66 | Affirmer's Copyright and Related Rights and associated claims and causes
67 | of action, whether now known or unknown (including existing as well as
68 | future claims and causes of action), in the Work (i) in all territories
69 | worldwide, (ii) for the maximum duration provided by applicable law or
70 | treaty (including future time extensions), (iii) in any current or future
71 | medium and for any number of copies, and (iv) for any purpose whatsoever,
72 | including without limitation commercial, advertising or promotional
73 | purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
74 | member of the public at large and to the detriment of Affirmer's heirs and
75 | successors, fully intending that such Waiver shall not be subject to
76 | revocation, rescission, cancellation, termination, or any other legal or
77 | equitable action to disrupt the quiet enjoyment of the Work by the public
78 | as contemplated by Affirmer's express Statement of Purpose.
79 |
80 | 3. Public License Fallback. Should any part of the Waiver for any reason
81 | be judged legally invalid or ineffective under applicable law, then the
82 | Waiver shall be preserved to the maximum extent permitted taking into
83 | account Affirmer's express Statement of Purpose. In addition, to the
84 | extent the Waiver is so judged Affirmer hereby grants to each affected
85 | person a royalty-free, non transferable, non sublicensable, non exclusive,
86 | irrevocable and unconditional license to exercise Affirmer's Copyright and
87 | Related Rights in the Work (i) in all territories worldwide, (ii) for the
88 | maximum duration provided by applicable law or treaty (including future
89 | time extensions), (iii) in any current or future medium and for any number
90 | of copies, and (iv) for any purpose whatsoever, including without
91 | limitation commercial, advertising or promotional purposes (the
92 | "License"). The License shall be deemed effective as of the date CC0 was
93 | applied by Affirmer to the Work. Should any part of the License for any
94 | reason be judged legally invalid or ineffective under applicable law, such
95 | partial invalidity or ineffectiveness shall not invalidate the remainder
96 | of the License, and in such case Affirmer hereby affirms that he or she
97 | will not (i) exercise any of his or her remaining Copyright and Related
98 | Rights in the Work or (ii) assert any associated claims and causes of
99 | action with respect to the Work, in either case contrary to Affirmer's
100 | express Statement of Purpose.
101 |
102 | 4. Limitations and Disclaimers.
103 |
104 | a. No trademark or patent rights held by Affirmer are waived, abandoned,
105 | surrendered, licensed or otherwise affected by this document.
106 | b. Affirmer offers the Work as-is and makes no representations or
107 | warranties of any kind concerning the Work, express, implied,
108 | statutory or otherwise, including without limitation warranties of
109 | title, merchantability, fitness for a particular purpose, non
110 | infringement, or the absence of latent or other defects, accuracy, or
111 | the present or absence of errors, whether or not discoverable, all to
112 | the greatest extent permissible under applicable law.
113 | c. Affirmer disclaims responsibility for clearing rights of other persons
114 | that may apply to the Work or any use thereof, including without
115 | limitation any person's Copyright and Related Rights in the Work.
116 | Further, Affirmer disclaims responsibility for obtaining any necessary
117 | consents, permissions or other rights required for any use of the
118 | Work.
119 | d. Affirmer understands and acknowledges that Creative Commons is not a
120 | party to this document and has no duty or obligation with respect to
121 | this CC0 or use of the Work.
122 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | export EMCC_WASM_BACKEND = 1
2 | export EMCC_EXPERIMENTAL_USE_LLD = 1
3 |
4 | all: vmsg.wasm
5 |
6 | lame-svn/lame/dist/lib/libmp3lame.so:
7 | cd lame-svn/lame && \
8 | git reset --hard && \
9 | patch -p2 < ../../lame-svn.patch && \
10 | emconfigure ./configure \
11 | CFLAGS="-DNDEBUG -Oz" \
12 | --prefix="$$(pwd)/dist" \
13 | --host=x86-none-linux \
14 | --disable-static \
15 | \
16 | --disable-gtktest \
17 | --disable-analyzer-hooks \
18 | --disable-decoder \
19 | --disable-frontend \
20 | && \
21 | emmake make -j8 && \
22 | emmake make install
23 |
24 | # WASM backend doesn't support EMSCRIPTEN_KEEPALIVE, see:
25 | # https://github.com/kripken/emscripten/issues/6233
26 | # Output to bare .wasm doesn't work properly so need to create
27 | # intermediate files.
28 | vmsg.wasm: lame-svn/lame/dist/lib/libmp3lame.so vmsg.c
29 | emcc $^ \
30 | -DNDEBUG -Oz --llvm-lto 3 -Ilame-svn/lame/dist/include \
31 | -s WASM=1 \
32 | -s "EXPORTED_FUNCTIONS=['_vmsg_init','_vmsg_encode','_vmsg_flush','_vmsg_free']" \
33 | -o _vmsg.js
34 | cp _vmsg.wasm $@
35 |
36 | clean: clean-lame clean-wasm
37 | clean-lame:
38 | cd lame-svn && git clean -dfx
39 | clean-wasm:
40 | rm -f vmsg.wasm _vmsg.*
41 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # vmsg [](https://www.npmjs.com/package/vmsg)
2 |
3 | vmsg is a small library for creating voice messages. While traditional
4 | way of communicating on the web is via text, sometimes it's easier or
5 | rather funnier to express your thoughts just by saying it. Of course it
6 | doesn't require any special support: record your voice with some
7 | standard program, upload to file hosting and share the link. But why
8 | bother with all of that tedious stuff if you can do the same in browser
9 | with a few clicks.
10 |
11 | :confetti_ball: :tada: **[DEMO](https://kagami.github.io/vmsg/)** :tada: :confetti_ball:
12 |
13 | ## Features
14 |
15 | * No dependencies, framework-agnostic, can be easily added to any site
16 | * Small: ~73kb gzipped WASM module and ~3kb gzipped JS + CSS
17 | * Uses MP3 format which is widely supported
18 | * Works in all latest browsers
19 |
20 | ## Supported browsers
21 |
22 | * Chrome 32+
23 | * Firefox 27+
24 | * Safari 11+
25 | * Edge 12+
26 |
27 | ## Usage
28 |
29 | ```
30 | npm install vmsg --save
31 | ```
32 |
33 | ```js
34 | import { record } from "vmsg";
35 |
36 | someButton.onclick = function() {
37 | record(/* {wasmURL: "/static/js/vmsg.wasm"} */).then(blob => {
38 | console.log("Recorded MP3", blob);
39 | // Can be used like this:
40 | //
41 | // const form = new FormData();
42 | // form.append("file[]", blob, "record.mp3");
43 | // fetch("/upload.php", {
44 | // credentials: "include",
45 | // method: "POST",
46 | // body: form,
47 | // }).then(resp => {
48 | // });
49 | });
50 | };
51 | ```
52 |
53 | That's it! Don't forget to include [vmsg.css](vmsg.css) and
54 | [vmsg.wasm](vmsg.wasm) in your project. For browsers without WebAssembly
55 | support you need to also include
56 | [wasm-polyfill.js](https://github.com/Kagami/wasm-polyfill.js).
57 |
58 | See [demo](demo) directory for a more feasible example.
59 |
60 | A minimal React example for using Recorder with your own UI can be found [here](https://codesandbox.io/s/v67oz43lm7).
61 |
62 | See also [non React demo](https://github.com/addpipe/simple-vmsg-demo) and [Recording mp3 audio in HTML5 using vmsg](https://addpipe.com/blog/recording-mp3-audio-in-html5-using-vmsg-a-webassembly-library-based-on-lame/) article.
63 |
64 | ## Development
65 |
66 | 1. Install [Emscripten SDK](https://github.com/juj/emsdk).
67 | 2. Install latest LLVM, Clang and LLD with WebAssembly backend, fix
68 | `LLVM_ROOT` variable of Emscripten config.
69 | 3. Make sure you have a standard GNU development environment.
70 | 4. Activate emsdk environment.
71 | 5. ```bash
72 | git clone --recurse-submodules https://github.com/Kagami/vmsg.git && cd vmsg
73 | make clean all
74 | npm install
75 | npm start
76 | ```
77 |
78 | These instructions are very basic because there're a lot of systems with
79 | different conventions. Docker image would probably be provided to fix it.
80 |
81 | ## Technical details for nerds
82 |
83 | vmsg uses LAME encoder underneath compiled with Emscripten to
84 | WebAssembly module. LAME build is optimized for size, weights only
85 | little more than 70kb gzipped and can be super-efficiently fetched and
86 | parsed by browser. [It's like a small image.](https://twitter.com/wycats/status/942908325775077376)
87 |
88 | Access to microphone is implemented with Web Audio API, data samples
89 | sent to Web Worker which is responsibe for loading WebAssembly module
90 | and calling LAME API.
91 |
92 | Module is produced with modern LLVM WASM backend and LLD linker which
93 | should become standard soon, also vmsg has own tiny WASM runtime instead
94 | of Emscripten's to decrease overall size and simplify architecture.
95 | Worker code is included in the main JS module so end-user has to care
96 | only about 3 files: `vmsg.js`, `vmsg.css` and `vmsg.wasm`. CSS can be
97 | inlined too but IMO that would be ugly.
98 |
99 | In order to support browsers without WebAssembly,
100 | [WebAssembly polyfill](https://github.com/Kagami/wasm-polyfill.js) is
101 | being used. It translates binary module into semantically-equivalent
102 | JavaScript on the fly (almost asm.js compatible but doesn't fully
103 | validate yet) so we don't need separate asm.js build and can use
104 | standard WebAssembly API. It's not as effecient but for audio encoding
105 | should be enough.
106 |
107 | **See also:** [Creating WebAssembly-powered library for modern web](https://hackernoon.com/creating-webassembly-powered-library-for-modern-web-846da334f8fc) article.
108 |
109 | ## Why not MediaRecorder?
110 |
111 | [MediaStream Recording API](https://developer.mozilla.org/en-US/docs/Web/API/MediaStream_Recording_API)
112 | is great but:
113 |
114 | * Works only in Firefox and Chrome
115 | * Provides little to no options, e.g. VBR quality can't be specified
116 | * Firefox/Chrome encode only to Opus which can't be natively played in Safari and Edge
117 |
118 | ## But you can use e.g. ogv.js polyfill!
119 |
120 | * It make things more complicated, now you need both encoder and decoder
121 | * Opus gives you ~2x bitrate win but for 500kb per minute files it's not that much
122 | * MP3 is much more widespread, so even while compression is not best compatibility matters
123 |
124 | ## License
125 |
126 | vmsg is licensed under [CC0](COPYING).
127 | LAME is licensed under [LGPL](https://github.com/Kagami/lame-svn/blob/master/lame/COPYING).
128 | MP3 patents seems to [have expired since April 23, 2017](https://en.wikipedia.org/wiki/LAME#Patents_and_legal_issues).
129 |
--------------------------------------------------------------------------------
/demo/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 20px;
3 | background: #ebeaeb;
4 | color: #0a0a0a;
5 | font-size: 16px;
6 | }
7 |
8 | body, textarea {
9 | font-family: Helvetica,sans-serif;
10 | line-height: 1.4;
11 | }
12 |
13 | textarea {
14 | font-size: 15px;
15 | }
16 |
17 | a {
18 | text-decoration: none;
19 | }
20 |
21 | .ribbon {
22 | position: absolute;
23 | top: 0;
24 | right: 0;
25 | border: 0;
26 | }
27 |
28 | .app {
29 | margin: 0 auto;
30 | max-width: 700px;
31 | }
32 |
33 | .post__header {
34 | margin: 0 0 20px 0;
35 | text-align: center;
36 | }
37 |
38 | .post__body {
39 | margin-bottom: 20px;
40 | }
41 |
42 | .comments {
43 | font-size: 15px;
44 | }
45 |
46 | .comment {
47 | margin-bottom: 20px;
48 | padding: 6px 10px;
49 | background: #e4e1e5;
50 | border-radius: 4px;
51 | box-shadow: 1px 1px 4px 0 rgba(59,26,84,.6);
52 | }
53 |
54 | .comment__header {
55 | margin-bottom: 3px;
56 | }
57 |
58 | .comment__id {
59 | display: inline-block;
60 | margin: 0;
61 | margin-right: 5px;
62 | cursor: default;
63 | user-select: none;
64 | }
65 |
66 | .comment__record {
67 | font-size: 20px;
68 | line-height: 17px;
69 | color: #00f;
70 | cursor: pointer;
71 | }
72 |
73 | .comment__record:hover {
74 | color: #f00;
75 | }
76 |
77 | .comment__body {
78 | margin: 0;
79 | white-space: pre;
80 | }
81 |
82 | .reply {
83 | padding: 6px 10px;
84 | background: #e4e1e5;
85 | border-radius: 4px;
86 | box-shadow: 1px 1px 4px 0 rgba(59,26,84,.6);
87 | }
88 |
89 | .reply__body {
90 | width: 100%;
91 | height: 80px;
92 | background: none;
93 | border: none;
94 | padding: 0;
95 | resize: none;
96 | outline: none;
97 | }
98 |
99 | .reply-control {
100 | margin-right: 5px;
101 | cursor: pointer;
102 | }
103 | .reply-control:disabled {
104 | cursor: default;
105 | }
106 |
107 | .reply-record {
108 | cursor: default;
109 | user-select: none;
110 | }
111 |
--------------------------------------------------------------------------------
/demo/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |