├── .babelrc ├── .editorconfig ├── .gitignore ├── .gitmodules ├── .jsdoc.json ├── .nvmrc ├── LICENSE ├── Makefile ├── README.md ├── docs └── @etercast │ └── mp3 │ └── 1.0.0 │ ├── Encoder.html │ ├── fonts │ ├── OpenSans-Bold-webfont.eot │ ├── OpenSans-Bold-webfont.svg │ ├── OpenSans-Bold-webfont.woff │ ├── OpenSans-BoldItalic-webfont.eot │ ├── OpenSans-BoldItalic-webfont.svg │ ├── OpenSans-BoldItalic-webfont.woff │ ├── OpenSans-Italic-webfont.eot │ ├── OpenSans-Italic-webfont.svg │ ├── OpenSans-Italic-webfont.woff │ ├── OpenSans-Light-webfont.eot │ ├── OpenSans-Light-webfont.svg │ ├── OpenSans-Light-webfont.woff │ ├── OpenSans-LightItalic-webfont.eot │ ├── OpenSans-LightItalic-webfont.svg │ ├── OpenSans-LightItalic-webfont.woff │ ├── OpenSans-Regular-webfont.eot │ ├── OpenSans-Regular-webfont.svg │ ├── OpenSans-Regular-webfont.woff │ ├── OpenSans-Semibold-webfont.eot │ ├── OpenSans-Semibold-webfont.svg │ ├── OpenSans-Semibold-webfont.ttf │ ├── OpenSans-Semibold-webfont.woff │ ├── OpenSans-SemiboldItalic-webfont.eot │ ├── OpenSans-SemiboldItalic-webfont.svg │ ├── OpenSans-SemiboldItalic-webfont.ttf │ └── OpenSans-SemiboldItalic-webfont.woff │ ├── global.html │ ├── index.html │ ├── module-@etercast_mp3-Encoder.html │ ├── module-@etercast_mp3.html │ ├── module-mp3-Encoder.html │ ├── module-mp3.html │ ├── mp3.js.html │ ├── scripts │ ├── linenumber.js │ └── prettify │ │ ├── Apache-License-2.0.txt │ │ ├── lang-css.js │ │ └── prettify.js │ └── styles │ ├── jsdoc-default.css │ ├── prettify-atelier-cave-dark.css │ ├── prettify-jsdoc.css │ └── prettify-tomorrow.css ├── examples ├── index.html ├── mp3.js └── mp3.wasm ├── package-lock.json ├── package.json ├── serve.json └── src ├── mp3.c ├── mp3.h └── mp3.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "@babel/preset-env" 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | 6 | indent_size = 2 7 | indent_style = space 8 | 9 | end_of_line = lf 10 | 11 | insert_final_newline = true 12 | 13 | trim_trailing_whitespace = true -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.gitignore.io/api/linux,osx,windows,vim,emacs,sublimetext,visualstudiocode,node 3 | # Edit at https://www.gitignore.io/?templates=linux,osx,windows,vim,emacs,sublimetext,visualstudiocode,node 4 | 5 | ### Emacs ### 6 | # -*- mode: gitignore; -*- 7 | *~ 8 | \#*\# 9 | /.emacs.desktop 10 | /.emacs.desktop.lock 11 | *.elc 12 | auto-save-list 13 | tramp 14 | .\#* 15 | 16 | # Org-mode 17 | .org-id-locations 18 | *_archive 19 | 20 | # flymake-mode 21 | *_flymake.* 22 | 23 | # eshell files 24 | /eshell/history 25 | /eshell/lastdir 26 | 27 | # elpa packages 28 | /elpa/ 29 | 30 | # reftex files 31 | *.rel 32 | 33 | # AUCTeX auto folder 34 | /auto/ 35 | 36 | # cask packages 37 | .cask/ 38 | dist/ 39 | 40 | # Flycheck 41 | flycheck_*.el 42 | 43 | # server auth directory 44 | /server/ 45 | 46 | # projectiles files 47 | .projectile 48 | 49 | # directory configuration 50 | .dir-locals.el 51 | 52 | # network security 53 | /network-security.data 54 | 55 | 56 | ### Linux ### 57 | 58 | # temporary files which can be created if a process still has a handle open of a deleted file 59 | .fuse_hidden* 60 | 61 | # KDE directory preferences 62 | .directory 63 | 64 | # Linux trash folder which might appear on any partition or disk 65 | .Trash-* 66 | 67 | # .nfs files are created when an open file is removed but is still being accessed 68 | .nfs* 69 | 70 | ### Node ### 71 | # Logs 72 | logs 73 | *.log 74 | npm-debug.log* 75 | yarn-debug.log* 76 | yarn-error.log* 77 | lerna-debug.log* 78 | 79 | # Diagnostic reports (https://nodejs.org/api/report.html) 80 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 81 | 82 | # Runtime data 83 | pids 84 | *.pid 85 | *.seed 86 | *.pid.lock 87 | 88 | # Directory for instrumented libs generated by jscoverage/JSCover 89 | lib-cov 90 | 91 | # Coverage directory used by tools like istanbul 92 | coverage 93 | *.lcov 94 | 95 | # nyc test coverage 96 | .nyc_output 97 | 98 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 99 | .grunt 100 | 101 | # Bower dependency directory (https://bower.io/) 102 | bower_components 103 | 104 | # node-waf configuration 105 | .lock-wscript 106 | 107 | # Compiled binary addons (https://nodejs.org/api/addons.html) 108 | build/Release 109 | 110 | # Dependency directories 111 | node_modules/ 112 | jspm_packages/ 113 | 114 | # TypeScript v1 declaration files 115 | typings/ 116 | 117 | # TypeScript cache 118 | *.tsbuildinfo 119 | 120 | # Optional npm cache directory 121 | .npm 122 | 123 | # Optional eslint cache 124 | .eslintcache 125 | 126 | # Optional REPL history 127 | .node_repl_history 128 | 129 | # Output of 'npm pack' 130 | *.tgz 131 | 132 | # Yarn Integrity file 133 | .yarn-integrity 134 | 135 | # dotenv environment variables file 136 | .env 137 | .env.test 138 | 139 | # parcel-bundler cache (https://parceljs.org/) 140 | .cache 141 | 142 | # next.js build output 143 | .next 144 | 145 | # nuxt.js build output 146 | .nuxt 147 | 148 | # vuepress build output 149 | .vuepress/dist 150 | 151 | # Serverless directories 152 | .serverless/ 153 | 154 | # FuseBox cache 155 | .fusebox/ 156 | 157 | # DynamoDB Local files 158 | .dynamodb/ 159 | 160 | ### OSX ### 161 | # General 162 | .DS_Store 163 | .AppleDouble 164 | .LSOverride 165 | 166 | # Icon must end with two \r 167 | Icon 168 | 169 | # Thumbnails 170 | ._* 171 | 172 | # Files that might appear in the root of a volume 173 | .DocumentRevisions-V100 174 | .fseventsd 175 | .Spotlight-V100 176 | .TemporaryItems 177 | .Trashes 178 | .VolumeIcon.icns 179 | .com.apple.timemachine.donotpresent 180 | 181 | # Directories potentially created on remote AFP share 182 | .AppleDB 183 | .AppleDesktop 184 | Network Trash Folder 185 | Temporary Items 186 | .apdisk 187 | 188 | ### SublimeText ### 189 | # Cache files for Sublime Text 190 | *.tmlanguage.cache 191 | *.tmPreferences.cache 192 | *.stTheme.cache 193 | 194 | # Workspace files are user-specific 195 | *.sublime-workspace 196 | 197 | # Project files should be checked into the repository, unless a significant 198 | # proportion of contributors will probably not be using Sublime Text 199 | # *.sublime-project 200 | 201 | # SFTP configuration file 202 | sftp-config.json 203 | 204 | # Package control specific files 205 | Package Control.last-run 206 | Package Control.ca-list 207 | Package Control.ca-bundle 208 | Package Control.system-ca-bundle 209 | Package Control.cache/ 210 | Package Control.ca-certs/ 211 | Package Control.merged-ca-bundle 212 | Package Control.user-ca-bundle 213 | oscrypto-ca-bundle.crt 214 | bh_unicode_properties.cache 215 | 216 | # Sublime-github package stores a github token in this file 217 | # https://packagecontrol.io/packages/sublime-github 218 | GitHub.sublime-settings 219 | 220 | ### Vim ### 221 | # Swap 222 | [._]*.s[a-v][a-z] 223 | [._]*.sw[a-p] 224 | [._]s[a-rt-v][a-z] 225 | [._]ss[a-gi-z] 226 | [._]sw[a-p] 227 | 228 | # Session 229 | Session.vim 230 | Sessionx.vim 231 | 232 | # Temporary 233 | .netrwhist 234 | # Auto-generated tag files 235 | tags 236 | # Persistent undo 237 | [._]*.un~ 238 | 239 | ### VisualStudioCode ### 240 | .vscode/* 241 | !.vscode/settings.json 242 | !.vscode/tasks.json 243 | !.vscode/launch.json 244 | !.vscode/extensions.json 245 | 246 | ### VisualStudioCode Patch ### 247 | # Ignore all local history of files 248 | .history 249 | 250 | ### Windows ### 251 | # Windows thumbnail cache files 252 | Thumbs.db 253 | Thumbs.db:encryptable 254 | ehthumbs.db 255 | ehthumbs_vista.db 256 | 257 | # Dump file 258 | *.stackdump 259 | 260 | # Folder config file 261 | [Dd]esktop.ini 262 | 263 | # Recycle Bin used on file shares 264 | $RECYCLE.BIN/ 265 | 266 | # Windows Installer files 267 | *.cab 268 | *.msi 269 | *.msix 270 | *.msm 271 | *.msp 272 | 273 | # Windows shortcuts 274 | *.lnk 275 | 276 | # End of https://www.gitignore.io/api/linux,osx,windows,vim,emacs,sublimetext,visualstudiocode,node 277 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "lame"] 2 | path = lame 3 | url = https://github.com/etercast/lame.git 4 | -------------------------------------------------------------------------------- /.jsdoc.json: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | "plugins/markdown" 4 | ], 5 | "source": { 6 | "include": ["src", "README.md", "package.json"], 7 | "includePattern": "\\.js$" 8 | }, 9 | "opts": { 10 | "template": "node_modules/@etercast/jsdoc-template", 11 | "encoding": "utf8", 12 | "destination": "./docs/", 13 | "package": "./package.json", 14 | "readme": "./README.md" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | v12.13.0 2 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | all: mp3.wasm 2 | 3 | # 4 | # What are these options for? 5 | # 6 | # -DNDEBUG: Defines NDEBUG to remove asserts. 7 | # -Oz: Optimize for size. 8 | # --prefix: Where are we going to put generated files. 9 | # --host: Indicates what kind of machine (in this wasm) we are using. 10 | # --disable-static: Disables building the static version of libmp3lame. 11 | # --disable-shared: Disables building the shared version of libmp3lame. 12 | # --disable-largefile: Disables handling large files. 13 | # --disable-gtktest: Disables gtktests. 14 | # --disable-analyzer-hooks: Disables hooks. 15 | # --disable-decoder: Disables decoder. 16 | # --disable-frontend: Disables LAME frontend (the binary lame). 17 | # 18 | lame/dist/lib/libmp3lame.a: 19 | cd lame && \ 20 | emconfigure ./configure \ 21 | CFLAGS="-DNDEBUG -Oz" \ 22 | --prefix="$$(pwd)/dist" \ 23 | --host=x86-none-linux \ 24 | --disable-shared \ 25 | --disable-largefile \ 26 | --disable-gtktest \ 27 | --disable-analyzer-hooks \ 28 | --disable-decoder \ 29 | --disable-frontend && \ 30 | emmake make -j12 && \ 31 | emmake make install 32 | 33 | mp3.wasm: lame/dist/lib/libmp3lame.a src/mp3.c 34 | emcc $^ \ 35 | -Ilame/dist/include \ 36 | -DNDEBUG \ 37 | --llvm-lto 3 \ 38 | -Oz \ 39 | -s STRICT=1 \ 40 | -s STANDALONE_WASM=1 \ 41 | -s MALLOC=emmalloc \ 42 | -s ASSERTIONS=0 \ 43 | -s FILESYSTEM=0 \ 44 | -s INVOKE_RUN=0 \ 45 | -s EXPORT_ES6=1 \ 46 | -s MODULARIZE_INSTANCE=1 \ 47 | -o dist/mp3.wasm 48 | 49 | clean: clean-lame clean-wasm 50 | 51 | clean-lame: 52 | cd lame && git clean -dfx 53 | 54 | clean-wasm: 55 | rm -f dist/mp3.* && \ 56 | rm -f src/mp3.js src/mp3.wasm 57 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Etercast MP3 Encoder 2 | 3 | ![Etercast](https://etercast.com/images/og_landscape.jpg) 4 | 5 | This repository contains part of the [Etercast](https://etercast.com) MP3 encoding library. It uses [LAME](https://lame.sourceforge.io/) and C/C++ -> WebAssembly compiler [Emscripten](https://emscripten.org). 6 | 7 | ## How to use it 8 | 9 | ```javascript 10 | import instantiate from '@etercast/mp3' 11 | 12 | // instantiate() function fetches and instantiates the WASM module 13 | // and returns a Encoder class. 14 | // If you upload the mp3.wasm to a CDN you can specify the 15 | // WASM url by passing it to instantiate. 16 | // const Encoder = await instantiate('https://mycdn.com/mp3.wasm') 17 | const Encoder = await instantiate() 18 | 19 | // You can also use Encoder.create() 20 | const encoder = new Encoder({ 21 | sampleRate: audioContext.sampleRate, 22 | samples: 2048, 23 | numChannels: 1 24 | }) 25 | 26 | // leftChannelData must be a Float32Array with 2048 (the 27 | // specified in the encoder samples option) samples. 28 | // rightChannelData is optional (can be null or undefined). 29 | const encodedMP3Frames = encoder.encode(leftChannelData, rightChannelData) 30 | 31 | // Remaining MP3 encoded frames. It flushes available MP3 32 | // frames not returned in the previous call. 33 | const remainingEncodedMP3Frames = encoder.encode() 34 | 35 | const blob = new Blob([ 36 | encodedMP3Frames, 37 | remainingEncodedMP3Frames 38 | ], { 39 | type: 'audio/mp3' 40 | }) 41 | const url = URL.createObjectURL(blob) 42 | const a = document.createElement('a') 43 | a.href = url 44 | a.download = 'audio.mp3' 45 | a.dispatchEvent(new MouseEvent('click')) 46 | ``` 47 | 48 | You need to copy `mp3.wasm` from `node_modules/@etercast/mp3/dist/mp3.wasm` into your `dist` or `public` folder. 49 | 50 | ### Parcel 51 | 52 | If you're using parcel you can achieve this by using the plugin `parcel-plugin-static-files-copy` and modifying 53 | your `package.json` to include this: 54 | 55 | ```javascript 56 | "staticFiles": { 57 | "staticPath": [ 58 | "node_modules/@etercast/mp3/dist" 59 | ], 60 | "excludeGlob": [ 61 | "*.{js,mjs,cjs}" 62 | ] 63 | } 64 | ``` 65 | 66 | ### Webpack 67 | 68 | TODO: Add webpack documentation 69 | 70 | ## How to build it 71 | 72 | If you have [Emscripten](https://emscripten.org) installed locally then you can run: 73 | 74 | ```sh 75 | make 76 | ``` 77 | 78 | Otherwise you can use the [Docker](https://www.docker.com/) image by [trcezi](https://hub.docker.com/r/trzeci/emscripten) to build the MP3 encoder by executing: 79 | 80 | ```sh 81 | npm run build:emscripten 82 | ``` 83 | 84 | ## How to build documentation 85 | 86 | Calling `npm run build` will build this documentation but if you want to build documentation specifically you can run: 87 | 88 | ```sh 89 | npm run build:docs 90 | ``` 91 | 92 | ## Demo 93 | 94 | If you want to see a live demo running, run: 95 | 96 | ```sh 97 | npm run serve:examples 98 | ``` 99 | 100 | Made with :heart: by [ROJO 2](https://rojo2.com) 101 | -------------------------------------------------------------------------------- /docs/@etercast/mp3/1.0.0/fonts/OpenSans-Bold-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etercast/mp3/08aa4d475557fe44f5b85f436b6911d1ec67e092/docs/@etercast/mp3/1.0.0/fonts/OpenSans-Bold-webfont.eot -------------------------------------------------------------------------------- /docs/@etercast/mp3/1.0.0/fonts/OpenSans-Bold-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etercast/mp3/08aa4d475557fe44f5b85f436b6911d1ec67e092/docs/@etercast/mp3/1.0.0/fonts/OpenSans-Bold-webfont.woff -------------------------------------------------------------------------------- /docs/@etercast/mp3/1.0.0/fonts/OpenSans-BoldItalic-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etercast/mp3/08aa4d475557fe44f5b85f436b6911d1ec67e092/docs/@etercast/mp3/1.0.0/fonts/OpenSans-BoldItalic-webfont.eot -------------------------------------------------------------------------------- /docs/@etercast/mp3/1.0.0/fonts/OpenSans-BoldItalic-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etercast/mp3/08aa4d475557fe44f5b85f436b6911d1ec67e092/docs/@etercast/mp3/1.0.0/fonts/OpenSans-BoldItalic-webfont.woff -------------------------------------------------------------------------------- /docs/@etercast/mp3/1.0.0/fonts/OpenSans-Italic-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etercast/mp3/08aa4d475557fe44f5b85f436b6911d1ec67e092/docs/@etercast/mp3/1.0.0/fonts/OpenSans-Italic-webfont.eot -------------------------------------------------------------------------------- /docs/@etercast/mp3/1.0.0/fonts/OpenSans-Italic-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etercast/mp3/08aa4d475557fe44f5b85f436b6911d1ec67e092/docs/@etercast/mp3/1.0.0/fonts/OpenSans-Italic-webfont.woff -------------------------------------------------------------------------------- /docs/@etercast/mp3/1.0.0/fonts/OpenSans-Light-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etercast/mp3/08aa4d475557fe44f5b85f436b6911d1ec67e092/docs/@etercast/mp3/1.0.0/fonts/OpenSans-Light-webfont.eot -------------------------------------------------------------------------------- /docs/@etercast/mp3/1.0.0/fonts/OpenSans-Light-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etercast/mp3/08aa4d475557fe44f5b85f436b6911d1ec67e092/docs/@etercast/mp3/1.0.0/fonts/OpenSans-Light-webfont.woff -------------------------------------------------------------------------------- /docs/@etercast/mp3/1.0.0/fonts/OpenSans-LightItalic-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etercast/mp3/08aa4d475557fe44f5b85f436b6911d1ec67e092/docs/@etercast/mp3/1.0.0/fonts/OpenSans-LightItalic-webfont.eot -------------------------------------------------------------------------------- /docs/@etercast/mp3/1.0.0/fonts/OpenSans-LightItalic-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etercast/mp3/08aa4d475557fe44f5b85f436b6911d1ec67e092/docs/@etercast/mp3/1.0.0/fonts/OpenSans-LightItalic-webfont.woff -------------------------------------------------------------------------------- /docs/@etercast/mp3/1.0.0/fonts/OpenSans-Regular-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etercast/mp3/08aa4d475557fe44f5b85f436b6911d1ec67e092/docs/@etercast/mp3/1.0.0/fonts/OpenSans-Regular-webfont.eot -------------------------------------------------------------------------------- /docs/@etercast/mp3/1.0.0/fonts/OpenSans-Regular-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etercast/mp3/08aa4d475557fe44f5b85f436b6911d1ec67e092/docs/@etercast/mp3/1.0.0/fonts/OpenSans-Regular-webfont.woff -------------------------------------------------------------------------------- /docs/@etercast/mp3/1.0.0/fonts/OpenSans-Semibold-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etercast/mp3/08aa4d475557fe44f5b85f436b6911d1ec67e092/docs/@etercast/mp3/1.0.0/fonts/OpenSans-Semibold-webfont.eot -------------------------------------------------------------------------------- /docs/@etercast/mp3/1.0.0/fonts/OpenSans-Semibold-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etercast/mp3/08aa4d475557fe44f5b85f436b6911d1ec67e092/docs/@etercast/mp3/1.0.0/fonts/OpenSans-Semibold-webfont.ttf -------------------------------------------------------------------------------- /docs/@etercast/mp3/1.0.0/fonts/OpenSans-Semibold-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etercast/mp3/08aa4d475557fe44f5b85f436b6911d1ec67e092/docs/@etercast/mp3/1.0.0/fonts/OpenSans-Semibold-webfont.woff -------------------------------------------------------------------------------- /docs/@etercast/mp3/1.0.0/fonts/OpenSans-SemiboldItalic-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etercast/mp3/08aa4d475557fe44f5b85f436b6911d1ec67e092/docs/@etercast/mp3/1.0.0/fonts/OpenSans-SemiboldItalic-webfont.eot -------------------------------------------------------------------------------- /docs/@etercast/mp3/1.0.0/fonts/OpenSans-SemiboldItalic-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etercast/mp3/08aa4d475557fe44f5b85f436b6911d1ec67e092/docs/@etercast/mp3/1.0.0/fonts/OpenSans-SemiboldItalic-webfont.ttf -------------------------------------------------------------------------------- /docs/@etercast/mp3/1.0.0/fonts/OpenSans-SemiboldItalic-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etercast/mp3/08aa4d475557fe44f5b85f436b6911d1ec67e092/docs/@etercast/mp3/1.0.0/fonts/OpenSans-SemiboldItalic-webfont.woff -------------------------------------------------------------------------------- /docs/@etercast/mp3/1.0.0/global.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Global 7 | 8 | 9 | 10 | 11 | 12 |
13 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 |
28 | 29 |

30 | 31 | 32 | 33 |

34 | 35 | 36 |
37 | 38 |
39 |
40 | 41 | 42 | 43 | 44 | 45 | 46 |
47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 |
80 | 81 | 82 | 83 | 84 |
85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 |

Methods

105 | 106 | 107 |
108 | 109 | 110 | 111 |

112 | instantiate(wasmopt) 113 |

114 | 115 | 116 | 117 | 118 | 119 |
120 |

Loads the mp3.wasm and returns the main class Encoder.

121 |
122 | 123 | 124 | 125 | 126 | 127 |
128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 |
Source:
155 |
158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 |
166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 |
Parameters:
177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 235 | 236 | 237 | 246 | 247 | 248 | 249 | 254 | 255 | 256 | 260 | 261 | 262 | 263 | 264 |
NameTypeAttributesDefaultDescription
wasm 209 | 210 | 211 | 212 | 213 | string 214 | 215 | 216 | | 217 | 218 | 219 | 220 | URL 221 | 222 | 223 | | 224 | 225 | 226 | 227 | Request 228 | 229 | 230 | 231 | 232 | 233 | 234 | 238 | 239 | <optional> 240 | 241 | 242 | 243 | 244 | 245 | 250 | 251 | 'mp3.wasm' 252 | 253 | 257 |

WASM file URL.

258 | 259 |
265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 |
280 |
Returns:
281 | 282 | 283 |
284 |

Promise<class Encoder|Error>

285 |
286 | 287 | 288 | 289 | 290 | 291 | 292 |
293 | 294 | 295 | 296 |
297 | 298 | 299 | 300 | 301 | 302 | 303 |

Type Definitions

304 | 305 | 306 | 307 |
308 | 309 | 310 | 311 |

312 | EncoderOptions() 313 |

314 | 315 | 316 | 317 | 318 | 319 |
320 |

MP3 encoder options

321 |
322 | 323 | 324 | 325 | 326 |
Properties:
327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 427 | 428 | 429 | 430 | 431 | 432 | 435 | 436 | 437 | 438 | 439 | 440 | 441 | 442 | 443 | 444 | 445 | 446 | 459 | 460 | 461 | 462 | 463 | 464 | 465 | 466 | 467 | 468 | 469 | 470 | 471 | 472 | 473 | 474 | 475 | 476 | 489 | 490 | 491 | 492 | 493 | 494 | 495 | 496 | 497 | 498 | 499 | 500 | 501 |
NameTypeDescription
sampleRate 355 | 356 | 357 | 358 | 359 | number 360 | 361 | 362 | 363 | 364 | 365 | 366 |

Input/output sample rate. Usually this is the value from an AudioContext.

numChannels 385 | 386 | 387 | 388 | 389 | number 390 | 391 | 392 | 393 | 394 | 395 | 396 |

Number of input/output channels. MP3 supports 1 (mono) or 2 (stereo) channels

quality 415 | 416 | 417 | 418 | 419 | number 420 | 421 | 422 | 423 | 424 | 425 | 426 |

Encoding quality (0 - lowest, 9 - highest). 433 | In VBR (Variable Bit-Rate) this quality indicates an average kbps but in 434 | CBR (Constant Bit-Rate) 0 is equal to 32kbps and 9 is equal to 320kbps.

samples 447 | 448 | 449 | 450 | 451 | number 452 | 453 | 454 | 455 | 456 | 457 | 458 |

Number of samples that will be encoded each time encode is called.

mode 477 | 478 | 479 | 480 | 481 | EncoderMode 482 | 483 | 484 | 485 | 486 | 487 | 488 |

Encoding mode (0 - CBR, 1 - VBR).

502 | 503 | 504 | 505 |
506 | 507 | 508 | 509 | 510 | 511 | 512 | 513 | 514 | 515 | 516 | 517 | 518 | 519 | 520 | 521 | 522 | 523 | 524 | 525 | 526 | 527 | 528 | 529 | 530 | 531 | 532 |
Source:
533 |
536 | 537 | 538 | 539 | 540 | 541 | 542 | 543 |
544 | 545 | 546 | 547 | 548 | 549 |
550 |
551 | 552 | Type: 553 | 554 |
555 |
    556 |
  • 557 | 558 | 559 | 560 | Object 561 | 562 | 563 | 564 | 565 | 566 |
  • 567 |
568 |
569 | 570 | 571 | 572 | 573 | 574 | 575 | 576 | 577 | 578 | 579 | 580 | 581 | 582 | 583 | 584 | 585 | 586 | 587 | 588 | 589 |
590 | 591 | 592 | 593 | 594 | 595 | 596 |
597 | 598 |
599 | 600 | 601 | 602 | 603 |
604 |
605 | 608 | 609 | -------------------------------------------------------------------------------- /docs/@etercast/mp3/1.0.0/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Home 7 | 8 | 9 | 10 | 11 | 12 |
13 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 |
35 |
36 |

Etercast MP3 Encoder

37 |

Etercast

38 |

This repository contains part of the Etercast MP3 encoding library. It uses LAME and C/C++ -> WebAssembly compiler Emscripten.

39 |

How to use it

40 |
import instantiate from '@etercast/mp3'
 41 | 
 42 | // instantiate() function fetches and instantiates the WASM module
 43 | // and returns a Encoder class.
 44 | // If you upload the mp3.wasm to a CDN you can specify the
 45 | // WASM url by passing it to instantiate.
 46 | // const Encoder = await instantiate('https://mycdn.com/mp3.wasm')
 47 | const Encoder = await instantiate()
 48 | 
 49 | // You can also use Encoder.create()
 50 | const encoder = new Encoder({
 51 |   sampleRate: audioContext.sampleRate,
 52 |   samples: 2048,
 53 |   numChannels: 1
 54 | })
 55 | 
 56 | // leftChannelData must be a Float32Array with 2048 (the
 57 | // specified in the encoder samples option) samples.
 58 | // rightChannelData is optional (can be null or undefined).
 59 | const encodedMP3Frames = encoder.encode(leftChannelData, rightChannelData)
 60 | 
 61 | // Remaining MP3 encoded frames. It flushes available MP3
 62 | // frames not returned in the previous call.
 63 | const remainingEncodedMP3Frames = encoder.encode()
 64 | 
 65 | const blob = new Blob([
 66 |   encodedMP3Frames,
 67 |   remainingEncodedMP3Frames
 68 | ], {
 69 |   type: 'audio/mp3'
 70 | })
 71 | const url = URL.createObjectURL(blob)
 72 | const a = document.createElement('a')
 73 | a.href = url
 74 | a.download = 'audio.mp3'
 75 | a.dispatchEvent(new MouseEvent('click'))
 76 | 
77 |

You need to copy mp3.wasm from node_modules/@etercast/mp3/dist/mp3.wasm into your dist or public folder.

78 |

If you're using webpack you can use the 79 | CopyWebpackPlugin or WriteFileWebpackPlugin if you're using webpack-dev-server.

80 |

How to build it

81 |

If you have Emscripten installed locally then you can run:

82 |
make
 83 | 
84 |

Otherwise you can use the Docker image by trcezi to build the MP3 encoder by executing:

85 |
npm run build:emscripten
 86 | 
87 |

How to build documentation

88 |

Calling npm run build will build this documentation but if you want to build documentation specifically you can run:

89 |
npm run build:docs
 90 | 
91 |

Demo

92 |

If you want to see a live demo running, run:

93 |
npm run serve:examples
 94 | 
95 |

Made with :heart: by ROJO 2

96 |
97 |
98 | 99 | 100 | 101 | 102 | 103 | 104 |
105 |
106 | 109 | 110 | -------------------------------------------------------------------------------- /docs/@etercast/mp3/1.0.0/module-@etercast_mp3-Encoder.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Encoder - Documentation 7 | 8 | 9 | 10 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 23 | 24 | 25 | 26 | 29 | 30 |
31 | 32 |

Encoder

33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 |
41 | 42 |
43 | 44 |

45 | @etercast/mp3~ 46 | 47 | Encoder 48 |

49 | 50 |

MP3 encoder.

51 | 52 | 53 |
54 | 55 |
56 |
57 | 58 | 59 |
60 | 61 | 62 |

Constructor

63 | 64 | 65 |

new Encoder(options)

66 | 67 | 68 | 69 | 70 | 71 |
72 |

Constructor

73 |
74 | 75 | 76 | 77 | 78 | 79 |
80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 |
Source:
107 |
110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 |
118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 |
Parameters:
128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 163 | 164 | 165 | 166 | 167 | 168 | 172 | 173 | 174 | 175 | 176 |
NameTypeDescription
options 156 | 157 | 158 | EncoderOptions 159 | 160 | 161 | 162 | 169 | 170 | 171 |
177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 |
194 | 195 |
196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 |

Members

209 | 210 | 211 | 212 |
213 |

error :null|Error

214 | 215 | 216 | 217 | 218 |
219 |

Error

220 |
221 | 222 | 223 | 224 | 225 | 226 |
227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 |
Source:
254 |
257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 |
265 | 266 | 267 | 268 |
Type:
269 |
    270 |
  • 271 | 272 | null 273 | | 274 | 275 | Error 276 | 277 | 278 |
  • 279 |
280 | 281 | 282 | 283 | 284 | 285 |
286 | 287 | 288 | 289 |
290 |

inputChannels :number

291 | 292 | 293 | 294 | 295 |
296 |

Input number of channels (1 - mono, 2 - stereo)

297 |
298 | 299 | 300 | 301 | 302 | 303 |
304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 |
Source:
331 |
334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 |
342 | 343 | 344 | 345 |
Type:
346 |
    347 |
  • 348 | 349 | number 350 | 351 | 352 |
  • 353 |
354 | 355 | 356 | 357 | 358 | 359 |
360 | 361 | 362 | 363 |
364 |

inputSampleRate :number

365 | 366 | 367 | 368 | 369 |
370 |

Input sample rate

371 |
372 | 373 | 374 | 375 | 376 | 377 |
378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 |
Source:
405 |
408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 |
416 | 417 | 418 | 419 |
Type:
420 |
    421 |
  • 422 | 423 | number 424 | 425 | 426 |
  • 427 |
428 | 429 | 430 | 431 | 432 | 433 |
434 | 435 | 436 | 437 |
438 |

inputSamples :number

439 | 440 | 441 | 442 | 443 |
444 |

Input samples

445 |
446 | 447 | 448 | 449 | 450 | 451 |
452 | 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | 461 | 462 | 463 | 464 | 465 | 466 | 467 | 468 | 469 | 470 | 471 | 472 | 473 | 474 | 475 | 476 | 477 | 478 |
Source:
479 |
482 | 483 | 484 | 485 | 486 | 487 | 488 | 489 |
490 | 491 | 492 | 493 |
Type:
494 |
    495 |
  • 496 | 497 | number 498 | 499 | 500 |
  • 501 |
502 | 503 | 504 | 505 | 506 | 507 |
508 | 509 | 510 | 511 |
512 |

isClosed :boolean

513 | 514 | 515 | 516 | 517 |
518 |

Indicates that the encoder is running.

519 |
520 | 521 | 522 | 523 | 524 | 525 |
526 | 527 | 528 | 529 | 530 | 531 | 532 | 533 | 534 | 535 | 536 | 537 | 538 | 539 | 540 | 541 | 542 | 543 | 544 | 545 | 546 | 547 | 548 | 549 | 550 | 551 | 552 |
Source:
553 |
556 | 557 | 558 | 559 | 560 | 561 | 562 | 563 |
564 | 565 | 566 | 567 |
Type:
568 |
    569 |
  • 570 | 571 | boolean 572 | 573 | 574 |
  • 575 |
576 | 577 | 578 | 579 | 580 | 581 |
582 | 583 | 584 | 585 |
586 |

isRunning :boolean

587 | 588 | 589 | 590 | 591 |
592 |

Indicates that the encoder is running.

593 |
594 | 595 | 596 | 597 | 598 | 599 |
600 | 601 | 602 | 603 | 604 | 605 | 606 | 607 | 608 | 609 | 610 | 611 | 612 | 613 | 614 | 615 | 616 | 617 | 618 | 619 | 620 | 621 | 622 | 623 | 624 | 625 | 626 |
Source:
627 |
630 | 631 | 632 | 633 | 634 | 635 | 636 | 637 |
638 | 639 | 640 | 641 |
Type:
642 |
    643 |
  • 644 | 645 | boolean 646 | 647 | 648 |
  • 649 |
650 | 651 | 652 | 653 | 654 | 655 |
656 | 657 | 658 | 659 |
660 |

outputBufferMaxSize :number

661 | 662 | 663 | 664 | 665 |
666 |

Max. output buffer size.

667 |
668 | 669 | 670 | 671 | 672 | 673 |
674 | 675 | 676 | 677 | 678 | 679 | 680 | 681 | 682 | 683 | 684 | 685 | 686 | 687 | 688 | 689 | 690 | 691 | 692 | 693 | 694 | 695 | 696 | 697 | 698 | 699 | 700 |
Source:
701 |
704 | 705 | 706 | 707 | 708 | 709 | 710 | 711 |
712 | 713 | 714 | 715 |
Type:
716 |
    717 |
  • 718 | 719 | number 720 | 721 | 722 |
  • 723 |
724 | 725 | 726 | 727 | 728 | 729 |
730 | 731 | 732 | 733 |
734 |

outputBufferSize :number

735 | 736 | 737 | 738 | 739 |
740 |

Current output buffer size.

741 |
742 | 743 | 744 | 745 | 746 | 747 |
748 | 749 | 750 | 751 | 752 | 753 | 754 | 755 | 756 | 757 | 758 | 759 | 760 | 761 | 762 | 763 | 764 | 765 | 766 | 767 | 768 | 769 | 770 | 771 | 772 | 773 | 774 |
Source:
775 |
778 | 779 | 780 | 781 | 782 | 783 | 784 | 785 |
786 | 787 | 788 | 789 |
Type:
790 |
    791 |
  • 792 | 793 | number 794 | 795 | 796 |
  • 797 |
798 | 799 | 800 | 801 | 802 | 803 |
804 | 805 | 806 | 807 |
808 |

outputQuality :number

809 | 810 | 811 | 812 | 813 |
814 |

Output quality

815 |
816 | 817 | 818 | 819 | 820 | 821 |
822 | 823 | 824 | 825 | 826 | 827 | 828 | 829 | 830 | 831 | 832 | 833 | 834 | 835 | 836 | 837 | 838 | 839 | 840 | 841 | 842 | 843 | 844 | 845 | 846 | 847 | 848 |
Source:
849 |
852 | 853 | 854 | 855 | 856 | 857 | 858 | 859 |
860 | 861 | 862 | 863 |
Type:
864 |
    865 |
  • 866 | 867 | number 868 | 869 | 870 |
  • 871 |
872 | 873 | 874 | 875 | 876 | 877 |
878 | 879 | 880 | 881 |
882 |

outputSampleRate :number

883 | 884 | 885 | 886 | 887 |
888 |

Output sample rate

889 |
890 | 891 | 892 | 893 | 894 | 895 |
896 | 897 | 898 | 899 | 900 | 901 | 902 | 903 | 904 | 905 | 906 | 907 | 908 | 909 | 910 | 911 | 912 | 913 | 914 | 915 | 916 | 917 | 918 | 919 | 920 | 921 | 922 |
Source:
923 |
926 | 927 | 928 | 929 | 930 | 931 | 932 | 933 |
934 | 935 | 936 | 937 |
Type:
938 |
    939 |
  • 940 | 941 | number 942 | 943 | 944 |
  • 945 |
946 | 947 | 948 | 949 | 950 | 951 |
952 | 953 | 954 | 955 |
956 |

state :EncoderState

957 | 958 | 959 | 960 | 961 |
962 |

Encoder state

963 |
964 | 965 | 966 | 967 | 968 | 969 |
970 | 971 | 972 | 973 | 974 | 975 | 976 | 977 | 978 | 979 | 980 | 981 | 982 | 983 | 984 | 985 | 986 | 987 | 988 | 989 | 990 | 991 | 992 | 993 | 994 | 995 | 996 |
Source:
997 |
1000 | 1001 | 1002 | 1003 | 1004 | 1005 | 1006 | 1007 |
1008 | 1009 | 1010 | 1011 |
Type:
1012 |
    1013 |
  • 1014 | 1015 | EncoderState 1016 | 1017 | 1018 |
  • 1019 |
1020 | 1021 | 1022 | 1023 | 1024 | 1025 |
1026 | 1027 | 1028 | 1029 | 1030 | 1031 |

Methods

1032 | 1033 | 1034 | 1035 |
1036 | 1037 | 1038 | 1039 |

(static) create(options)

1040 | 1041 | 1042 | 1043 | 1044 | 1045 |
1046 |

Creates a new MP3 encoder.

1047 |
1048 | 1049 | 1050 | 1051 | 1052 | 1053 |
1054 | 1055 | 1056 | 1057 | 1058 | 1059 | 1060 | 1061 | 1062 | 1063 | 1064 | 1065 | 1066 | 1067 | 1068 | 1069 | 1070 | 1071 | 1072 | 1073 | 1074 | 1075 | 1076 | 1077 | 1078 | 1079 | 1080 |
Source:
1081 |
1084 | 1085 | 1086 | 1087 | 1088 | 1089 | 1090 | 1091 |
1092 | 1093 | 1094 | 1095 | 1096 | 1097 | 1098 | 1099 | 1100 | 1101 |
Parameters:
1102 | 1103 | 1104 | 1105 | 1106 | 1107 | 1108 | 1109 | 1110 | 1111 | 1112 | 1113 | 1114 | 1115 | 1116 | 1117 | 1118 | 1119 | 1120 | 1121 | 1122 | 1123 | 1124 | 1125 | 1126 | 1127 | 1128 | 1129 | 1137 | 1138 | 1139 | 1140 | 1141 | 1142 | 1146 | 1147 | 1148 | 1149 | 1150 |
NameTypeDescription
options 1130 | 1131 | 1132 | EncoderOptions 1133 | 1134 | 1135 | 1136 | 1143 | 1144 | 1145 |
1151 | 1152 | 1153 | 1154 | 1155 | 1156 | 1157 | 1158 | 1159 | 1160 | 1161 | 1162 | 1163 | 1164 | 1165 | 1166 | 1167 |
1168 | 1169 | 1170 |
1171 | 1172 | 1173 | 1174 |

close()

1175 | 1176 | 1177 | 1178 | 1179 | 1180 |
1181 |

Closes the encoder.

1182 |
1183 | 1184 | 1185 | 1186 | 1187 | 1188 |
1189 | 1190 | 1191 | 1192 | 1193 | 1194 | 1195 | 1196 | 1197 | 1198 | 1199 | 1200 | 1201 | 1202 | 1203 | 1204 | 1205 | 1206 | 1207 | 1208 | 1209 | 1210 | 1211 | 1212 | 1213 | 1214 | 1215 |
Source:
1216 |
1219 | 1220 | 1221 | 1222 | 1223 | 1224 | 1225 | 1226 |
1227 | 1228 | 1229 | 1230 | 1231 | 1232 | 1233 | 1234 | 1235 | 1236 | 1237 | 1238 | 1239 | 1240 | 1241 | 1242 | 1243 | 1244 | 1245 | 1246 | 1247 | 1248 | 1249 | 1250 |
1251 | 1252 | 1253 |
1254 | 1255 | 1256 | 1257 |

encode(left, rightopt) → {Uint8Array}

1258 | 1259 | 1260 | 1261 | 1262 | 1263 |
1264 |

Encodes raw float 32-bit audio data into MP3 frames

1265 |
1266 | 1267 | 1268 | 1269 | 1270 | 1271 |
1272 | 1273 | 1274 | 1275 | 1276 | 1277 | 1278 | 1279 | 1280 | 1281 | 1282 | 1283 | 1284 | 1285 | 1286 | 1287 | 1288 | 1289 | 1290 | 1291 | 1292 | 1293 | 1294 | 1295 | 1296 | 1297 | 1298 |
Source:
1299 |
1302 | 1303 | 1304 | 1305 | 1306 | 1307 | 1308 | 1309 |
1310 | 1311 | 1312 | 1313 | 1314 | 1315 | 1316 | 1317 | 1318 | 1319 |
Parameters:
1320 | 1321 | 1322 | 1323 | 1324 | 1325 | 1326 | 1327 | 1328 | 1329 | 1330 | 1331 | 1332 | 1333 | 1334 | 1335 | 1336 | 1337 | 1338 | 1339 | 1340 | 1341 | 1342 | 1343 | 1344 | 1345 | 1346 | 1347 | 1348 | 1349 | 1357 | 1358 | 1359 | 1366 | 1367 | 1368 | 1369 | 1370 | 1374 | 1375 | 1376 | 1377 | 1378 | 1379 | 1380 | 1381 | 1382 | 1383 | 1391 | 1392 | 1393 | 1402 | 1403 | 1404 | 1405 | 1406 | 1410 | 1411 | 1412 | 1413 | 1414 |
NameTypeAttributesDescription
left 1350 | 1351 | 1352 | Float32Array 1353 | 1354 | 1355 | 1356 | 1360 | 1361 | 1362 | 1363 | 1364 | 1365 | 1371 |

Left channel (mono)

1372 | 1373 |
right 1384 | 1385 | 1386 | Float32Array 1387 | 1388 | 1389 | 1390 | 1394 | 1395 | <optional>
1396 | 1397 | 1398 | 1399 | 1400 | 1401 |
1407 |

Right channel (stereo)

1408 | 1409 |
1415 | 1416 | 1417 | 1418 | 1419 | 1420 | 1421 | 1422 | 1423 | 1424 | 1425 | 1426 | 1427 | 1428 | 1429 |
1430 |
Returns:
1431 | 1432 | 1433 | 1434 |
1435 |
1436 | Type: 1437 |
1438 |
1439 | 1440 | Uint8Array 1441 | 1442 | 1443 |
1444 |
1445 | 1446 | 1447 |
1448 |
    1449 |
  • Returns a bunch of encoded frames
  • 1450 |
1451 |
1452 | 1453 | 1454 |
1455 | 1456 | 1457 | 1458 |
1459 | 1460 | 1461 | 1462 | 1463 | 1464 | 1465 |
1466 | 1467 |
1468 | 1469 | 1470 | 1471 | 1472 |
1473 | 1474 |
1475 | 1476 | 1479 | 1480 | 1481 | 1482 | 1483 | -------------------------------------------------------------------------------- /docs/@etercast/mp3/1.0.0/module-@etercast_mp3.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | @etercast/mp3 - Documentation 7 | 8 | 9 | 10 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 23 | 24 | 25 | 26 | 29 | 30 |
31 | 32 |

@etercast/mp3

33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 |
41 | 42 |
43 | 44 | 45 | 46 | 47 | 48 |
49 | 50 |
51 |
52 | 53 | 54 |

Loads the mp3.wasm and returns the main class Encoder.

55 | 56 | 57 | 58 | 59 |
60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 |
68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 |
Source:
95 |
98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 |
106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 |
130 | 131 | 132 |
133 | 134 | 135 | 136 | 137 | 138 | 139 |

Classes

140 | 141 |
142 |
Encoder
143 |
144 |
145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 |

Type Definitions

157 | 158 | 159 | 160 |
161 |

EncoderOptions

162 | 163 | 164 | 165 | 166 |
167 |

MP3 encoder options

168 |
169 | 170 | 171 | 172 | 173 | 174 |
Properties:
175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 |
NameTypeDescription
sampleRate 204 | 205 | 206 | number 207 | 208 | 209 | 210 |

Input/output sample rate

numChannels 229 | 230 | 231 | number 232 | 233 | 234 | 235 |

Number of input/output channels

quality 254 | 255 | 256 | number 257 | 258 | 259 | 260 |

Encoding quality (0 - lowest, 9 - highest)

samples 279 | 280 | 281 | number 282 | 283 | 284 | 285 |

Number of samples that will be encoded each time encode is called.

mode 304 | 305 | 306 | number 307 | 308 | 309 | 310 |

Encoding mode (0 - CBR, 1 - VBR)

324 | 325 | 326 | 327 | 328 |
329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 |
Source:
356 |
359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 |
367 | 368 | 369 | 370 |
Type:
371 |
    372 |
  • 373 | 374 | Object 375 | 376 | 377 |
  • 378 |
379 | 380 | 381 | 382 | 383 | 384 |
385 | 386 | 387 | 388 | 389 | 390 |
391 | 392 |
393 | 394 | 395 | 396 | 397 |
398 | 399 |
400 | 401 | 404 | 405 | 406 | 407 | 408 | -------------------------------------------------------------------------------- /docs/@etercast/mp3/1.0.0/module-mp3-Encoder.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Encoder - Documentation 7 | 8 | 9 | 10 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 23 | 24 | 25 | 26 | 29 | 30 |
31 | 32 |

Encoder

33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 |
41 | 42 |
43 | 44 |

45 | mp3~ 46 | 47 | Encoder 48 |

49 | 50 |

MP3 encoder.

51 | 52 | 53 |
54 | 55 |
56 |
57 | 58 | 59 |
60 | 61 | 62 |

Constructor

63 | 64 | 65 |

new Encoder(options)

66 | 67 | 68 | 69 | 70 | 71 |
72 |

Constructor

73 |
74 | 75 | 76 | 77 | 78 | 79 |
80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 |
Source:
107 |
110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 |
118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 |
Parameters:
128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 163 | 164 | 165 | 166 | 167 | 168 | 172 | 173 | 174 | 175 | 176 |
NameTypeDescription
options 156 | 157 | 158 | EncoderOptions 159 | 160 | 161 | 162 | 169 | 170 | 171 |
177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 |
194 | 195 |
196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 |

Members

209 | 210 | 211 | 212 |
213 |

error :null|Error

214 | 215 | 216 | 217 | 218 |
219 |

Error

220 |
221 | 222 | 223 | 224 | 225 | 226 |
227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 |
Source:
254 |
257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 |
265 | 266 | 267 | 268 |
Type:
269 |
    270 |
  • 271 | 272 | null 273 | | 274 | 275 | Error 276 | 277 | 278 |
  • 279 |
280 | 281 | 282 | 283 | 284 | 285 |
286 | 287 | 288 | 289 |
290 |

inputChannels :number

291 | 292 | 293 | 294 | 295 |
296 |

Input number of channels (1 - mono, 2 - stereo)

297 |
298 | 299 | 300 | 301 | 302 | 303 |
304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 |
Source:
331 |
334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 |
342 | 343 | 344 | 345 |
Type:
346 |
    347 |
  • 348 | 349 | number 350 | 351 | 352 |
  • 353 |
354 | 355 | 356 | 357 | 358 | 359 |
360 | 361 | 362 | 363 |
364 |

inputSampleRate :number

365 | 366 | 367 | 368 | 369 |
370 |

Input sample rate

371 |
372 | 373 | 374 | 375 | 376 | 377 |
378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 |
Source:
405 |
408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 |
416 | 417 | 418 | 419 |
Type:
420 |
    421 |
  • 422 | 423 | number 424 | 425 | 426 |
  • 427 |
428 | 429 | 430 | 431 | 432 | 433 |
434 | 435 | 436 | 437 |
438 |

inputSamples :number

439 | 440 | 441 | 442 | 443 |
444 |

Input samples

445 |
446 | 447 | 448 | 449 | 450 | 451 |
452 | 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | 461 | 462 | 463 | 464 | 465 | 466 | 467 | 468 | 469 | 470 | 471 | 472 | 473 | 474 | 475 | 476 | 477 | 478 |
Source:
479 |
482 | 483 | 484 | 485 | 486 | 487 | 488 | 489 |
490 | 491 | 492 | 493 |
Type:
494 |
    495 |
  • 496 | 497 | number 498 | 499 | 500 |
  • 501 |
502 | 503 | 504 | 505 | 506 | 507 |
508 | 509 | 510 | 511 |
512 |

isClosed :boolean

513 | 514 | 515 | 516 | 517 |
518 |

Indicates that the encoder is running.

519 |
520 | 521 | 522 | 523 | 524 | 525 |
526 | 527 | 528 | 529 | 530 | 531 | 532 | 533 | 534 | 535 | 536 | 537 | 538 | 539 | 540 | 541 | 542 | 543 | 544 | 545 | 546 | 547 | 548 | 549 | 550 | 551 | 552 |
Source:
553 |
556 | 557 | 558 | 559 | 560 | 561 | 562 | 563 |
564 | 565 | 566 | 567 |
Type:
568 |
    569 |
  • 570 | 571 | boolean 572 | 573 | 574 |
  • 575 |
576 | 577 | 578 | 579 | 580 | 581 |
582 | 583 | 584 | 585 |
586 |

isRunning :boolean

587 | 588 | 589 | 590 | 591 |
592 |

Indicates that the encoder is running.

593 |
594 | 595 | 596 | 597 | 598 | 599 |
600 | 601 | 602 | 603 | 604 | 605 | 606 | 607 | 608 | 609 | 610 | 611 | 612 | 613 | 614 | 615 | 616 | 617 | 618 | 619 | 620 | 621 | 622 | 623 | 624 | 625 | 626 |
Source:
627 |
630 | 631 | 632 | 633 | 634 | 635 | 636 | 637 |
638 | 639 | 640 | 641 |
Type:
642 |
    643 |
  • 644 | 645 | boolean 646 | 647 | 648 |
  • 649 |
650 | 651 | 652 | 653 | 654 | 655 |
656 | 657 | 658 | 659 |
660 |

outputBufferMaxSize :number

661 | 662 | 663 | 664 | 665 |
666 |

Max. output buffer size.

667 |
668 | 669 | 670 | 671 | 672 | 673 |
674 | 675 | 676 | 677 | 678 | 679 | 680 | 681 | 682 | 683 | 684 | 685 | 686 | 687 | 688 | 689 | 690 | 691 | 692 | 693 | 694 | 695 | 696 | 697 | 698 | 699 | 700 |
Source:
701 |
704 | 705 | 706 | 707 | 708 | 709 | 710 | 711 |
712 | 713 | 714 | 715 |
Type:
716 |
    717 |
  • 718 | 719 | number 720 | 721 | 722 |
  • 723 |
724 | 725 | 726 | 727 | 728 | 729 |
730 | 731 | 732 | 733 |
734 |

outputBufferSize :number

735 | 736 | 737 | 738 | 739 |
740 |

Current output buffer size.

741 |
742 | 743 | 744 | 745 | 746 | 747 |
748 | 749 | 750 | 751 | 752 | 753 | 754 | 755 | 756 | 757 | 758 | 759 | 760 | 761 | 762 | 763 | 764 | 765 | 766 | 767 | 768 | 769 | 770 | 771 | 772 | 773 | 774 |
Source:
775 |
778 | 779 | 780 | 781 | 782 | 783 | 784 | 785 |
786 | 787 | 788 | 789 |
Type:
790 |
    791 |
  • 792 | 793 | number 794 | 795 | 796 |
  • 797 |
798 | 799 | 800 | 801 | 802 | 803 |
804 | 805 | 806 | 807 |
808 |

outputQuality :number

809 | 810 | 811 | 812 | 813 |
814 |

Output quality

815 |
816 | 817 | 818 | 819 | 820 | 821 |
822 | 823 | 824 | 825 | 826 | 827 | 828 | 829 | 830 | 831 | 832 | 833 | 834 | 835 | 836 | 837 | 838 | 839 | 840 | 841 | 842 | 843 | 844 | 845 | 846 | 847 | 848 |
Source:
849 |
852 | 853 | 854 | 855 | 856 | 857 | 858 | 859 |
860 | 861 | 862 | 863 |
Type:
864 |
    865 |
  • 866 | 867 | number 868 | 869 | 870 |
  • 871 |
872 | 873 | 874 | 875 | 876 | 877 |
878 | 879 | 880 | 881 |
882 |

outputSampleRate :number

883 | 884 | 885 | 886 | 887 |
888 |

Output sample rate

889 |
890 | 891 | 892 | 893 | 894 | 895 |
896 | 897 | 898 | 899 | 900 | 901 | 902 | 903 | 904 | 905 | 906 | 907 | 908 | 909 | 910 | 911 | 912 | 913 | 914 | 915 | 916 | 917 | 918 | 919 | 920 | 921 | 922 |
Source:
923 |
926 | 927 | 928 | 929 | 930 | 931 | 932 | 933 |
934 | 935 | 936 | 937 |
Type:
938 |
    939 |
  • 940 | 941 | number 942 | 943 | 944 |
  • 945 |
946 | 947 | 948 | 949 | 950 | 951 |
952 | 953 | 954 | 955 |
956 |

state :EncoderState

957 | 958 | 959 | 960 | 961 |
962 |

Encoder state

963 |
964 | 965 | 966 | 967 | 968 | 969 |
970 | 971 | 972 | 973 | 974 | 975 | 976 | 977 | 978 | 979 | 980 | 981 | 982 | 983 | 984 | 985 | 986 | 987 | 988 | 989 | 990 | 991 | 992 | 993 | 994 | 995 | 996 |
Source:
997 |
1000 | 1001 | 1002 | 1003 | 1004 | 1005 | 1006 | 1007 |
1008 | 1009 | 1010 | 1011 |
Type:
1012 |
    1013 |
  • 1014 | 1015 | EncoderState 1016 | 1017 | 1018 |
  • 1019 |
1020 | 1021 | 1022 | 1023 | 1024 | 1025 |
1026 | 1027 | 1028 | 1029 | 1030 | 1031 |

Methods

1032 | 1033 | 1034 | 1035 |
1036 | 1037 | 1038 | 1039 |

(static) create(options)

1040 | 1041 | 1042 | 1043 | 1044 | 1045 |
1046 |

Creates a new MP3 encoder.

1047 |
1048 | 1049 | 1050 | 1051 | 1052 | 1053 |
1054 | 1055 | 1056 | 1057 | 1058 | 1059 | 1060 | 1061 | 1062 | 1063 | 1064 | 1065 | 1066 | 1067 | 1068 | 1069 | 1070 | 1071 | 1072 | 1073 | 1074 | 1075 | 1076 | 1077 | 1078 | 1079 | 1080 |
Source:
1081 |
1084 | 1085 | 1086 | 1087 | 1088 | 1089 | 1090 | 1091 |
1092 | 1093 | 1094 | 1095 | 1096 | 1097 | 1098 | 1099 | 1100 | 1101 |
Parameters:
1102 | 1103 | 1104 | 1105 | 1106 | 1107 | 1108 | 1109 | 1110 | 1111 | 1112 | 1113 | 1114 | 1115 | 1116 | 1117 | 1118 | 1119 | 1120 | 1121 | 1122 | 1123 | 1124 | 1125 | 1126 | 1127 | 1128 | 1129 | 1137 | 1138 | 1139 | 1140 | 1141 | 1142 | 1146 | 1147 | 1148 | 1149 | 1150 |
NameTypeDescription
options 1130 | 1131 | 1132 | EncoderOptions 1133 | 1134 | 1135 | 1136 | 1143 | 1144 | 1145 |
1151 | 1152 | 1153 | 1154 | 1155 | 1156 | 1157 | 1158 | 1159 | 1160 | 1161 | 1162 | 1163 | 1164 | 1165 | 1166 | 1167 |
1168 | 1169 | 1170 |
1171 | 1172 | 1173 | 1174 |

close()

1175 | 1176 | 1177 | 1178 | 1179 | 1180 |
1181 |

Closes the encoder.

1182 |
1183 | 1184 | 1185 | 1186 | 1187 | 1188 |
1189 | 1190 | 1191 | 1192 | 1193 | 1194 | 1195 | 1196 | 1197 | 1198 | 1199 | 1200 | 1201 | 1202 | 1203 | 1204 | 1205 | 1206 | 1207 | 1208 | 1209 | 1210 | 1211 | 1212 | 1213 | 1214 | 1215 |
Source:
1216 |
1219 | 1220 | 1221 | 1222 | 1223 | 1224 | 1225 | 1226 |
1227 | 1228 | 1229 | 1230 | 1231 | 1232 | 1233 | 1234 | 1235 | 1236 | 1237 | 1238 | 1239 | 1240 | 1241 | 1242 | 1243 | 1244 | 1245 | 1246 | 1247 | 1248 | 1249 | 1250 |
1251 | 1252 | 1253 |
1254 | 1255 | 1256 | 1257 |

encode(left, rightopt) → {Uint8Array}

1258 | 1259 | 1260 | 1261 | 1262 | 1263 |
1264 |

Encodes raw float 32-bit audio data into MP3 frames

1265 |
1266 | 1267 | 1268 | 1269 | 1270 | 1271 |
1272 | 1273 | 1274 | 1275 | 1276 | 1277 | 1278 | 1279 | 1280 | 1281 | 1282 | 1283 | 1284 | 1285 | 1286 | 1287 | 1288 | 1289 | 1290 | 1291 | 1292 | 1293 | 1294 | 1295 | 1296 | 1297 | 1298 |
Source:
1299 |
1302 | 1303 | 1304 | 1305 | 1306 | 1307 | 1308 | 1309 |
1310 | 1311 | 1312 | 1313 | 1314 | 1315 | 1316 | 1317 | 1318 | 1319 |
Parameters:
1320 | 1321 | 1322 | 1323 | 1324 | 1325 | 1326 | 1327 | 1328 | 1329 | 1330 | 1331 | 1332 | 1333 | 1334 | 1335 | 1336 | 1337 | 1338 | 1339 | 1340 | 1341 | 1342 | 1343 | 1344 | 1345 | 1346 | 1347 | 1348 | 1349 | 1357 | 1358 | 1359 | 1366 | 1367 | 1368 | 1369 | 1370 | 1374 | 1375 | 1376 | 1377 | 1378 | 1379 | 1380 | 1381 | 1382 | 1383 | 1391 | 1392 | 1393 | 1402 | 1403 | 1404 | 1405 | 1406 | 1410 | 1411 | 1412 | 1413 | 1414 |
NameTypeAttributesDescription
left 1350 | 1351 | 1352 | Float32Array 1353 | 1354 | 1355 | 1356 | 1360 | 1361 | 1362 | 1363 | 1364 | 1365 | 1371 |

Left channel (mono)

1372 | 1373 |
right 1384 | 1385 | 1386 | Float32Array 1387 | 1388 | 1389 | 1390 | 1394 | 1395 | <optional>
1396 | 1397 | 1398 | 1399 | 1400 | 1401 |
1407 |

Right channel (stereo)

1408 | 1409 |
1415 | 1416 | 1417 | 1418 | 1419 | 1420 | 1421 | 1422 | 1423 | 1424 | 1425 | 1426 | 1427 | 1428 | 1429 |
1430 |
Returns:
1431 | 1432 | 1433 | 1434 |
1435 |
1436 | Type: 1437 |
1438 |
1439 | 1440 | Uint8Array 1441 | 1442 | 1443 |
1444 |
1445 | 1446 | 1447 |
1448 |
    1449 |
  • Returns a bunch of encoded frames
  • 1450 |
1451 |
1452 | 1453 | 1454 |
1455 | 1456 | 1457 | 1458 |
1459 | 1460 | 1461 | 1462 | 1463 | 1464 | 1465 |
1466 | 1467 |
1468 | 1469 | 1470 | 1471 | 1472 |
1473 | 1474 |
1475 | 1476 | 1479 | 1480 | 1481 | 1482 | 1483 | -------------------------------------------------------------------------------- /docs/@etercast/mp3/1.0.0/module-mp3.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | mp3 - Documentation 7 | 8 | 9 | 10 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 23 | 24 | 25 | 26 | 29 | 30 |
31 | 32 |

mp3

33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 |
41 | 42 |
43 | 44 | 45 | 46 | 47 | 48 |
49 | 50 |
51 |
52 | 53 | 54 |

Loads the mp3.wasm and returns the main class Encoder.

55 | 56 | 57 | 58 | 59 |
60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 |
68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 |
Source:
95 |
98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 |
106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 |
130 | 131 | 132 |
133 | 134 | 135 | 136 | 137 | 138 | 139 |

Classes

140 | 141 |
142 |
Encoder
143 |
144 |
145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 |

Type Definitions

157 | 158 | 159 | 160 |
161 |

EncoderOptions

162 | 163 | 164 | 165 | 166 |
167 |

MP3 encoder options

168 |
169 | 170 | 171 | 172 | 173 | 174 |
Properties:
175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 |
NameTypeDescription
sampleRate 204 | 205 | 206 | number 207 | 208 | 209 | 210 |

Input/output sample rate

numChannels 229 | 230 | 231 | number 232 | 233 | 234 | 235 |

Number of input/output channels

quality 254 | 255 | 256 | number 257 | 258 | 259 | 260 |

Encoding quality (0 - lowest, 9 - highest)

samples 279 | 280 | 281 | number 282 | 283 | 284 | 285 |

Number of samples that will be encoded each time encode is called.

mode 304 | 305 | 306 | number 307 | 308 | 309 | 310 |

Encoding mode (0 - CBR, 1 - VBR)

324 | 325 | 326 | 327 | 328 |
329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 |
Source:
356 |
359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 |
367 | 368 | 369 | 370 |
Type:
371 |
    372 |
  • 373 | 374 | Object 375 | 376 | 377 |
  • 378 |
379 | 380 | 381 | 382 | 383 | 384 |
385 | 386 | 387 | 388 | 389 | 390 |
391 | 392 |
393 | 394 | 395 | 396 | 397 |
398 | 399 |
400 | 401 | 404 | 405 | 406 | 407 | 408 | -------------------------------------------------------------------------------- /docs/@etercast/mp3/1.0.0/mp3.js.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | mp3.js 7 | 8 | 9 | 10 | 11 | 12 |
13 | 18 |
19 | 20 | 21 | 22 | 23 | 24 |
25 |
26 |
 27 |       
 28 |         /**
 29 |  * Loads the `mp3.wasm` and returns the main class `Encoder`.
 30 |  * @module @etercast/mp3
 31 |  *
 32 |  * @func instantiate
 33 |  * @param {string|URL|Request} [wasm='mp3.wasm'] WASM file URL.
 34 |  * @returns Promise<class Encoder|Error>
 35 |  */
 36 | export function instantiate(wasm = './mp3.wasm') {
 37 |   return fetch(wasm)
 38 |     .then((response) => response.arrayBuffer())
 39 |     .then((arrayBuffer) => WebAssembly.instantiate(arrayBuffer, {
 40 |       // TODO: We really don't need these imports because mp3.wasm
 41 |       // does not handle file descriptors.
 42 |       wasi_unstable: {
 43 |         fd_close() { console.log('fd_close') },
 44 |         fd_seek() { console.log('fd_seek') },
 45 |         fd_write() { console.log('fd_write') },
 46 |         proc_exit() { console.log('proc_exit') }
 47 |       }
 48 |     }))
 49 |     .then(({ instance }) => {
 50 |       // TODO: There's an extra export called _start that's not used
 51 |       // but is defined in WASI. Look for ways to remove this unnecessary
 52 |       // export.
 53 |       const {
 54 |         memory,
 55 |         mp3_create,
 56 |         mp3_init,
 57 |         mp3_encode,
 58 |         mp3_destroy
 59 |       } = instance.exports
 60 | 
 61 |       /**
 62 |        * Returns the error codes from the MP3 encoder.
 63 |        * @param {number} errorCode
 64 |        * @return {string}
 65 |        */
 66 |       function getEncoderError(errorCode) {
 67 |         switch (errorCode) {
 68 |           default:
 69 |             return `Unknown error: ${errorCode}`
 70 |           case -1:
 71 |             return 'Invalid input sample rate'
 72 |           case -2:
 73 |             return 'Invalid input channels (mono and stereo only)'
 74 |           case -3:
 75 |             return 'Invalid quality'
 76 |           case -4:
 77 |             return 'Error calling lame_init'
 78 |           case -5:
 79 |             return 'Error calling lame_init_params'
 80 |           case -6:
 81 |             return 'Error reallocating buffers'
 82 |           case -7:
 83 |             return 'Too much input samples'
 84 |           case -8:
 85 |             return 'Error calling lame_encode_buffer_ieee_float'
 86 |           case -9:
 87 |             return 'Error calling lame_encode_flush'
 88 |           case -10:
 89 |             return 'Invalid number of samples passed'
 90 |           case -11:
 91 |             return 'Invalid input samples'
 92 |           case -100:
 93 |             return 'Ok'
 94 |           case -101:
 95 |             return 'Error calling lame_encode_buffer_ieee_float: Buffer was too small'
 96 |           case -102:
 97 |             return 'Error calling lame_encode_buffer_ieee_float: malloc() problem'
 98 |           case -103:
 99 |             return 'Error calling lame_encode_buffer_ieee_float: lame_init_params not called'
100 |           case -104:
101 |             return 'Error calling lame_encode_buffer_ieee_float: Psycho acoustic problems'
102 |           case -110:
103 |             return 'Error calling lame_encode_buffer_ieee_float: No memory'
104 |           case -111:
105 |             return 'Error calling lame_encode_buffer_ieee_float: Bad bitrate'
106 |           case -112:
107 |             return 'Error calling lame_encode_buffer_ieee_float: Bad samplefreq'
108 |           case -113:
109 |             return 'Error calling lame_encode_buffer_ieee_float: Internal error'
110 |         }
111 |       }
112 | 
113 |       /**
114 |        * Encoder states.
115 |        * @readonly
116 |        * @enum {string}
117 |        */
118 |       const EncoderState = {
119 |         /** Indicates that the encoder is running and is capable of encode MP3 frames. */
120 |         RUNNING: 'running',
121 |         /** Indicates that the encoder was closed and no longer can encode MP3 frames. */
122 |         CLOSED: 'closed',
123 |         /** Indicates that something went wrong. */
124 |         ERROR: 'error'
125 |       }
126 | 
127 |       /**
128 |        * Encoder mode.
129 |        * @readonly
130 |        * @enum {number}
131 |        */
132 |       const EncoderMode = {
133 |         /** Constant bit-rate */
134 |         CBR: 0,
135 |         /** Variable bit-rate */
136 |         VBR: 1
137 |       }
138 | 
139 |       /**
140 |        * MP3 encoder options
141 |        * @typedef {Object} EncoderOptions
142 |        * @property {number} sampleRate Input/output sample rate. Usually this is the value from an AudioContext.
143 |        * @property {number} numChannels Number of input/output channels. MP3 supports 1 (mono) or 2 (stereo) channels
144 |        * @property {number} quality Encoding quality (0 - lowest, 9 - highest).
145 |        *                              In VBR (Variable Bit-Rate) this quality indicates an average kbps but in
146 |        *                              CBR (Constant Bit-Rate) 0 is equal to 32kbps and 9 is equal to 320kbps.
147 |        * @property {number} samples Number of samples that will be encoded each time `encode` is called.
148 |        * @property {EncoderMode} mode Encoding mode (0 - CBR, 1 - VBR).
149 |        */
150 | 
151 |       /**
152 |        * Not exported by default in the module, you need
153 |        * to call `instantiate` to retrieve this class.
154 |        *
155 |        * @example
156 |        * import instantiate from '@etercast/mp3'
157 |        *
158 |        * const Encoder = await instantiate()
159 |        * const encoder = new Encoder(encoderOptions)
160 |        * encoder.encode(leftChannelData, rightChannelData)
161 |        * encoder.close()
162 |        */
163 |       class Encoder {
164 |         /**
165 |          * Creates a new MP3 encoder. This is equivalent to calling
166 |          * the constructor using the `new` keyword. It's useful
167 |          * if you need a function that instantiates the Encoder.
168 |          * @param {EncoderOptions} options
169 |          * @example
170 |          * import instantiate from '@etercast/mp3'
171 |          *
172 |          * const Encoder = await instantiate()
173 |          * const encoder = Encoder.create(encoderOptions)
174 |          * @returns {Encoder}
175 |          */
176 |         static create(options) {
177 |           return new Encoder(options)
178 |         }
179 | 
180 |         /**
181 |          * Internally this calls the exported method `mp3_create` to
182 |          * make WASM module create a new structure to hold all the LAME
183 |          * encoding data. It also calls `mp3_init` to establish encoder
184 |          * options like number of channels, quality, sampleRate, etc.
185 |          * @param {EncoderOptions} options
186 |          */
187 |         constructor(options) {
188 |           const {
189 |             sampleRate,
190 |             numChannels,
191 |             quality,
192 |             samples,
193 |             mode,
194 |           } = {
195 |             sampleRate: 44100,
196 |             numChannels: 1,
197 |             quality: 9,
198 |             samples: 2048,
199 |             mode: 0,
200 |             ...options
201 |           }
202 |           this._error = null
203 |           this._state = EncoderState.RUNNING
204 |           this._pointer = null
205 |           const pointer = mp3_create()
206 |           if (!pointer) {
207 |             return this._throw(new Error('Cannot create mp3 encoder'))
208 |           }
209 |           this._pointer = pointer
210 |           const internalRegisters = 10
211 |           const internal = new Int32Array(memory.buffer, pointer, internalRegisters)
212 |           this._internal = internal
213 |           const errorCode = mp3_init(this._pointer, sampleRate, numChannels, quality, samples, mode)
214 |           if (errorCode < 0) {
215 |             return this._throw(new Error(getEncoderError(errorCode)))
216 |           }
217 | 
218 |           const [, outputBufferMaxSize, , , inputSamples, , , inputBufferLeftPointer, inputBufferRightPointer, outputBufferPointer] = internal
219 |           this._inputBufferLeft = new Float32Array(memory.buffer, inputBufferLeftPointer, inputSamples)
220 |           this._inputBufferRight = new Float32Array(memory.buffer, inputBufferRightPointer, inputSamples)
221 |           this._outputBuffer = new Uint8Array(memory.buffer, outputBufferPointer, outputBufferMaxSize)
222 |         }
223 | 
224 |         /**
225 |          * Encoder state
226 |          * @type {EncoderState}
227 |          */
228 |         get state() {
229 |           return this._state
230 |         }
231 | 
232 |         /**
233 |          * Error
234 |          * @type {null|Error}
235 |          */
236 |         get error() {
237 |           return this._error
238 |         }
239 | 
240 |         /**
241 |          * Current output buffer size.
242 |          * @type {number}
243 |          */
244 |         get outputBufferSize() {
245 |           return this._internal[0]
246 |         }
247 | 
248 |         /**
249 |          * Max. output buffer size.
250 |          * @type {number}
251 |          */
252 |         get outputBufferMaxSize() {
253 |           return this._internal[1]
254 |         }
255 | 
256 |         /**
257 |          * Output sample rate
258 |          * @type {number}
259 |          */
260 |         get outputSampleRate() {
261 |           return this._internal[2]
262 |         }
263 | 
264 |         /**
265 |          * Output quality
266 |          * @type {number}
267 |          */
268 |         get outputQuality() {
269 |           return this._internal[3]
270 |         }
271 | 
272 |         /**
273 |          * Input samples
274 |          * @type {number}
275 |          */
276 |         get inputSamples() {
277 |           return this._internal[4]
278 |         }
279 | 
280 |         /**
281 |          * Input sample rate
282 |          * @type {number}
283 |          */
284 |         get inputSampleRate() {
285 |           return this._internal[5]
286 |         }
287 | 
288 |         /**
289 |          * Input number of channels (1 - mono, 2 - stereo)
290 |          * @type {number}
291 |          */
292 |         get inputChannels() {
293 |           return this._internal[6]
294 |         }
295 | 
296 |         /**
297 |          * Indicates that the encoder is running.
298 |          * @type {boolean}
299 |          */
300 |         get isRunning() {
301 |           return this._state === EncoderState.RUNNING
302 |         }
303 | 
304 |         /**
305 |          * Indicates that the encoder is running.
306 |          * @type {boolean}
307 |          */
308 |         get isClosed() {
309 |           return this._state === EncoderState.CLOSED
310 |         }
311 | 
312 |         /**
313 |          * Throws an error
314 |          * @private
315 |          * @param {Error} error
316 |          */
317 |         _throw(error) {
318 |           this._state = EncoderState.ERROR
319 |           this._error = error
320 |           throw error
321 |         }
322 | 
323 |         /**
324 |          * Encodes raw Float 32-bit audio data into MP3 frames. An MP3
325 |          * frame consist of a header and payload, this makes MP3 streamable
326 |          * and implies that you can merge two files easily by concatenating them.
327 |          *
328 |          * If you need to merge two channels then you should use a `ChannelMergeNode`
329 |          * and then reencode the audio.
330 |          *
331 |          * @example
332 |          * import instantiate from '@etercast/mp3'
333 |          *
334 |          * const Encoder = await instantiate()
335 |          * const encoder = new Encoder({
336 |          *   sampleRate,
337 |          *   samples,
338 |          *   numChannels,
339 |          *   quality,
340 |          *   mode
341 |          * })
342 |          * encoder.encode(leftChannelData, rightChannelData)
343 |          *
344 |          * @param {Float32Array} left Left channel (mono)
345 |          * @param {Float32Array} [right] Right channel (stereo)
346 |          * @returns {Uint8Array} Returns a bunch of encoded frames
347 |          */
348 |         encode(left, right) {
349 |           if (this._state !== EncoderState.RUNNING) {
350 |             this._throw(new Error('Encoder already closed'))
351 |           }
352 |           let samples = 0
353 |           if (left) {
354 |             samples = left.length
355 |             this._inputBufferLeft.set(left)
356 |             if (right) {
357 |               if (samples !== right.length) {
358 |                 this._throw(new Error('Encoder channels have different lengths'))
359 |               }
360 |               this._inputBufferRight.set(right)
361 |             }
362 |           }
363 |           // Codifica el MP3.
364 |           const errorCode = mp3_encode(this._pointer, samples)
365 |           if (errorCode < 0) {
366 |             this._throw(new Error(getEncoderError(errorCode)))
367 |           }
368 |           return this._outputBuffer.slice(0, this.outputBufferSize)
369 |         }
370 | 
371 |         /**
372 |          * Closes the encoder. After this method any call to `encode`
373 |          * will throw an Error. If you need to append more data after
374 |          * closing an encoder you can instantiate a new Encoder and then
375 |          * concatenate all the new data with the old data.
376 |          *
377 |          * @example
378 |          * import instantiate from '@etercast/mp3'
379 |          *
380 |          * const Encoder = await instantiate()
381 |          * const encoder = new Encoder()
382 |          *
383 |          * // Do something with the encoder...
384 |          *
385 |          * encoder.close()
386 |          *
387 |          * // `encoder` is no longer usable and all the
388 |          * // memory reserved is freed.
389 |          */
390 |         close() {
391 |           if (this._state !== EncoderState.RUNNING) {
392 |             this._throw(new Error('Encoder already closed'))
393 |           }
394 | 
395 |           const errorCode = mp3_destroy(this._pointer)
396 |           if (errorCode < 0) {
397 |             this._throw(new Error(getEncoderError(errorCode)))
398 |           }
399 |           this._inputBufferLeft = null
400 |           this._inputBufferRight = null
401 |           this._outputBuffer = null
402 |           this._pointer = null
403 |           this._internal = null
404 |           this._state = EncoderState.CLOSED
405 |         }
406 |       }
407 | 
408 |       return Encoder
409 |     })
410 | }
411 | 
412 | export default instantiate
413 | 
414 |       
415 |     
416 |
417 |
418 | 419 | 420 | 421 | 422 | 423 |
424 |
425 | 428 | 429 | 430 | -------------------------------------------------------------------------------- /docs/@etercast/mp3/1.0.0/scripts/linenumber.js: -------------------------------------------------------------------------------- 1 | /*global document */ 2 | (function() { 3 | var source = document.getElementsByClassName('prettyprint source linenums'); 4 | var i = 0; 5 | var lineNumber = 0; 6 | var lineId; 7 | var lines; 8 | var totalLines; 9 | var anchorHash; 10 | 11 | if (source && source[0]) { 12 | anchorHash = document.location.hash.substring(1); 13 | lines = source[0].getElementsByTagName('li'); 14 | totalLines = lines.length; 15 | 16 | for (; i < totalLines; i++) { 17 | lineNumber++; 18 | lineId = 'line' + lineNumber; 19 | lines[i].id = lineId; 20 | if (lineId === anchorHash) { 21 | lines[i].className += ' selected'; 22 | } 23 | } 24 | } 25 | })(); 26 | -------------------------------------------------------------------------------- /docs/@etercast/mp3/1.0.0/scripts/prettify/Apache-License-2.0.txt: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /docs/@etercast/mp3/1.0.0/scripts/prettify/lang-css.js: -------------------------------------------------------------------------------- 1 | PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\f\r ]+/,null," \t\r\n "]],[["str",/^"(?:[^\n\f\r"\\]|\\(?:\r\n?|\n|\f)|\\[\S\s])*"/,null],["str",/^'(?:[^\n\f\r'\\]|\\(?:\r\n?|\n|\f)|\\[\S\s])*'/,null],["lang-css-str",/^url\(([^"')]*)\)/i],["kwd",/^(?:url|rgb|!important|@import|@page|@media|@charset|inherit)(?=[^\w-]|$)/i,null],["lang-css-kw",/^(-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*)\s*:/i],["com",/^\/\*[^*]*\*+(?:[^*/][^*]*\*+)*\//],["com", 2 | /^(?:<\!--|--\>)/],["lit",/^(?:\d+|\d*\.\d+)(?:%|[a-z]+)?/i],["lit",/^#[\da-f]{3,6}/i],["pln",/^-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*/i],["pun",/^[^\s\w"']+/]]),["css"]);PR.registerLangHandler(PR.createSimpleLexer([],[["kwd",/^-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*/i]]),["css-kw"]);PR.registerLangHandler(PR.createSimpleLexer([],[["str",/^[^"')]+/]]),["css-str"]); 3 | -------------------------------------------------------------------------------- /docs/@etercast/mp3/1.0.0/scripts/prettify/prettify.js: -------------------------------------------------------------------------------- 1 | var q=null;window.PR_SHOULD_USE_CONTINUATION=!0; 2 | (function(){function L(a){function m(a){var f=a.charCodeAt(0);if(f!==92)return f;var b=a.charAt(1);return(f=r[b])?f:"0"<=b&&b<="7"?parseInt(a.substring(1),8):b==="u"||b==="x"?parseInt(a.substring(2),16):a.charCodeAt(1)}function e(a){if(a<32)return(a<16?"\\x0":"\\x")+a.toString(16);a=String.fromCharCode(a);if(a==="\\"||a==="-"||a==="["||a==="]")a="\\"+a;return a}function h(a){for(var f=a.substring(1,a.length-1).match(/\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\[0-3][0-7]{0,2}|\\[0-7]{1,2}|\\[\S\s]|[^\\]/g),a= 3 | [],b=[],o=f[0]==="^",c=o?1:0,i=f.length;c122||(d<65||j>90||b.push([Math.max(65,j)|32,Math.min(d,90)|32]),d<97||j>122||b.push([Math.max(97,j)&-33,Math.min(d,122)&-33]))}}b.sort(function(a,f){return a[0]-f[0]||f[1]-a[1]});f=[];j=[NaN,NaN];for(c=0;ci[0]&&(i[1]+1>i[0]&&b.push("-"),b.push(e(i[1])));b.push("]");return b.join("")}function y(a){for(var f=a.source.match(/\[(?:[^\\\]]|\\[\S\s])*]|\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\\d+|\\[^\dux]|\(\?[!:=]|[()^]|[^()[\\^]+/g),b=f.length,d=[],c=0,i=0;c=2&&a==="["?f[c]=h(j):a!=="\\"&&(f[c]=j.replace(/[A-Za-z]/g,function(a){a=a.charCodeAt(0);return"["+String.fromCharCode(a&-33,a|32)+"]"}));return f.join("")}for(var t=0,s=!1,l=!1,p=0,d=a.length;p=5&&"lang-"===b.substring(0,5))&&!(o&&typeof o[1]==="string"))c=!1,b="src";c||(r[f]=b)}i=d;d+=f.length;if(c){c=o[1];var j=f.indexOf(c),k=j+c.length;o[2]&&(k=f.length-o[2].length,j=k-c.length);b=b.substring(5);B(l+i,f.substring(0,j),e,p);B(l+i+j,c,C(b,c),p);B(l+i+k,f.substring(k),e,p)}else p.push(l+i,b)}a.e=p}var h={},y;(function(){for(var e=a.concat(m), 9 | l=[],p={},d=0,g=e.length;d=0;)h[n.charAt(k)]=r;r=r[1];n=""+r;p.hasOwnProperty(n)||(l.push(r),p[n]=q)}l.push(/[\S\s]/);y=L(l)})();var t=m.length;return e}function u(a){var m=[],e=[];a.tripleQuotedStrings?m.push(["str",/^(?:'''(?:[^'\\]|\\[\S\s]|''?(?=[^']))*(?:'''|$)|"""(?:[^"\\]|\\[\S\s]|""?(?=[^"]))*(?:"""|$)|'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$))/,q,"'\""]):a.multiLineStrings?m.push(["str",/^(?:'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$)|`(?:[^\\`]|\\[\S\s])*(?:`|$))/, 10 | q,"'\"`"]):m.push(["str",/^(?:'(?:[^\n\r'\\]|\\.)*(?:'|$)|"(?:[^\n\r"\\]|\\.)*(?:"|$))/,q,"\"'"]);a.verbatimStrings&&e.push(["str",/^@"(?:[^"]|"")*(?:"|$)/,q]);var h=a.hashComments;h&&(a.cStyleComments?(h>1?m.push(["com",/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,q,"#"]):m.push(["com",/^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\n\r]*)/,q,"#"]),e.push(["str",/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,q])):m.push(["com",/^#[^\n\r]*/, 11 | q,"#"]));a.cStyleComments&&(e.push(["com",/^\/\/[^\n\r]*/,q]),e.push(["com",/^\/\*[\S\s]*?(?:\*\/|$)/,q]));a.regexLiterals&&e.push(["lang-regex",/^(?:^^\.?|[!+-]|!=|!==|#|%|%=|&|&&|&&=|&=|\(|\*|\*=|\+=|,|-=|->|\/|\/=|:|::|;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|[?@[^]|\^=|\^\^|\^\^=|{|\||\|=|\|\||\|\|=|~|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\s*(\/(?=[^*/])(?:[^/[\\]|\\[\S\s]|\[(?:[^\\\]]|\\[\S\s])*(?:]|$))+\/)/]);(h=a.types)&&e.push(["typ",h]);a=(""+a.keywords).replace(/^ | $/g, 12 | "");a.length&&e.push(["kwd",RegExp("^(?:"+a.replace(/[\s,]+/g,"|")+")\\b"),q]);m.push(["pln",/^\s+/,q," \r\n\t\xa0"]);e.push(["lit",/^@[$_a-z][\w$@]*/i,q],["typ",/^(?:[@_]?[A-Z]+[a-z][\w$@]*|\w+_t\b)/,q],["pln",/^[$_a-z][\w$@]*/i,q],["lit",/^(?:0x[\da-f]+|(?:\d(?:_\d+)*\d*(?:\.\d*)?|\.\d\+)(?:e[+-]?\d+)?)[a-z]*/i,q,"0123456789"],["pln",/^\\[\S\s]?/,q],["pun",/^.[^\s\w"-$'./@\\`]*/,q]);return x(m,e)}function D(a,m){function e(a){switch(a.nodeType){case 1:if(k.test(a.className))break;if("BR"===a.nodeName)h(a), 13 | a.parentNode&&a.parentNode.removeChild(a);else for(a=a.firstChild;a;a=a.nextSibling)e(a);break;case 3:case 4:if(p){var b=a.nodeValue,d=b.match(t);if(d){var c=b.substring(0,d.index);a.nodeValue=c;(b=b.substring(d.index+d[0].length))&&a.parentNode.insertBefore(s.createTextNode(b),a.nextSibling);h(a);c||a.parentNode.removeChild(a)}}}}function h(a){function b(a,d){var e=d?a.cloneNode(!1):a,f=a.parentNode;if(f){var f=b(f,1),g=a.nextSibling;f.appendChild(e);for(var h=g;h;h=g)g=h.nextSibling,f.appendChild(h)}return e} 14 | for(;!a.nextSibling;)if(a=a.parentNode,!a)return;for(var a=b(a.nextSibling,0),e;(e=a.parentNode)&&e.nodeType===1;)a=e;d.push(a)}var k=/(?:^|\s)nocode(?:\s|$)/,t=/\r\n?|\n/,s=a.ownerDocument,l;a.currentStyle?l=a.currentStyle.whiteSpace:window.getComputedStyle&&(l=s.defaultView.getComputedStyle(a,q).getPropertyValue("white-space"));var p=l&&"pre"===l.substring(0,3);for(l=s.createElement("LI");a.firstChild;)l.appendChild(a.firstChild);for(var d=[l],g=0;g=0;){var h=m[e];A.hasOwnProperty(h)?window.console&&console.warn("cannot override language handler %s",h):A[h]=a}}function C(a,m){if(!a||!A.hasOwnProperty(a))a=/^\s*=o&&(h+=2);e>=c&&(a+=2)}}catch(w){"console"in window&&console.log(w&&w.stack?w.stack:w)}}var v=["break,continue,do,else,for,if,return,while"],w=[[v,"auto,case,char,const,default,double,enum,extern,float,goto,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"], 18 | "catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof,async,await"],F=[w,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,dynamic_cast,explicit,export,friend,inline,late_check,mutable,namespace,nullptr,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"],G=[w,"abstract,boolean,byte,extends,final,finally,implements,import,instanceof,null,native,package,strictfp,super,synchronized,throws,transient"], 19 | H=[G,"as,base,by,checked,decimal,delegate,descending,dynamic,event,fixed,foreach,from,group,implicit,in,interface,internal,into,is,lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var"],w=[w,"debugger,eval,export,function,get,null,set,undefined,var,with,Infinity,NaN"],I=[v,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"], 20 | J=[v,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"],v=[v,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"],K=/^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)/,N=/\S/,O=u({keywords:[F,H,w,"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END"+ 21 | I,J,v],hashComments:!0,cStyleComments:!0,multiLineStrings:!0,regexLiterals:!0}),A={};k(O,["default-code"]);k(x([],[["pln",/^[^]*(?:>|$)/],["com",/^<\!--[\S\s]*?(?:--\>|$)/],["lang-",/^<\?([\S\s]+?)(?:\?>|$)/],["lang-",/^<%([\S\s]+?)(?:%>|$)/],["pun",/^(?:<[%?]|[%?]>)/],["lang-",/^]*>([\S\s]+?)<\/xmp\b[^>]*>/i],["lang-js",/^]*>([\S\s]*?)(<\/script\b[^>]*>)/i],["lang-css",/^]*>([\S\s]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i]]), 22 | ["default-markup","htm","html","mxml","xhtml","xml","xsl"]);k(x([["pln",/^\s+/,q," \t\r\n"],["atv",/^(?:"[^"]*"?|'[^']*'?)/,q,"\"'"]],[["tag",/^^<\/?[a-z](?:[\w-.:]*\w)?|\/?>$/i],["atn",/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],["lang-uq.val",/^=\s*([^\s"'>]*(?:[^\s"'/>]|\/(?=\s)))/],["pun",/^[/<->]+/],["lang-js",/^on\w+\s*=\s*"([^"]+)"/i],["lang-js",/^on\w+\s*=\s*'([^']+)'/i],["lang-js",/^on\w+\s*=\s*([^\s"'>]+)/i],["lang-css",/^style\s*=\s*"([^"]+)"/i],["lang-css",/^style\s*=\s*'([^']+)'/i],["lang-css", 23 | /^style\s*=\s*([^\s"'>]+)/i]]),["in.tag"]);k(x([],[["atv",/^[\S\s]+/]]),["uq.val"]);k(u({keywords:F,hashComments:!0,cStyleComments:!0,types:K}),["c","cc","cpp","cxx","cyc","m"]);k(u({keywords:"null,true,false"}),["json"]);k(u({keywords:H,hashComments:!0,cStyleComments:!0,verbatimStrings:!0,types:K}),["cs"]);k(u({keywords:G,cStyleComments:!0}),["java"]);k(u({keywords:v,hashComments:!0,multiLineStrings:!0}),["bsh","csh","sh"]);k(u({keywords:I,hashComments:!0,multiLineStrings:!0,tripleQuotedStrings:!0}), 24 | ["cv","py"]);k(u({keywords:"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END",hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["perl","pl","pm"]);k(u({keywords:J,hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["rb"]);k(u({keywords:w,cStyleComments:!0,regexLiterals:!0}),["js"]);k(u({keywords:"all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,true,try,unless,until,when,while,yes", 25 | hashComments:3,cStyleComments:!0,multilineStrings:!0,tripleQuotedStrings:!0,regexLiterals:!0}),["coffee"]);k(x([],[["str",/^[\S\s]+/]]),["regex"]);window.prettyPrintOne=function(a,m,e){var h=document.createElement("PRE");h.innerHTML=a;e&&D(h,e);E({g:m,i:e,h:h});return h.innerHTML};window.prettyPrint=function(a){function m(){for(var e=window.PR_SHOULD_USE_CONTINUATION?l.now()+250:Infinity;p=0){var k=k.match(g),f,b;if(b= 26 | !k){b=n;for(var o=void 0,c=b.firstChild;c;c=c.nextSibling)var i=c.nodeType,o=i===1?o?b:c:i===3?N.test(c.nodeValue)?b:o:o;b=(f=o===b?void 0:o)&&"CODE"===f.tagName}b&&(k=f.className.match(g));k&&(k=k[1]);b=!1;for(o=n.parentNode;o;o=o.parentNode)if((o.tagName==="pre"||o.tagName==="code"||o.tagName==="xmp")&&o.className&&o.className.indexOf("prettyprint")>=0){b=!0;break}b||((b=(b=n.className.match(/\blinenums\b(?::(\d+))?/))?b[1]&&b[1].length?+b[1]:!0:!1)&&D(n,b),d={g:k,h:n,i:b},E(d))}}p code { 119 | background: var(--source-code-background); 120 | color: var(--source-code-foreground); 121 | padding: .1rem .5rem; 122 | margin: .25rem; 123 | box-shadow: var(--box-shadow); 124 | } 125 | 126 | h1, h2, h3, h4, h5, h6 { 127 | color: var(--color--darkest); 128 | font-weight: 400; 129 | margin: 0; 130 | } 131 | 132 | h1 { 133 | font-weight: 300; 134 | font-size: 2.5rem; 135 | margin: 1em 0 .5em; 136 | } 137 | 138 | h1.page-title { 139 | margin-bottom: 10px; 140 | font-size: 2.25rem; 141 | font-weight: 300; 142 | border-bottom: solid 2px #ddd; 143 | padding: .5em 0 .5em; 144 | margin-top: 0; 145 | } 146 | 147 | h2 { 148 | font-size: 2rem; 149 | margin: 1.2em 0 .8em; 150 | font-weight: bold; 151 | } 152 | 153 | h3 { 154 | /* margin-top: 1em; */ 155 | /* margin-bottom: 16px; */ 156 | /* font-weight: bold; */ 157 | padding: 0; 158 | margin: 1em 0 .6em; 159 | font-size: 1.75rem; 160 | /* border-bottom: 1px solid #eee; */ 161 | /* padding-bottom: 15px; */ 162 | } 163 | 164 | h4 { 165 | font-size: 1.5rem; 166 | margin: 1em 0 .2em; 167 | color: #4d4e53; 168 | /* border-bottom: 1px solid #eee; */ 169 | padding-bottom: 8px; 170 | } 171 | 172 | h5, 173 | .container-overview .subsection-title { 174 | font-size: 120%; 175 | /* letter-spacing: -0.01em; */ 176 | margin: 20px 0 5px; 177 | } 178 | 179 | h6 { 180 | font-size: 100%; 181 | letter-spacing: -0.01em; 182 | margin: 6px 0 3px 0; 183 | font-style: italic; 184 | } 185 | 186 | tt, 187 | code, 188 | kbd, 189 | samp { 190 | font-family: var(--font-family-code); 191 | border-radius: var(--border-radius); 192 | } 193 | 194 | blockquote { 195 | display: block; 196 | border-left: 4px solid #eee; 197 | margin: 0; 198 | padding-left: 1em; 199 | color: #888; 200 | } 201 | 202 | header { 203 | display: block 204 | } 205 | 206 | section { 207 | display: block; 208 | padding: 0; 209 | } 210 | 211 | footer { 212 | color: hsl(0, 0%, 28%); 213 | display: block; 214 | padding: 2rem; 215 | padding-left: 20vw; 216 | font-style: italic; 217 | border-top: 1px solid #eee; 218 | } 219 | 220 | img { 221 | max-width: 100%; 222 | } 223 | 224 | main { 225 | margin-left: 20vw; 226 | width: 60vw; 227 | } 228 | 229 | aside { 230 | width: 20vw; 231 | padding: 1rem; 232 | } 233 | 234 | /** Nav **/ 235 | nav { 236 | position: fixed; 237 | } 238 | 239 | nav li { 240 | list-style: none; 241 | padding: 0; 242 | margin: 0; 243 | } 244 | 245 | .nav-heading { 246 | margin-top: 1rem; 247 | } 248 | 249 | .nav-item { 250 | margin-top: .25rem; 251 | } 252 | 253 | .nav-item-type { 254 | /* margin-left: 5px; */ 255 | display: inline-block; 256 | width: 2rem; 257 | height: 2rem; 258 | text-align: center; 259 | vertical-align: middle; 260 | border-radius: var(--border-radius-s); 261 | margin-right: .25rem; 262 | font-weight: bold; 263 | padding: .25rem; 264 | box-sizing: border-box; 265 | } 266 | 267 | .nav-item-name { 268 | display: inline-block; 269 | vertical-align: middle; 270 | } 271 | 272 | .type-function, 273 | .type-class, 274 | .type-member, 275 | .type-module, 276 | .type-typedef { 277 | /* 278 | background: hsl(var(--hue--base), 50%, 75%); 279 | color: hsl(var(--hue--base), 50%, 25%); 280 | */ 281 | background: var(--color--action); 282 | color: var(--color--light); 283 | } 284 | 285 | .class-description { 286 | margin-bottom: 1em; 287 | margin-top: 1em; 288 | } 289 | 290 | .class-description:empty { 291 | margin: 0 292 | } 293 | 294 | .variation { 295 | display: none 296 | } 297 | 298 | .signature-attributes { 299 | color: #aaa; 300 | font-style: italic; 301 | font-weight: lighter; 302 | font-size: 50%; 303 | } 304 | 305 | .ancestors { 306 | color: #999 307 | } 308 | 309 | .clear { 310 | clear: both 311 | } 312 | 313 | .important { 314 | font-weight: bold; 315 | color: #950B02; 316 | } 317 | 318 | .yes-def { 319 | text-indent: -1000px; 320 | } 321 | 322 | .type-signature { 323 | color: #aaa; 324 | } 325 | 326 | .name, 327 | .signature { 328 | font-family: Consolas, Monaco, 'Andale Mono', monospace; 329 | } 330 | 331 | .details { 332 | margin-top: 1rem; 333 | border-left: 2px solid #DDD; 334 | line-height: 1.125rem; 335 | } 336 | 337 | .details dt { 338 | width: 120px; 339 | float: left; 340 | padding-left: 1rem; 341 | } 342 | 343 | .details dd { 344 | margin-left: 70px; 345 | } 346 | 347 | .details ul { 348 | margin: 0; 349 | } 350 | 351 | .details ul { 352 | list-style-type: none; 353 | } 354 | 355 | .details li { 356 | margin-left: 30px; 357 | } 358 | 359 | .details .object-value { 360 | padding-top: 0; 361 | } 362 | 363 | .types { 364 | display: flex; 365 | flex-direction: row; 366 | align-items: center; 367 | } 368 | 369 | .type-title { 370 | display: inline-block; 371 | margin: 0 1rem 0 0; 372 | } 373 | 374 | .type-list { 375 | list-style: none; 376 | padding: 0; 377 | } 378 | 379 | .type-list li:after:not(:last-child) { 380 | content: '|'; 381 | } 382 | 383 | .type-list li { 384 | display: inline-block; 385 | } 386 | 387 | .description { 388 | margin-bottom: 1em; 389 | margin-top: 1em; 390 | } 391 | 392 | .code-caption { 393 | font-style: italic; 394 | margin: 0; 395 | } 396 | 397 | .params, 398 | .props { 399 | border-spacing: 0; 400 | border: 1px solid #ddd; 401 | border-collapse: collapse; 402 | box-shadow: var(--box-shadow); 403 | width: 100%; 404 | } 405 | 406 | .params .name, 407 | .props .name, 408 | .name code { 409 | color: #4D4E53; 410 | font-family: var(--font-family-code); 411 | } 412 | 413 | .params td, 414 | .params th, 415 | .props td, 416 | .props th { 417 | margin: 0px; 418 | text-align: left; 419 | vertical-align: top; 420 | padding: 10px; 421 | display: table-cell; 422 | } 423 | 424 | .params td { 425 | border-top: 1px solid #eee 426 | } 427 | 428 | .params thead tr, 429 | .props thead tr { 430 | background-color: #fff; 431 | font-weight: bold; 432 | } 433 | 434 | .params .params thead tr, 435 | .props .props thead tr { 436 | background-color: #fff; 437 | font-weight: bold; 438 | } 439 | 440 | .params td.description>p:first-child, 441 | .props td.description>p:first-child { 442 | margin-top: 0; 443 | padding-top: 0; 444 | } 445 | 446 | .params td.description>p:last-child, 447 | .props td.description>p:last-child { 448 | margin-bottom: 0; 449 | padding-bottom: 0; 450 | } 451 | 452 | dl.param-type { 453 | /* border-bottom: 1px solid hsl(0, 0%, 87%); */ 454 | margin: 0; 455 | padding: 0; 456 | font-size: 16px; 457 | } 458 | 459 | .param-type dt, 460 | .param-type dd { 461 | display: inline-block 462 | } 463 | 464 | .param-type dd { 465 | font-family: Consolas, Monaco, 'Andale Mono', monospace; 466 | display: inline-block; 467 | padding: 0; 468 | margin: 0; 469 | font-size: 14px; 470 | } 471 | 472 | .disabled { 473 | color: #454545 474 | } 475 | 476 | /* navicon button */ 477 | .navicon-button { 478 | display: none; 479 | position: relative; 480 | padding: 2.0625rem 1.5rem; 481 | transition: 0.25s; 482 | cursor: pointer; 483 | user-select: none; 484 | opacity: .8; 485 | } 486 | 487 | .navicon-button .navicon:before, 488 | .navicon-button .navicon:after { 489 | transition: 0.25s; 490 | } 491 | 492 | .navicon-button:hover { 493 | transition: 0.5s; 494 | opacity: 1; 495 | } 496 | 497 | .navicon-button:hover .navicon:before, 498 | .navicon-button:hover .navicon:after { 499 | transition: 0.25s; 500 | } 501 | 502 | .navicon-button:hover .navicon:before { 503 | top: .825rem; 504 | } 505 | 506 | .navicon-button:hover .navicon:after { 507 | top: -.825rem; 508 | } 509 | 510 | /* navicon */ 511 | .navicon { 512 | position: relative; 513 | width: 2.5em; 514 | height: .3125rem; 515 | background: #000; 516 | transition: 0.3s; 517 | border-radius: 2.5rem; 518 | } 519 | 520 | .navicon:before, 521 | .navicon:after { 522 | display: block; 523 | content: ""; 524 | height: .3125rem; 525 | width: 2.5rem; 526 | background: #000; 527 | position: absolute; 528 | z-index: -1; 529 | transition: 0.3s 0.25s; 530 | border-radius: 1rem; 531 | } 532 | 533 | .navicon:before { 534 | top: .625rem; 535 | } 536 | 537 | .navicon:after { 538 | top: -.625rem; 539 | } 540 | 541 | .overlay { 542 | display: none; 543 | position: fixed; 544 | top: 0; 545 | bottom: 0; 546 | left: 0; 547 | right: 0; 548 | width: 100%; 549 | height: 100%; 550 | background: hsla(0, 0%, 0%, 0.5); 551 | z-index: 1; 552 | } 553 | 554 | .section-method { 555 | margin-bottom: 30px; 556 | padding-bottom: 30px; 557 | border-bottom: 1px solid #eee; 558 | } 559 | 560 | pre.prettyprint { 561 | font-family: var(--font-family-code); 562 | padding: .5rem 1rem; 563 | margin: 0; 564 | border-radius: var(--border-radius); 565 | border: 0; 566 | background: var(--source-code-background); 567 | box-shadow: var(--box-shadow); 568 | overflow-x: auto; 569 | } 570 | 571 | ol.linenums { 572 | margin-top: 0; 573 | margin-bottom: 0; 574 | color: #655f6d; 575 | } 576 | 577 | li.L0, 578 | li.L1, 579 | li.L2, 580 | li.L3, 581 | li.L4, 582 | li.L5, 583 | li.L6, 584 | li.L7, 585 | li.L8, 586 | li.L9 { 587 | padding-left: 1em; 588 | background-color: #19171c; 589 | list-style-type: decimal !important; 590 | } 591 | 592 | /* plain text */ 593 | .pln { 594 | color: var(--source-code-foreground); 595 | } 596 | /* string content */ 597 | .str { 598 | color: var(--source-code-string); 599 | } 600 | /* keyword */ 601 | .kwd { 602 | color: var(--source-code-keyword); 603 | } 604 | /* comment */ 605 | .com { 606 | color: var(--source-code-comment); 607 | } 608 | /* type name */ 609 | .typ { 610 | color: var(--source-code-type); 611 | } 612 | /* literal value */ 613 | .lit { 614 | color: var(--source-code-literal); 615 | } 616 | /* punctuation */ 617 | .pun { 618 | color: var(--source-code-punctuation); 619 | } 620 | /* lisp open bracket */ 621 | .opn { 622 | color: var(--source-code-open-bracket); 623 | } 624 | /* lisp close bracket */ 625 | .clo { 626 | color: var(--source-code-close-bracket); 627 | } 628 | /* markup tag name */ 629 | .tag { 630 | color: var(--source-code-markup-tag-name); 631 | } 632 | /* markup attribute name */ 633 | .atn { 634 | color: var(--source-code-markup-attribute-name); 635 | } 636 | /* markup attribute value */ 637 | .atv { 638 | color: var(--source-code-markup-attribute-value); 639 | } 640 | /* declaration */ 641 | .dec { 642 | color: var(--source-code-declaration); 643 | } 644 | /* variable name */ 645 | .var { 646 | color: var(--source-code-variable-name); 647 | } 648 | /* function name */ 649 | .fun { 650 | color: var(--source-code-function-name); 651 | } 652 | -------------------------------------------------------------------------------- /docs/@etercast/mp3/1.0.0/styles/prettify-atelier-cave-dark.css: -------------------------------------------------------------------------------- 1 | /*! Color themes for Google Code Prettify | MIT License | github.com/jmblog/color-themes-for-google-code-prettify */ 2 | .prettyprint { 3 | background: #19171c; 4 | font-family: Menlo, "Bitstream Vera Sans Mono", "DejaVu Sans Mono", Monaco, Consolas, monospace; 5 | border: 0 !important; 6 | } 7 | 8 | .pln { 9 | color: #efecf4; 10 | } 11 | 12 | /* Specify class=linenums on a pre to get line numbering */ 13 | ol.linenums { 14 | margin-top: 0; 15 | margin-bottom: 0; 16 | color: #655f6d; 17 | } 18 | 19 | li.L0, 20 | li.L1, 21 | li.L2, 22 | li.L3, 23 | li.L4, 24 | li.L5, 25 | li.L6, 26 | li.L7, 27 | li.L8, 28 | li.L9 { 29 | padding-left: 1em; 30 | background-color: #19171c; 31 | list-style-type: decimal; 32 | } 33 | 34 | @media screen { 35 | 36 | /* string content */ 37 | 38 | .str { 39 | color: #2a9292; 40 | } 41 | 42 | /* keyword */ 43 | 44 | .kwd { 45 | color: #955ae7; 46 | } 47 | 48 | /* comment */ 49 | 50 | .com { 51 | color: #655f6d; 52 | } 53 | 54 | /* type name */ 55 | 56 | .typ { 57 | color: #576ddb; 58 | } 59 | 60 | /* literal value */ 61 | 62 | .lit { 63 | color: #aa573c; 64 | } 65 | 66 | /* punctuation */ 67 | 68 | .pun { 69 | color: #efecf4; 70 | } 71 | 72 | /* lisp open bracket */ 73 | 74 | .opn { 75 | color: #efecf4; 76 | } 77 | 78 | /* lisp close bracket */ 79 | 80 | .clo { 81 | color: #efecf4; 82 | } 83 | 84 | /* markup tag name */ 85 | 86 | .tag { 87 | color: #be4678; 88 | } 89 | 90 | /* markup attribute name */ 91 | 92 | .atn { 93 | color: #aa573c; 94 | } 95 | 96 | /* markup attribute value */ 97 | 98 | .atv { 99 | color: #398bc6; 100 | } 101 | 102 | /* declaration */ 103 | 104 | .dec { 105 | color: #aa573c; 106 | } 107 | 108 | /* variable name */ 109 | 110 | .var { 111 | color: #be4678; 112 | } 113 | 114 | /* function name */ 115 | 116 | .fun { 117 | color: #576ddb; 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /docs/@etercast/mp3/1.0.0/styles/prettify-jsdoc.css: -------------------------------------------------------------------------------- 1 | /* JSDoc prettify.js theme */ 2 | 3 | /* plain text */ 4 | .pln { 5 | color: #000000; 6 | font-weight: normal; 7 | font-style: normal; 8 | } 9 | 10 | /* string content */ 11 | .str { 12 | color: hsl(104, 100%, 24%); 13 | font-weight: normal; 14 | font-style: normal; 15 | } 16 | 17 | /* a keyword */ 18 | .kwd { 19 | color: #000000; 20 | font-weight: bold; 21 | font-style: normal; 22 | } 23 | 24 | /* a comment */ 25 | .com { 26 | font-weight: normal; 27 | font-style: italic; 28 | } 29 | 30 | /* a type name */ 31 | .typ { 32 | color: #000000; 33 | font-weight: normal; 34 | font-style: normal; 35 | } 36 | 37 | /* a literal value */ 38 | .lit { 39 | color: #006400; 40 | font-weight: normal; 41 | font-style: normal; 42 | } 43 | 44 | /* punctuation */ 45 | .pun { 46 | color: #000000; 47 | font-weight: bold; 48 | font-style: normal; 49 | } 50 | 51 | /* lisp open bracket */ 52 | .opn { 53 | color: #000000; 54 | font-weight: bold; 55 | font-style: normal; 56 | } 57 | 58 | /* lisp close bracket */ 59 | .clo { 60 | color: #000000; 61 | font-weight: bold; 62 | font-style: normal; 63 | } 64 | 65 | /* a markup tag name */ 66 | .tag { 67 | color: #006400; 68 | font-weight: normal; 69 | font-style: normal; 70 | } 71 | 72 | /* a markup attribute name */ 73 | .atn { 74 | color: #006400; 75 | font-weight: normal; 76 | font-style: normal; 77 | } 78 | 79 | /* a markup attribute value */ 80 | .atv { 81 | color: #006400; 82 | font-weight: normal; 83 | font-style: normal; 84 | } 85 | 86 | /* a declaration */ 87 | .dec { 88 | color: #000000; 89 | font-weight: bold; 90 | font-style: normal; 91 | } 92 | 93 | /* a variable name */ 94 | .var { 95 | color: #000000; 96 | font-weight: normal; 97 | font-style: normal; 98 | } 99 | 100 | /* a function name */ 101 | .fun { 102 | color: #000000; 103 | font-weight: bold; 104 | font-style: normal; 105 | } 106 | 107 | /* Specify class=linenums on a pre to get line numbering */ 108 | ol.linenums { 109 | margin-top: 0; 110 | margin-bottom: 0; 111 | } 112 | -------------------------------------------------------------------------------- /docs/@etercast/mp3/1.0.0/styles/prettify-tomorrow.css: -------------------------------------------------------------------------------- 1 | /* Tomorrow Theme */ 2 | /* Original theme - https://github.com/chriskempson/tomorrow-theme */ 3 | /* Pretty printing styles. Used with prettify.js. */ 4 | /* SPAN elements with the classes below are added by prettyprint. */ 5 | /* plain text */ 6 | .pln { 7 | color: #4d4d4c; 8 | } 9 | 10 | @media screen { 11 | /* string content */ 12 | .str { 13 | color: hsl(104, 100%, 24%); 14 | } 15 | 16 | /* a keyword */ 17 | .kwd { 18 | color: hsl(240, 100%, 50%); 19 | } 20 | 21 | /* a comment */ 22 | .com { 23 | color: hsl(0, 0%, 60%); 24 | } 25 | 26 | /* a type name */ 27 | .typ { 28 | color: hsl(240, 100%, 32%); 29 | } 30 | 31 | /* a literal value */ 32 | .lit { 33 | color: hsl(240, 100%, 40%); 34 | } 35 | 36 | /* punctuation */ 37 | .pun { 38 | color: #000000; 39 | } 40 | 41 | /* lisp open bracket */ 42 | .opn { 43 | color: #000000; 44 | } 45 | 46 | /* lisp close bracket */ 47 | .clo { 48 | color: #000000; 49 | } 50 | 51 | /* a markup tag name */ 52 | .tag { 53 | color: #c82829; 54 | } 55 | 56 | /* a markup attribute name */ 57 | .atn { 58 | color: #f5871f; 59 | } 60 | 61 | /* a markup attribute value */ 62 | .atv { 63 | color: #3e999f; 64 | } 65 | 66 | /* a declaration */ 67 | .dec { 68 | color: #f5871f; 69 | } 70 | 71 | /* a variable name */ 72 | .var { 73 | color: #c82829; 74 | } 75 | 76 | /* a function name */ 77 | .fun { 78 | color: #4271ae; 79 | } 80 | } 81 | 82 | /* Use higher contrast and text-weight for printable form. */ 83 | @media print, projection { 84 | .str { 85 | color: #060; 86 | } 87 | 88 | .kwd { 89 | color: #006; 90 | font-weight: bold; 91 | } 92 | 93 | .com { 94 | color: #600; 95 | font-style: italic; 96 | } 97 | 98 | .typ { 99 | color: #404; 100 | font-weight: bold; 101 | } 102 | 103 | .lit { 104 | color: #044; 105 | } 106 | 107 | .pun, .opn, .clo { 108 | color: #440; 109 | } 110 | 111 | .tag { 112 | color: #006; 113 | font-weight: bold; 114 | } 115 | 116 | .atn { 117 | color: #404; 118 | } 119 | 120 | .atv { 121 | color: #060; 122 | } } 123 | /* Style */ 124 | /* 125 | pre.prettyprint { 126 | background: white; 127 | font-family: Consolas, Monaco, 'Andale Mono', monospace; 128 | font-size: 12px; 129 | line-height: 1.5; 130 | border: 1px solid #ccc; 131 | padding: 10px; 132 | } 133 | */ 134 | 135 | /* Specify class=linenums on a pre to get line numbering */ 136 | ol.linenums { 137 | margin-top: 0; 138 | margin-bottom: 0; 139 | } 140 | 141 | /* IE indents via margin-left */ 142 | li.L0, 143 | li.L1, 144 | li.L2, 145 | li.L3, 146 | li.L4, 147 | li.L5, 148 | li.L6, 149 | li.L7, 150 | li.L8, 151 | li.L9 { 152 | /* */ } 153 | 154 | /* Alternate shading for lines */ 155 | li.L1, 156 | li.L3, 157 | li.L5, 158 | li.L7, 159 | li.L9 { 160 | /* */ } 161 | -------------------------------------------------------------------------------- /examples/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Etercast MP3 Encoder Example 7 | 39 | 40 | 41 |
42 | 43 | 44 |
45 | 46 | 149 | 150 | 151 | -------------------------------------------------------------------------------- /examples/mp3.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Loads the `mp3.wasm` and returns the main class `Encoder`. 3 | * @module @etercast/mp3 4 | * 5 | * @func instantiate 6 | * @returns Promise 7 | */ 8 | export function instantiate(wasmURL = './mp3.wasm') { 9 | return fetch(wasmURL) 10 | .then((response) => response.arrayBuffer()) 11 | .then((arrayBuffer) => WebAssembly.instantiate(arrayBuffer, { 12 | // TODO: We really don't need these imports because mp3.wasm 13 | // does not handle file descriptors. 14 | wasi_unstable: { 15 | fd_close() { console.log('fd_close') }, 16 | fd_seek() { console.log('fd_seek') }, 17 | fd_write() { console.log('fd_write') }, 18 | proc_exit() { console.log('proc_exit') } 19 | } 20 | })) 21 | .then(({ instance }) => { 22 | // TODO: There's an extra export called _start that's not used 23 | // but is defined in WASI. Look for ways to remove this unnecessary 24 | // export. 25 | const { 26 | memory, 27 | mp3_create, 28 | mp3_init, 29 | mp3_encode, 30 | mp3_destroy 31 | } = instance.exports 32 | 33 | /** 34 | * Returns the error codes from the MP3 encoder. 35 | * @param {number} errorCode 36 | * @return {string} 37 | */ 38 | function getEncoderError(errorCode) { 39 | switch (errorCode) { 40 | default: 41 | return `Unknown error: ${errorCode}` 42 | case -1: 43 | return 'Invalid input sample rate' 44 | case -2: 45 | return 'Invalid input channels (mono and stereo only)' 46 | case -3: 47 | return 'Invalid quality' 48 | case -4: 49 | return 'Error calling lame_init' 50 | case -5: 51 | return 'Error calling lame_init_params' 52 | case -6: 53 | return 'Error reallocating buffers' 54 | case -7: 55 | return 'Too much input samples' 56 | case -8: 57 | return 'Error calling lame_encode_buffer_ieee_float' 58 | case -9: 59 | return 'Error calling lame_encode_flush' 60 | case -10: 61 | return 'Invalid number of samples passed' 62 | case -11: 63 | return 'Invalid input samples' 64 | case -100: 65 | return 'Ok' 66 | case -101: 67 | return 'Error calling lame_encode_buffer_ieee_float: Buffer was too small' 68 | case -102: 69 | return 'Error calling lame_encode_buffer_ieee_float: malloc() problem' 70 | case -103: 71 | return 'Error calling lame_encode_buffer_ieee_float: lame_init_params not called' 72 | case -104: 73 | return 'Error calling lame_encode_buffer_ieee_float: Psycho acoustic problems' 74 | case -110: 75 | return 'Error calling lame_encode_buffer_ieee_float: No memory' 76 | case -111: 77 | return 'Error calling lame_encode_buffer_ieee_float: Bad bitrate' 78 | case -112: 79 | return 'Error calling lame_encode_buffer_ieee_float: Bad samplefreq' 80 | case -113: 81 | return 'Error calling lame_encode_buffer_ieee_float: Internal error' 82 | } 83 | } 84 | 85 | /** 86 | * Encoder states. 87 | * @readonly 88 | * @enum {string} 89 | */ 90 | const EncoderState = { 91 | /** Indicates that the encoder is running and is capable of encode MP3 frames. */ 92 | RUNNING: 'running', 93 | /** Indicates that the encoder was closed and no longer can encode MP3 frames. */ 94 | CLOSED: 'closed', 95 | /** Indicates that something went wrong. */ 96 | ERROR: 'error' 97 | } 98 | 99 | /** 100 | * Encoder mode. 101 | * @readonly 102 | * @enum {number} 103 | */ 104 | const EncoderMode = { 105 | /** Constant bit-rate */ 106 | CBR: 0, 107 | /** Variable bit-rate */ 108 | VBR: 1 109 | } 110 | 111 | /** 112 | * MP3 encoder options 113 | * @typedef {Object} EncoderOptions 114 | * @property {number} sampleRate - Input/output sample rate. Usually this is the value from an AudioContext. 115 | * @property {number} numChannels - Number of input/output channels. MP3 supports 1 (mono) or 2 (stereo) channels 116 | * @property {number} quality - Encoding quality (0 - lowest, 9 - highest). 117 | * In VBR (Variable Bit-Rate) this quality indicates an average kbps but in 118 | * CBR (Constant Bit-Rate) 0 is equal to 32kbps and 9 is equal to 320kbps. 119 | * @property {number} samples - Number of samples that will be encoded each time `encode` is called. 120 | * @property {EncoderMode} mode - Encoding mode (0 - CBR, 1 - VBR). 121 | */ 122 | 123 | /** 124 | * Not exported by default in the module, you need 125 | * to call `instantiate` to retrieve this class. 126 | * 127 | * @example 128 | * import instantiate from '@etercast/mp3' 129 | * 130 | * const Encoder = await instantiate() 131 | * const encoder = new Encoder(encoderOptions) 132 | * encoder.encode(leftChannelData, rightChannelData) 133 | * encoder.close() 134 | */ 135 | class Encoder { 136 | /** 137 | * Creates a new MP3 encoder. 138 | * @param {EncoderOptions} options 139 | * @example 140 | * import instantiate from '@etercast/mp3' 141 | * 142 | * const Encoder = await instantiate() 143 | * const encoder = Encoder.create(encoderOptions) 144 | */ 145 | static create(options) { 146 | return new Encoder(options) 147 | } 148 | 149 | /** 150 | * Constructor 151 | * @param {EncoderOptions} options 152 | */ 153 | constructor(options) { 154 | const { 155 | sampleRate, 156 | numChannels, 157 | quality, 158 | samples, 159 | mode, 160 | } = { 161 | sampleRate: 44100, 162 | numChannels: 1, 163 | quality: 9, 164 | samples: 2048, 165 | mode: 0, 166 | ...options 167 | } 168 | this._error = null 169 | this._state = EncoderState.RUNNING 170 | this._pointer = null 171 | const pointer = mp3_create() 172 | if (!pointer) { 173 | return this._throw(new Error('Cannot create mp3 encoder')) 174 | } 175 | this._pointer = pointer 176 | const internalRegisters = 10 177 | const internal = new Int32Array(memory.buffer, pointer, internalRegisters) 178 | this._internal = internal 179 | const errorCode = mp3_init(this._pointer, sampleRate, numChannels, quality, samples, mode) 180 | if (errorCode < 0) { 181 | return this._throw(new Error(getEncoderError(errorCode))) 182 | } 183 | 184 | const [, outputBufferMaxSize, , , inputSamples, , , inputBufferLeftPointer, inputBufferRightPointer, outputBufferPointer] = internal 185 | this._inputBufferLeft = new Float32Array(memory.buffer, inputBufferLeftPointer, inputSamples) 186 | this._inputBufferRight = new Float32Array(memory.buffer, inputBufferRightPointer, inputSamples) 187 | this._outputBuffer = new Uint8Array(memory.buffer, outputBufferPointer, outputBufferMaxSize) 188 | } 189 | 190 | /** 191 | * Encoder state 192 | * @type {EncoderState} 193 | */ 194 | get state() { 195 | return this._state 196 | } 197 | 198 | /** 199 | * Error 200 | * @type {null|Error} 201 | */ 202 | get error() { 203 | return this._error 204 | } 205 | 206 | /** 207 | * Current output buffer size. 208 | * @type {number} 209 | */ 210 | get outputBufferSize() { 211 | return this._internal[0] 212 | } 213 | 214 | /** 215 | * Max. output buffer size. 216 | * @type {number} 217 | */ 218 | get outputBufferMaxSize() { 219 | return this._internal[1] 220 | } 221 | 222 | /** 223 | * Output sample rate 224 | * @type {number} 225 | */ 226 | get outputSampleRate() { 227 | return this._internal[2] 228 | } 229 | 230 | /** 231 | * Output quality 232 | * @type {number} 233 | */ 234 | get outputQuality() { 235 | return this._internal[3] 236 | } 237 | 238 | /** 239 | * Input samples 240 | * @type {number} 241 | */ 242 | get inputSamples() { 243 | return this._internal[4] 244 | } 245 | 246 | /** 247 | * Input sample rate 248 | * @type {number} 249 | */ 250 | get inputSampleRate() { 251 | return this._internal[5] 252 | } 253 | 254 | /** 255 | * Input number of channels (1 - mono, 2 - stereo) 256 | * @type {number} 257 | */ 258 | get inputChannels() { 259 | return this._internal[6] 260 | } 261 | 262 | /** 263 | * Indicates that the encoder is running. 264 | * @type {boolean} 265 | */ 266 | get isRunning() { 267 | return this._state === EncoderState.RUNNING 268 | } 269 | 270 | /** 271 | * Indicates that the encoder is running. 272 | * @type {boolean} 273 | */ 274 | get isClosed() { 275 | return this._state === EncoderState.CLOSED 276 | } 277 | 278 | /** 279 | * Throws an error 280 | * @private 281 | * @param {Error} error 282 | */ 283 | _throw(error) { 284 | this._state = EncoderState.ERROR 285 | this._error = error 286 | throw error 287 | } 288 | 289 | /** 290 | * Encodes raw float 32-bit audio data into MP3 frames 291 | * @example 292 | * import instantiate from '@etercast/mp3' 293 | * 294 | * const Encoder = await instantiate() 295 | * const encoder = new Encoder({ 296 | * sampleRate, 297 | * samples, 298 | * numChannels, 299 | * quality, 300 | * mode 301 | * }) 302 | * encoder.encode(leftChannelData, rightChannelData) 303 | * 304 | * @param {Float32Array} left - Left channel (mono) 305 | * @param {Float32Array} [right] - Right channel (stereo) 306 | * @returns {Uint8Array} - Returns a bunch of encoded frames 307 | */ 308 | encode(left, right) { 309 | if (this._state !== EncoderState.RUNNING) { 310 | this._throw(new Error('Encoder already closed')) 311 | } 312 | let samples = 0 313 | if (left) { 314 | samples = left.length 315 | this._inputBufferLeft.set(left) 316 | if (right) { 317 | if (samples !== right.length) { 318 | this._throw(new Error('Encoder channels have different lengths')) 319 | } 320 | this._inputBufferRight.set(right) 321 | } 322 | } 323 | // Codifica el MP3. 324 | const errorCode = mp3_encode(this._pointer, samples) 325 | if (errorCode < 0) { 326 | this._throw(new Error(getEncoderError(errorCode))) 327 | } 328 | return this._outputBuffer.slice(0, this.outputBufferSize) 329 | } 330 | 331 | /** 332 | * Closes the encoder. 333 | * 334 | * @example 335 | * import instantiate from '@etercast/mp3' 336 | * 337 | * const Encoder = await instantiate() 338 | * const encoder = new Encoder() 339 | * 340 | * // Do something with the encoder... 341 | * 342 | * encoder.close() 343 | * 344 | * // `encoder` is no longer usable and all the 345 | * // memory reserved is freed. 346 | */ 347 | close() { 348 | if (this._state !== EncoderState.RUNNING) { 349 | this._throw(new Error('Encoder already closed')) 350 | } 351 | 352 | const errorCode = mp3_destroy(this._pointer) 353 | if (errorCode < 0) { 354 | this._throw(new Error(getEncoderError(errorCode))) 355 | } 356 | this._inputBufferLeft = null 357 | this._inputBufferRight = null 358 | this._outputBuffer = null 359 | this._pointer = null 360 | this._internal = null 361 | this._state = EncoderState.CLOSED 362 | } 363 | } 364 | 365 | return Encoder 366 | }) 367 | } 368 | 369 | export default instantiate 370 | -------------------------------------------------------------------------------- /examples/mp3.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etercast/mp3/08aa4d475557fe44f5b85f436b6911d1ec67e092/examples/mp3.wasm -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@etercast/mp3", 3 | "version": "1.0.2", 4 | "description": "Etercast MP3 encoder", 5 | "main": "dist/mp3.js", 6 | "files": [ 7 | "dist" 8 | ], 9 | "scripts": { 10 | "serve:examples": "cp dist/mp3.* ./examples && serve ./examples", 11 | "serve:docs": "serve ./docs/@etercast/mp3/1.0.0/", 12 | "build:emscripten": "docker run --rm -v $(pwd):/src -e EMCC_DEBUG=1 trzeci/emscripten-upstream:1.39.3 make", 13 | "build:js": "cp src/mp3.js dist/mp3.mjs && babel src/mp3.js -o dist/mp3.cjs", 14 | "build:docs": "jsdoc -c .jsdoc.json", 15 | "build:examples": "cp dist/mp3.wasm examples && cp dist/mp3.js examples", 16 | "build": "npm run build:emscripten && npm run build:js && npm run build:docs && npm run build:examples", 17 | "docs": "npm run build:docs && npm run serve:docs", 18 | "test": "echo \"Error: no test specified\" && exit 1" 19 | }, 20 | "keywords": [ 21 | "mp3", 22 | "lame", 23 | "encoder" 24 | ], 25 | "repository": { 26 | "type": "git", 27 | "url": "https://github.com/etercast/mp3.git" 28 | }, 29 | "bugs": { 30 | "url": "https://github.com/etercast/jsdoc-template/issues" 31 | }, 32 | "homepage": "https://github.com/etercast/mp3", 33 | "author": "ROJO 2 (https://rojo2.com)", 34 | "license": "GPL-3.0", 35 | "devDependencies": { 36 | "@babel/cli": "^7.8.4", 37 | "@babel/core": "^7.9.0", 38 | "@babel/preset-env": "^7.9.5", 39 | "@etercast/jsdoc-template": "^1.0.0", 40 | "jsdoc": "^3.6.4", 41 | "minami": "^1.2.3", 42 | "serve": "^11.3.0" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /serve.json: -------------------------------------------------------------------------------- 1 | { 2 | "public": "examples" 3 | } 4 | -------------------------------------------------------------------------------- /src/mp3.c: -------------------------------------------------------------------------------- 1 | #include "mp3.h" 2 | 3 | /** 4 | * Returns the audio quality depending on which mode we are 5 | * using and the quality parameter. 6 | */ 7 | int32_t mp3_get_output_quality(int32_t mode, int32_t quality) { 8 | if (mode == MP3_VBR_MODE) { 9 | return quality; 10 | } else { 11 | switch (quality) { 12 | default: return 320; 13 | case 0: return 32; 14 | case 1: return 48; 15 | case 2: return 64; 16 | case 3: return 96; 17 | case 4: return 128; 18 | case 5: return 160; 19 | case 6: return 192; 20 | case 7: return 224; 21 | case 8: return 256; 22 | case 9: return 320; 23 | } 24 | } 25 | } 26 | 27 | /** 28 | * Creates a structure used to keep lame and mp3_t data. 29 | */ 30 | EMSCRIPTEN_KEEPALIVE 31 | mp3_t* mp3_create() { 32 | return malloc(sizeof(mp3_t)); 33 | } 34 | 35 | /** 36 | * Frees all the used memory. 37 | */ 38 | EMSCRIPTEN_KEEPALIVE 39 | int32_t mp3_destroy(mp3_t* e) { 40 | lame_close(e->gfp); 41 | free(e->input_buffer_left); 42 | free(e->input_buffer_right); 43 | free(e->output_buffer); 44 | free(e); 45 | return MP3_NO_ERROR; 46 | } 47 | 48 | /** 49 | * Initializes lame and all the buffers. 50 | */ 51 | EMSCRIPTEN_KEEPALIVE 52 | int32_t mp3_init(mp3_t* e, int32_t sample_rate, int32_t num_channels, int32_t quality, int32_t samples, int32_t mode) { 53 | if (sample_rate < 11025 || sample_rate > 48000) { 54 | return MP3_ERROR_CONFIG_SAMPLE_RATE; 55 | } 56 | 57 | if (num_channels < 1 || num_channels > 2) { 58 | return MP3_ERROR_CONFIG_NUM_CHANNELS; 59 | } 60 | 61 | if (quality < 0 || quality > 9) { 62 | return MP3_ERROR_CONFIG_QUALITY; 63 | } 64 | 65 | if (samples < 2048 || samples > 16384) { 66 | return MP3_ERROR_CONFIG_SAMPLES; 67 | } 68 | 69 | // Inicializamos la estructura de LAME. 70 | e->gfp = lame_init(); 71 | if (!e->gfp) { 72 | return MP3_ERROR_LAME_INIT; 73 | } 74 | 75 | // Reservamos la memoria necesaria para copiar las muestras que van 76 | // a entrar a través del micrófono. 77 | e->input_buffer_left = malloc(samples * sizeof(float)); 78 | e->input_buffer_right = malloc(samples * sizeof(float)); 79 | 80 | // Muestras de entrada. 81 | e->input_samples = samples; 82 | e->input_samplerate = sample_rate; 83 | e->input_channels = num_channels; 84 | 85 | e->output_samplerate = MP3_DEFAULT_OUTPUT_SAMPLE_RATE; 86 | e->output_quality = mp3_get_output_quality(mode, quality); 87 | 88 | // Reservamos 1MB para ir tirando. Aquí se irá almacenando 89 | // todo lo que necesitemos a la hora de codificar el MP3. 90 | e->output_buffer_max_size = samples * 1.25 + 7200; 91 | e->output_buffer_size = 0; 92 | e->output_buffer = malloc(e->output_buffer_max_size); 93 | 94 | // Establecemos la configuración de LAME. 95 | lame_set_in_samplerate(e->gfp, e->input_samplerate); 96 | lame_set_out_samplerate(e->gfp, e->output_samplerate); 97 | lame_set_num_channels(e->gfp, e->input_channels); 98 | if (mode == MP3_VBR_MODE) { 99 | lame_set_VBR(e->gfp, vbr_default); 100 | lame_set_VBR_q(e->gfp, e->output_quality); 101 | } else { 102 | lame_set_VBR(e->gfp, vbr_off); 103 | lame_set_brate(e->gfp, e->output_quality); 104 | } 105 | 106 | // Reservamos la memoria necesaria para los settings 107 | // que hemos establecido. 108 | if (lame_init_params(e->gfp) < 0) { 109 | return MP3_ERROR_LAME_INIT_PARAMS; 110 | } 111 | 112 | return MP3_NO_ERROR; 113 | } 114 | 115 | /** 116 | * Encodes MP3 frames. 117 | */ 118 | EMSCRIPTEN_KEEPALIVE 119 | int32_t mp3_encode(mp3_t* e, int32_t num_samples) { 120 | // If 0 samples are passed we consider that we have already finished 121 | // to encode the mp3 so let's flush what we have in 122 | // the internal buffer. 123 | if (num_samples == 0) { 124 | return mp3_flush(e); 125 | } 126 | 127 | // If negative samples are passed this returns an error. 128 | if (num_samples < 0) { 129 | return MP3_ERROR_NUM_SAMPLES; 130 | } 131 | 132 | // If more samples than allowed are passed, then 133 | // we return an error. 134 | if (num_samples > e->input_samples) { 135 | return MP3_ERROR_TOO_MUCH_SAMPLES; 136 | } 137 | 138 | // Encodes the buffer and returns the number of bytes 139 | // that you have encoded. 140 | float* right = e->input_channels == 2 ? e->input_buffer_right : NULL; 141 | int32_t num_bytes = lame_encode_buffer_ieee_float( 142 | e->gfp, 143 | e->input_buffer_left, 144 | right, 145 | num_samples, 146 | e->output_buffer, 147 | e->output_buffer_max_size 148 | ); 149 | 150 | if (num_bytes < 0) { 151 | return num_bytes - 100; 152 | } 153 | e->output_buffer_size = num_bytes; 154 | return MP3_NO_ERROR; 155 | } 156 | 157 | /** 158 | * Prevents losing half encoded frames. 159 | */ 160 | int32_t mp3_flush(mp3_t* e) { 161 | int32_t num_bytes = lame_encode_flush(e->gfp, e->output_buffer, e->output_buffer_max_size); 162 | if (num_bytes < 0) { 163 | return MP3_ERROR_LAME_ENCODE_FLUSH; 164 | } 165 | e->output_buffer_size = num_bytes; 166 | return MP3_NO_ERROR; 167 | } 168 | -------------------------------------------------------------------------------- /src/mp3.h: -------------------------------------------------------------------------------- 1 | #ifndef __MP3_H__ 2 | #define __MP3_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | /** 10 | * Constants. 11 | */ 12 | #define MP3_DEFAULT_OUTPUT_SAMPLE_RATE 44100 13 | 14 | /** 15 | * Error list. 16 | */ 17 | #define MP3_NO_ERROR 0 18 | 19 | #define MP3_CBR_MODE 0 20 | #define MP3_VBR_MODE 1 21 | 22 | #define MP3_ERROR_CONFIG_SAMPLE_RATE -1 23 | #define MP3_ERROR_CONFIG_NUM_CHANNELS -2 24 | #define MP3_ERROR_CONFIG_QUALITY -3 25 | #define MP3_ERROR_LAME_INIT -4 26 | #define MP3_ERROR_LAME_INIT_PARAMS -5 27 | #define MP3_ERROR_REALLOC_BUFFER_SIZE -6 28 | #define MP3_ERROR_TOO_MUCH_SAMPLES -7 29 | #define MP3_ERROR_LAME_ENCODE_IEEE_FLOAT -8 30 | #define MP3_ERROR_LAME_ENCODE_FLUSH -9 31 | #define MP3_ERROR_NUM_SAMPLES -10 32 | #define MP3_ERROR_CONFIG_SAMPLES -11 33 | 34 | typedef struct { 35 | // Output configuration. 36 | int32_t output_buffer_size; 37 | int32_t output_buffer_max_size; 38 | int32_t output_samplerate; 39 | int32_t output_quality; 40 | 41 | // Input configuration. 42 | int32_t input_samples; 43 | int32_t input_samplerate; 44 | int32_t input_channels; 45 | 46 | // Pointer to input samples. 47 | float* input_buffer_left; 48 | float* input_buffer_right; 49 | 50 | // Pointer to output frames. 51 | uint8_t* output_buffer; 52 | 53 | // LAME structure 54 | lame_global_flags* gfp; 55 | } mp3_t; 56 | 57 | // Creates the mp3_t structure and returns a pointer to it. 58 | mp3_t* mp3_create(); 59 | 60 | // Initializes the mp3_t structure reserving space for 61 | // buffers and configuring LAME. 62 | int32_t mp3_init(mp3_t* e, int32_t sample_rate, int32_t num_channels, int32_t quality, int32_t samples, int32_t mode); 63 | 64 | // Encodes MP3 frames. 65 | int32_t mp3_encode(mp3_t* e, int32_t num_samples); 66 | 67 | // Flushes half-encoded frames. 68 | int32_t mp3_flush(mp3_t* e); 69 | 70 | // Destroys MP3 structure and frees memory. 71 | int32_t mp3_destroy(mp3_t* e); 72 | 73 | #endif 74 | -------------------------------------------------------------------------------- /src/mp3.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Loads the `mp3.wasm` and returns the main class `Encoder`. 3 | * @module @etercast/mp3 4 | * 5 | * @func instantiate 6 | * @param {string|URL|Request} [wasm='mp3.wasm'] WASM file URL. 7 | * @returns Promise 8 | */ 9 | export function instantiate(wasm = './mp3.wasm') { 10 | return fetch(wasm) 11 | .then((response) => response.arrayBuffer()) 12 | .then((arrayBuffer) => WebAssembly.instantiate(arrayBuffer, { 13 | // TODO: We really don't need these imports because mp3.wasm 14 | // does not handle file descriptors. 15 | wasi_unstable: { 16 | fd_close() { console.log('fd_close') }, 17 | fd_seek() { console.log('fd_seek') }, 18 | fd_write() { console.log('fd_write') }, 19 | proc_exit() { console.log('proc_exit') } 20 | } 21 | })) 22 | .then(({ instance }) => { 23 | // TODO: There's an extra export called _start that's not used 24 | // but is defined in WASI. Look for ways to remove this unnecessary 25 | // export. 26 | const { 27 | memory, 28 | mp3_create, 29 | mp3_init, 30 | mp3_encode, 31 | mp3_destroy 32 | } = instance.exports 33 | 34 | /** 35 | * Returns the error codes from the MP3 encoder. 36 | * @param {number} errorCode 37 | * @return {string} 38 | */ 39 | function getEncoderError(errorCode) { 40 | switch (errorCode) { 41 | default: 42 | return `Unknown error: ${errorCode}` 43 | case -1: 44 | return 'Invalid input sample rate' 45 | case -2: 46 | return 'Invalid input channels (mono and stereo only)' 47 | case -3: 48 | return 'Invalid quality' 49 | case -4: 50 | return 'Error calling lame_init' 51 | case -5: 52 | return 'Error calling lame_init_params' 53 | case -6: 54 | return 'Error reallocating buffers' 55 | case -7: 56 | return 'Too much input samples' 57 | case -8: 58 | return 'Error calling lame_encode_buffer_ieee_float' 59 | case -9: 60 | return 'Error calling lame_encode_flush' 61 | case -10: 62 | return 'Invalid number of samples passed' 63 | case -11: 64 | return 'Invalid input samples' 65 | case -100: 66 | return 'Ok' 67 | case -101: 68 | return 'Error calling lame_encode_buffer_ieee_float: Buffer was too small' 69 | case -102: 70 | return 'Error calling lame_encode_buffer_ieee_float: malloc() problem' 71 | case -103: 72 | return 'Error calling lame_encode_buffer_ieee_float: lame_init_params not called' 73 | case -104: 74 | return 'Error calling lame_encode_buffer_ieee_float: Psycho acoustic problems' 75 | case -110: 76 | return 'Error calling lame_encode_buffer_ieee_float: No memory' 77 | case -111: 78 | return 'Error calling lame_encode_buffer_ieee_float: Bad bitrate' 79 | case -112: 80 | return 'Error calling lame_encode_buffer_ieee_float: Bad samplefreq' 81 | case -113: 82 | return 'Error calling lame_encode_buffer_ieee_float: Internal error' 83 | } 84 | } 85 | 86 | /** 87 | * Encoder states. 88 | * @readonly 89 | * @enum {string} 90 | */ 91 | const EncoderState = { 92 | /** Indicates that the encoder is running and is capable of encode MP3 frames. */ 93 | RUNNING: 'running', 94 | /** Indicates that the encoder was closed and no longer can encode MP3 frames. */ 95 | CLOSED: 'closed', 96 | /** Indicates that something went wrong. */ 97 | ERROR: 'error' 98 | } 99 | 100 | /** 101 | * Encoder mode. 102 | * @readonly 103 | * @enum {number} 104 | */ 105 | const EncoderMode = { 106 | /** Constant bit-rate */ 107 | CBR: 0, 108 | /** Variable bit-rate */ 109 | VBR: 1 110 | } 111 | 112 | /** 113 | * MP3 encoder options 114 | * @typedef {Object} EncoderOptions 115 | * @property {number} sampleRate Input/output sample rate. Usually this is the value from an AudioContext. 116 | * @property {number} numChannels Number of input/output channels. MP3 supports 1 (mono) or 2 (stereo) channels 117 | * @property {number} quality Encoding quality (0 - lowest, 9 - highest). 118 | * In VBR (Variable Bit-Rate) this quality indicates an average kbps but in 119 | * CBR (Constant Bit-Rate) 0 is equal to 32kbps and 9 is equal to 320kbps. 120 | * @property {number} samples Number of samples that will be encoded each time `encode` is called. 121 | * @property {EncoderMode} mode Encoding mode (0 - CBR, 1 - VBR). 122 | */ 123 | 124 | /** 125 | * Not exported by default in the module, you need 126 | * to call `instantiate` to retrieve this class. 127 | * 128 | * @example 129 | * import instantiate from '@etercast/mp3' 130 | * 131 | * const Encoder = await instantiate() 132 | * const encoder = new Encoder(encoderOptions) 133 | * encoder.encode(leftChannelData, rightChannelData) 134 | * encoder.close() 135 | */ 136 | class Encoder { 137 | /** 138 | * Creates a new MP3 encoder. This is equivalent to calling 139 | * the constructor using the `new` keyword. It's useful 140 | * if you need a function that instantiates the Encoder. 141 | * @param {EncoderOptions} options 142 | * @example 143 | * import instantiate from '@etercast/mp3' 144 | * 145 | * const Encoder = await instantiate() 146 | * const encoder = Encoder.create(encoderOptions) 147 | * @returns {Encoder} 148 | */ 149 | static create(options) { 150 | return new Encoder(options) 151 | } 152 | 153 | /** 154 | * Internally this calls the exported method `mp3_create` to 155 | * make WASM module create a new structure to hold all the LAME 156 | * encoding data. It also calls `mp3_init` to establish encoder 157 | * options like number of channels, quality, sampleRate, etc. 158 | * @param {EncoderOptions} options 159 | */ 160 | constructor(options) { 161 | const { 162 | sampleRate, 163 | numChannels, 164 | quality, 165 | samples, 166 | mode, 167 | } = { 168 | sampleRate: 44100, 169 | numChannels: 1, 170 | quality: 9, 171 | samples: 2048, 172 | mode: 0, 173 | ...options 174 | } 175 | this._error = null 176 | this._state = EncoderState.RUNNING 177 | this._pointer = null 178 | const pointer = mp3_create() 179 | if (!pointer) { 180 | return this._throw(new Error('Cannot create mp3 encoder')) 181 | } 182 | this._pointer = pointer 183 | const internalRegisters = 10 184 | const internal = new Int32Array(memory.buffer, pointer, internalRegisters) 185 | this._internal = internal 186 | const errorCode = mp3_init(this._pointer, sampleRate, numChannels, quality, samples, mode) 187 | if (errorCode < 0) { 188 | return this._throw(new Error(getEncoderError(errorCode))) 189 | } 190 | 191 | const [, outputBufferMaxSize, , , inputSamples, , , inputBufferLeftPointer, inputBufferRightPointer, outputBufferPointer] = internal 192 | this._inputBufferLeft = new Float32Array(memory.buffer, inputBufferLeftPointer, inputSamples) 193 | this._inputBufferRight = new Float32Array(memory.buffer, inputBufferRightPointer, inputSamples) 194 | this._outputBuffer = new Uint8Array(memory.buffer, outputBufferPointer, outputBufferMaxSize) 195 | } 196 | 197 | /** 198 | * Encoder state 199 | * @type {EncoderState} 200 | */ 201 | get state() { 202 | return this._state 203 | } 204 | 205 | /** 206 | * Error 207 | * @type {null|Error} 208 | */ 209 | get error() { 210 | return this._error 211 | } 212 | 213 | /** 214 | * Current output buffer size. 215 | * @type {number} 216 | */ 217 | get outputBufferSize() { 218 | return this._internal[0] 219 | } 220 | 221 | /** 222 | * Max. output buffer size. 223 | * @type {number} 224 | */ 225 | get outputBufferMaxSize() { 226 | return this._internal[1] 227 | } 228 | 229 | /** 230 | * Output sample rate 231 | * @type {number} 232 | */ 233 | get outputSampleRate() { 234 | return this._internal[2] 235 | } 236 | 237 | /** 238 | * Output quality 239 | * @type {number} 240 | */ 241 | get outputQuality() { 242 | return this._internal[3] 243 | } 244 | 245 | /** 246 | * Input samples 247 | * @type {number} 248 | */ 249 | get inputSamples() { 250 | return this._internal[4] 251 | } 252 | 253 | /** 254 | * Input sample rate 255 | * @type {number} 256 | */ 257 | get inputSampleRate() { 258 | return this._internal[5] 259 | } 260 | 261 | /** 262 | * Input number of channels (1 - mono, 2 - stereo) 263 | * @type {number} 264 | */ 265 | get inputChannels() { 266 | return this._internal[6] 267 | } 268 | 269 | /** 270 | * Indicates that the encoder is running. 271 | * @type {boolean} 272 | */ 273 | get isRunning() { 274 | return this._state === EncoderState.RUNNING 275 | } 276 | 277 | /** 278 | * Indicates that the encoder is running. 279 | * @type {boolean} 280 | */ 281 | get isClosed() { 282 | return this._state === EncoderState.CLOSED 283 | } 284 | 285 | /** 286 | * Throws an error 287 | * @private 288 | * @param {Error} error 289 | */ 290 | _throw(error) { 291 | this._state = EncoderState.ERROR 292 | this._error = error 293 | throw error 294 | } 295 | 296 | /** 297 | * Encodes raw Float 32-bit audio data into MP3 frames. An MP3 298 | * frame consist of a header and payload, this makes MP3 streamable 299 | * and implies that you can merge two files easily by concatenating them. 300 | * 301 | * If you need to merge two channels then you should use a `ChannelMergeNode` 302 | * and then reencode the audio. 303 | * 304 | * @example 305 | * import instantiate from '@etercast/mp3' 306 | * 307 | * const Encoder = await instantiate() 308 | * const encoder = new Encoder({ 309 | * sampleRate, 310 | * samples, 311 | * numChannels, 312 | * quality, 313 | * mode 314 | * }) 315 | * encoder.encode(leftChannelData, rightChannelData) 316 | * 317 | * @param {Float32Array} left Left channel (mono) 318 | * @param {Float32Array} [right] Right channel (stereo) 319 | * @returns {Uint8Array} Returns a bunch of encoded frames 320 | */ 321 | encode(left, right) { 322 | if (this._state !== EncoderState.RUNNING) { 323 | this._throw(new Error('Encoder already closed')) 324 | } 325 | let samples = 0 326 | if (left) { 327 | samples = left.length 328 | this._inputBufferLeft.set(left) 329 | if (right) { 330 | if (samples !== right.length) { 331 | this._throw(new Error('Encoder channels have different lengths')) 332 | } 333 | this._inputBufferRight.set(right) 334 | } 335 | } 336 | // Codifica el MP3. 337 | const errorCode = mp3_encode(this._pointer, samples) 338 | if (errorCode < 0) { 339 | this._throw(new Error(getEncoderError(errorCode))) 340 | } 341 | return this._outputBuffer.slice(0, this.outputBufferSize) 342 | } 343 | 344 | /** 345 | * Closes the encoder. After this method any call to `encode` 346 | * will throw an Error. If you need to append more data after 347 | * closing an encoder you can instantiate a new Encoder and then 348 | * concatenate all the new data with the old data. 349 | * 350 | * @example 351 | * import instantiate from '@etercast/mp3' 352 | * 353 | * const Encoder = await instantiate() 354 | * const encoder = new Encoder() 355 | * 356 | * // Do something with the encoder... 357 | * 358 | * encoder.close() 359 | * 360 | * // `encoder` is no longer usable and all the 361 | * // memory reserved is freed. 362 | */ 363 | close() { 364 | if (this._state !== EncoderState.RUNNING) { 365 | this._throw(new Error('Encoder already closed')) 366 | } 367 | 368 | const errorCode = mp3_destroy(this._pointer) 369 | if (errorCode < 0) { 370 | this._throw(new Error(getEncoderError(errorCode))) 371 | } 372 | this._inputBufferLeft = null 373 | this._inputBufferRight = null 374 | this._outputBuffer = null 375 | this._pointer = null 376 | this._internal = null 377 | this._state = EncoderState.CLOSED 378 | } 379 | } 380 | 381 | return Encoder 382 | }) 383 | } 384 | 385 | export default instantiate 386 | --------------------------------------------------------------------------------