├── utility ├── server.command ├── tts.command ├── lookupwebmaus.txt ├── lookupvdat.txt ├── package.json ├── subtitle.command ├── createindex.js ├── lookupgentle.txt ├── lookup.js └── pitch.js ├── icons ├── audio.png ├── json.png ├── audition.png ├── micactive.png ├── microphone.png └── aftereffects.png ├── presets.json ├── README.md ├── replacewavwithmp4.jsx ├── package.json ├── LICENSE ├── main.js ├── scripts ├── TextToSpeech.js ├── BufferToFile.js ├── AuditionSession.js ├── Triphone.js ├── SpeechToSpeech.js ├── Convert.js ├── Main.js └── modules │ ├── tonal.js │ ├── midi.js │ └── wavefile.js ├── style.css ├── lookup.json ├── editor.html ├── convertaudition.jsx ├── index.html └── editor.css /utility/server.command: -------------------------------------------------------------------------------- 1 | http-server "./Desktop/PHOMEME/WEBSITE" --cors -c-1 -o -------------------------------------------------------------------------------- /icons/audio.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MysteryPancake/Phomeme/HEAD/icons/audio.png -------------------------------------------------------------------------------- /icons/json.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MysteryPancake/Phomeme/HEAD/icons/json.png -------------------------------------------------------------------------------- /icons/audition.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MysteryPancake/Phomeme/HEAD/icons/audition.png -------------------------------------------------------------------------------- /icons/micactive.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MysteryPancake/Phomeme/HEAD/icons/micactive.png -------------------------------------------------------------------------------- /icons/microphone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MysteryPancake/Phomeme/HEAD/icons/microphone.png -------------------------------------------------------------------------------- /icons/aftereffects.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MysteryPancake/Phomeme/HEAD/icons/aftereffects.png -------------------------------------------------------------------------------- /utility/tts.command: -------------------------------------------------------------------------------- 1 | cd "./Desktop/PHOMEME" 2 | echo -n 'TEXT: ' 3 | read text 4 | say -v Samantha $text -o output.aiff -------------------------------------------------------------------------------- /presets.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "author": "MysteryPancake", 4 | "description": "Series 1 (2008) of the world's greatest poet.", 5 | "name": "Michael Rosen", 6 | "url": "https://mysterypancake.github.io/Phomeme-Rosen/index.json" 7 | }, 8 | { 9 | "author": "MysteryPancake", 10 | "description": "We'll take you through this monkey rap.", 11 | "name": "Donkey Kong", 12 | "url": "https://mysterypancake.github.io/Phomeme-Kong/index.json" 13 | } 14 | ] -------------------------------------------------------------------------------- /utility/lookupwebmaus.txt: -------------------------------------------------------------------------------- 1 | 3: ER 2 | 6 AH 3 | 6: AE 4 | SIL 5 | @ AH 6 | @} OW 7 | Ae AY 8 | b B 9 | d D 10 | D DH 11 | dZ JH 12 | E EH 13 | e EH 14 | f F 15 | g G 16 | h HH 17 | I IH 18 | i IY 19 | i: IY 20 | I@ IH 21 | j Y 22 | k K 23 | l L 24 | m M 25 | n N 26 | N NG 27 | O AA 28 | o: AO 29 | OI OY 30 | p P 31 | r ER 32 | r\ R 33 | s S 34 | S SH 35 | t T 36 | T TH 37 | tS CH 38 | U UH 39 | u UW 40 | V AH 41 | v V 42 | w W 43 | z Z 44 | { AE 45 | {I EY 46 | {O AW 47 | }: UW -------------------------------------------------------------------------------- /utility/lookupvdat.txt: -------------------------------------------------------------------------------- 1 | b B 2 | m M 3 | p P 4 | w W 5 | f F 6 | v V 7 | r R 8 | r2 R 9 | r3 R 10 | er ER 11 | er2 ER 12 | dh DH 13 | th TH 14 | sh SH 15 | jh JH 16 | ch CH 17 | s S 18 | z Z 19 | d D 20 | d2 D 21 | l L 22 | l2 L 23 | n N 24 | t T 25 | ow OW 26 | uw UW 27 | ey EY 28 | ae AE 29 | aa AA 30 | aa2 AA 31 | iy IY 32 | y Y 33 | ah AH 34 | ao AO 35 | ax AH 36 | ax2 AH 37 | eh EH 38 | ih IH 39 | ih2 IH 40 | uh UH 41 | g G 42 | g2 G 43 | hh HH 44 | hh2 HH 45 | c K 46 | nx NG 47 | zh ZH 48 | h HH 49 | k K 50 | ay AY 51 | ng NG 52 | aw AW 53 | oy OY 54 | SIL -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Phomeme 2 | Simple sentence mixing tool. 3 | This project will become public once it works well enough to be useful. 4 | 5 | ## Experiments 6 | [![YouTube Wants You Gone](https://img.youtube.com/vi/B6BDVJbobwY/mqdefault.jpg)](https://youtu.be/B6BDVJbobwY) 7 | [![See D Again](https://img.youtube.com/vi/MHPQJgmPjko/mqdefault.jpg)](https://youtu.be/MHPQJgmPjko) 8 | [![Bon Appétit](https://img.youtube.com/vi/oEyb8g7Ze-w/mqdefault.jpg)](https://youtu.be/oEyb8g7Ze-w) 9 | [![Pitch Aligned](https://img.youtube.com/vi/aPsaWqvyO6E/mqdefault.jpg)](https://youtu.be/aPsaWqvyO6E) 10 | -------------------------------------------------------------------------------- /utility/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Phomeme", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "MysteryPancake", 11 | "license": "MIT", 12 | "dependencies": { 13 | "@soundtouchjs/audio-worklet": "^0.1.5", 14 | "cmake-js": "^4.0.0", 15 | "node-wav": "0.0.2", 16 | "pitchfinder": "^2.0.9", 17 | "pkg-config": "^1.1.1", 18 | "pocketsphinx": "^5.0.7", 19 | "wav-decoder": "^1.3.0", 20 | "wavefile": "^11.0.0" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /utility/subtitle.command: -------------------------------------------------------------------------------- 1 | cd Downloads 2 | 3 | while true; do 4 | 5 | echo -n 'LINK: ' 6 | read link 7 | 8 | # FOR YOUTUBE: 9 | # youtube-dl -f bestvideo+bestaudio --write-sub --write-auto-sub --all-subs $link 10 | 11 | # FOR YOUTUBE MP4: 12 | # youtube-dl -f 'bestvideo[ext=mp4]+bestaudio[ext=m4a]/bestvideo+bestaudio' --merge-output-format mp4 --write-sub --write-auto-sub --all-subs $link 13 | 14 | # FOR YOUTUBE WAV: 15 | youtube-dl -f bestaudio -x --audio-format wav --add-metadata --write-sub --write-auto-sub --all-subs $link 16 | 17 | # FOR SUBTITLES ONLY: 18 | # youtube-dl --skip-download --write-sub --write-auto-sub --all-subs $link 19 | 20 | done -------------------------------------------------------------------------------- /replacewavwithmp4.jsx: -------------------------------------------------------------------------------- 1 | (function() { 2 | 3 | function replaceWAVWithMP4() { 4 | for (var i = 1; i <= app.project.numItems; i++) { 5 | var item = app.project.item(i); 6 | if (item instanceof FootageItem) { 7 | var file = item.file; 8 | if (file) { 9 | var name = file.name; 10 | if (name) { 11 | var extension = file.name.split(".").pop(); 12 | if (extension === "wav") { 13 | var mp4 = new File(file.fullName.replace(".wav", ".mp4")); 14 | if (mp4.exists) { 15 | item.replace(mp4); 16 | } 17 | } 18 | } 19 | } 20 | } 21 | } 22 | } 23 | 24 | replaceWAVWithMP4(); 25 | 26 | })(); -------------------------------------------------------------------------------- /utility/createindex.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const fs = require("fs"); 4 | 5 | function createIndex(path) { 6 | const transcripts = []; 7 | const files = fs.readdirSync(path); 8 | for (let i = 0; i < files.length; i++) { 9 | if (files[i].split(".").pop() === "json") { 10 | const name = files[i].replace(/\.[^/.]+$/, ""); 11 | transcripts.push({ 12 | audio: "https://mysterypancake.github.io/Phomeme-Rosen/series1/" + name.replace(/ /g, "_") + ".wav", 13 | name: name.split(" - ")[0], 14 | transcript: "https://mysterypancake.github.io/Phomeme-Rosen/series1/" + name.replace(/ /g, "_") + ".json", 15 | video: "https://mysterypancake.github.io/Phomeme-Rosen/series1/" + name.replace(/ /g, "_") + ".mp4" 16 | }); 17 | } 18 | } 19 | fs.writeFileSync("index.json", JSON.stringify(transcripts, undefined, "\t")); 20 | } 21 | 22 | createIndex("./PRESETS/rosen/series1"); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Phomeme", 3 | "version": "1.0.0", 4 | "description": "Simple sentence mixing tool.", 5 | "main": "main.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "electron .", 9 | "pack": "electron-builder --dir", 10 | "dist": "electron-builder", 11 | "dist-mac": "electron-builder --macos", 12 | "dist-win": "electron-builder --win --x64" 13 | }, 14 | "build": { 15 | "appId": "phomeme", 16 | "mac": { 17 | "category": "public.app-category.music" 18 | }, 19 | "win": { 20 | "target": "NSIS" 21 | } 22 | }, 23 | "keywords": [], 24 | "author": "MysteryPancake", 25 | "license": "MIT", 26 | "devDependencies": { 27 | "electron": "^15.3.1", 28 | "electron-builder": "^22.4.1" 29 | }, 30 | "dependencies": { 31 | "python-shell": "^1.0.8" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 MysteryPancake 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /main.js: -------------------------------------------------------------------------------- 1 | const { app, BrowserWindow } = require("electron"); 2 | //const { PythonShell } = require("python-shell"); 3 | 4 | function createWindow() { 5 | let mainWindow = new BrowserWindow({ 6 | backgroundColor: "#000000", 7 | title: "Phomeme", 8 | show: false, 9 | webPreferences: { 10 | nodeIntegration: true, 11 | webSecurity: false 12 | } 13 | }); 14 | mainWindow.loadFile("editor.html"); 15 | mainWindow.maximize(); 16 | //mainWindow.webContents.openDevTools(); 17 | mainWindow.on("ready-to-show", function() { 18 | mainWindow.show(); 19 | mainWindow.focus(); 20 | }); 21 | /*PythonShell.run("align.py", { 22 | args: ["gentle/examples/data/lucier.mp3", "gentle/examples/data/lucier.txt"], 23 | scriptPath: "gentle" 24 | }, function(err, results) { 25 | if (err) { 26 | console.log(err); 27 | } 28 | console.log(results); 29 | });*/ 30 | } 31 | 32 | app.whenReady().then(createWindow); 33 | 34 | app.on("window-all-closed", function() { 35 | if (process.platform !== "darwin") { 36 | app.quit(); 37 | } 38 | }); 39 | 40 | app.on("activate", function () { 41 | if (BrowserWindow.getAllWindows().length === 0) { 42 | createWindow(); 43 | } 44 | }); -------------------------------------------------------------------------------- /scripts/TextToSpeech.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const textToSpeech = (function() { 4 | 5 | function addClips(targets, phones, mix, params, length, func) { 6 | for (let i = 0; i < targets.length; i++) { 7 | const target = targets[i]; 8 | const words = phones.get(target.label); 9 | if (words) { 10 | // For now, choose first candidate (editor should allow multiple choices) 11 | const match = triphone(words, target, params)[0]; 12 | mix.addClip(match.file, target.label, length - params.overlapStart, length + match.dur + params.overlapEnd, match.start - params.overlapStart, match.end + params.overlapEnd, 1, 0, 1); 13 | length += match.dur; 14 | } else { 15 | length = func(target, length) || length; 16 | } 17 | } 18 | return length; 19 | } 20 | 21 | return function(source, destination, params) { 22 | // Convert input and output to intermediate format 23 | const input = convert(source, params); 24 | const output = convert(destination, params); 25 | const mix = new AuditionSession("session", 32, params.sampleRate); 26 | if (params.matchWords && input.words && output.words) { 27 | // Attempt word level alignment 28 | addClips(output.words, input.words, mix, params, 0, function(target, length) { 29 | console.log(`USING PHONES FOR: ${target.label}`); 30 | if (target.phones) { 31 | // Fallback to phoneme level alignment if word is absent from input 32 | return addClips(target.phones, input.phones, mix, params, length, function(data) { 33 | // Move on if no words or phonemes match 34 | console.log(`MISSING PHONE: ${data.label}`); 35 | }); 36 | } else { 37 | // Move on if no phonemes are available 38 | console.log(`MISSING DEFINITION: ${target.label}`); 39 | } 40 | }); 41 | } else { 42 | // Attempt phoneme level alignment 43 | addClips(output.phones, input.phones, mix, params, 0, function(target) { 44 | // Move on if no phonemes match 45 | console.log(`MISSING PHONE: ${target.label}`); 46 | }); 47 | } 48 | return mix.compile(); 49 | }; 50 | 51 | }()); -------------------------------------------------------------------------------- /utility/lookupgentle.txt: -------------------------------------------------------------------------------- 1 | #0 SIL 2 | #1 SIL 3 | #2 SIL 4 | #3 SIL 5 | #4 SIL 6 | #5 SIL 7 | #6 SIL 8 | #7 SIL 9 | #8 SIL 10 | #9 SIL 11 | SIL 12 | aa_B AA 13 | aa_E AA 14 | aa_I AA 15 | aa_S AA 16 | ae_B AE 17 | ae_E AE 18 | ae_I AE 19 | ae_S AE 20 | ah_B AH 21 | ah_E AH 22 | ah_I AH 23 | ah_S AH 24 | ao_B AO 25 | ao_E AO 26 | ao_I AO 27 | ao_S AO 28 | aw_B AW 29 | aw_E AW 30 | aw_I AW 31 | aw_S AW 32 | ay_B AY 33 | ay_E AY 34 | ay_I AY 35 | ay_S AY 36 | b_B B 37 | b_E B 38 | b_I B 39 | b_S B 40 | ch_B CH 41 | ch_E CH 42 | ch_I CH 43 | ch_S CH 44 | d_B D 45 | d_E D 46 | d_I D 47 | d_S D 48 | dh_B DH 49 | dh_E DH 50 | dh_I DH 51 | dh_S DH 52 | eh_B EH 53 | eh_E EH 54 | eh_I EH 55 | eh_S EH 56 | er_B ER 57 | er_E ER 58 | er_I ER 59 | er_S ER 60 | ey_B EY 61 | ey_E EY 62 | ey_I EY 63 | ey_S EY 64 | f_B F 65 | f_E F 66 | f_I F 67 | f_S F 68 | g_B G 69 | g_E G 70 | g_I G 71 | g_S G 72 | hh_B HH 73 | hh_E HH 74 | hh_I HH 75 | hh_S HH 76 | ih_B IH 77 | ih_E IH 78 | ih_I IH 79 | ih_S IH 80 | iy_B IY 81 | iy_E IY 82 | iy_I IY 83 | iy_S IY 84 | jh_B JH 85 | jh_E JH 86 | jh_I JH 87 | jh_S JH 88 | k_B K 89 | k_E K 90 | k_I K 91 | k_S K 92 | l_B L 93 | l_E L 94 | l_I L 95 | l_S L 96 | laughter OOV 97 | laughter_B OOV 98 | laughter_E OOV 99 | laughter_I OOV 100 | laughter_S OOV 101 | m_B M 102 | m_E M 103 | m_I M 104 | m_S M 105 | n_B N 106 | n_E N 107 | n_I N 108 | n_S N 109 | ng_B NG 110 | ng_E NG 111 | ng_I NG 112 | ng_S NG 113 | noise SIL 114 | noise_B SIL 115 | noise_E SIL 116 | noise_I SIL 117 | noise_S SIL 118 | oov OOV 119 | oov_B OOV 120 | oov_E OOV 121 | oov_I OOV 122 | oov_S OOV 123 | ow_B OW 124 | ow_E OW 125 | ow_I OW 126 | ow_S OW 127 | oy_B OY 128 | oy_E OY 129 | oy_I OY 130 | oy_S OY 131 | p_B P 132 | p_E P 133 | p_I P 134 | p_S P 135 | r_B R 136 | r_E R 137 | r_I R 138 | r_S R 139 | s_B S 140 | s_E S 141 | s_I S 142 | s_S S 143 | sh_B SH 144 | sh_E SH 145 | sh_I SH 146 | sh_S SH 147 | sil SIL 148 | sil_B SIL 149 | sil_E SIL 150 | sil_I SIL 151 | sil_S SIL 152 | t_B T 153 | t_E T 154 | t_I T 155 | t_S T 156 | th_B TH 157 | th_E TH 158 | th_I TH 159 | th_S TH 160 | uh_B UH 161 | uh_E UH 162 | uh_I UH 163 | uh_S UH 164 | uw_B UW 165 | uw_E UW 166 | uw_I UW 167 | uw_S UW 168 | v_B V 169 | v_E V 170 | v_I V 171 | v_S V 172 | w_B W 173 | w_E W 174 | w_I W 175 | w_S W 176 | y_B Y 177 | y_E Y 178 | y_I Y 179 | y_S Y 180 | z_B Z 181 | z_E Z 182 | z_I Z 183 | z_S Z 184 | zh_B ZH 185 | zh_E ZH 186 | zh_I ZH 187 | zh_S ZH -------------------------------------------------------------------------------- /scripts/BufferToFile.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const bufferToFile = (function() { 4 | 5 | function interleave(inputL, inputR) { 6 | const length = inputL.length + inputR.length; 7 | const result = new Float32Array(length); 8 | let index = 0; 9 | let inputIndex = 0; 10 | while (index < length) { 11 | result[index++] = inputL[inputIndex]; 12 | result[index++] = inputR[inputIndex]; 13 | inputIndex++; 14 | } 15 | return result; 16 | } 17 | 18 | function floatTo16Bit(output, offset, input) { 19 | for (let i = 0; i < input.length; i++, offset += 2) { 20 | const s = Math.max(-1, Math.min(1, input[i])); 21 | output.setInt16(offset, s < 0 ? s * 0x8000 : s * 0x7FFF, true); 22 | } 23 | } 24 | 25 | function floatTo32Bit(output, offset, input) { 26 | for (let i = 0; i < input.length; i++, offset += 4) { 27 | output.setFloat32(offset, input[i], true); 28 | } 29 | } 30 | 31 | function writeString(view, offset, str) { 32 | for (let i = 0; i < str.length; i++) { 33 | view.setUint8(offset + i, str.charCodeAt(i)); 34 | } 35 | } 36 | 37 | function encodeWAV(samples, format, sampleRate, numChannels, bitDepth) { 38 | const bytesPerSample = bitDepth / 8; 39 | const blockAlign = numChannels * bytesPerSample; 40 | const buffer = new ArrayBuffer(44 + samples.length * bytesPerSample); 41 | const view = new DataView(buffer); 42 | writeString(view, 0, "RIFF"); 43 | view.setUint32(4, 36 + samples.length * bytesPerSample, true); 44 | writeString(view, 8, "WAVE"); 45 | writeString(view, 12, "fmt "); 46 | view.setUint32(16, 16, true); 47 | view.setUint16(20, format, true); 48 | view.setUint16(22, numChannels, true); 49 | view.setUint32(24, sampleRate, true); 50 | view.setUint32(28, sampleRate * blockAlign, true); 51 | view.setUint16(32, blockAlign, true); 52 | view.setUint16(34, bitDepth, true); 53 | writeString(view, 36, "data"); 54 | view.setUint32(40, samples.length * blockAlign, true); 55 | if (format === 1) { 56 | floatTo16Bit(view, 44, samples); 57 | } else { 58 | floatTo32Bit(view, 44, samples); 59 | } 60 | return view; 61 | } 62 | 63 | return function(name, buffer, float32) { 64 | let interleaved; 65 | const numChannels = Math.min(buffer.numberOfChannels, 2); 66 | if (numChannels === 2) { 67 | interleaved = interleave(buffer.getChannelData(0), buffer.getChannelData(1)); 68 | } else { 69 | interleaved = buffer.getChannelData(0); 70 | } 71 | const view = encodeWAV(interleaved, float32 ? 3 : 1, buffer.sampleRate, numChannels, float32 ? 32 : 16); 72 | return new File([view], name + ".wav", { type: "audio/wav" }); 73 | }; 74 | 75 | }()); -------------------------------------------------------------------------------- /style.css: -------------------------------------------------------------------------------- 1 | * { 2 | color: white; 3 | font-family: "Courier New", Courier, monospace; 4 | } 5 | 6 | html, body { 7 | background-color: #061306; 8 | } 9 | 10 | h1 { 11 | text-align: center; 12 | } 13 | 14 | h1, h2, h3, img, input, select { 15 | margin: 6px 0; 16 | } 17 | 18 | audio, img, input[type="image"] { 19 | outline: none; 20 | vertical-align: middle; 21 | } 22 | 23 | input[type="checkbox"], input[type="file"], input[type="submit"], label, select { 24 | cursor: pointer; 25 | } 26 | 27 | input[type="number"]:hover, input[type="submit"]:hover, select:hover, code:hover { 28 | border: 1px solid #80FF80; 29 | } 30 | 31 | input[type="number"]:active, input[type="submit"]:active, select:focus, code:focus { 32 | border: 1px solid white; 33 | } 34 | 35 | input[type="number"], input[type="submit"], select, code { 36 | background-color: transparent; 37 | border: 1px solid #00FF00; 38 | border-radius: 8px; 39 | box-sizing: border-box; 40 | width: 100%; 41 | } 42 | 43 | input[type="number"], select { 44 | font-size: medium; 45 | height: 32px; 46 | padding-left: 8px; 47 | } 48 | 49 | input[type="submit"] { 50 | box-shadow: 0 0 16px #00FF00; 51 | font-size: large; 52 | height: 48px; 53 | } 54 | 55 | input[type="submit"]:hover { 56 | box-shadow: 0 0 16px #80FF80; 57 | } 58 | 59 | input[type="submit"]:active { 60 | box-shadow: none; 61 | outline: none; 62 | } 63 | 64 | form, #result { 65 | border: 2px solid #00FF00; 66 | border-radius: 8px; 67 | box-shadow: 0 0 32px #00FF00; 68 | margin: 8px 8%; 69 | padding: 12px 24px; 70 | } 71 | 72 | code { 73 | display: block; 74 | font-size: medium; 75 | height: 16em; 76 | margin: 6px 0 12px 0px; 77 | overflow: auto; 78 | padding: 8px; 79 | resize: vertical; 80 | } 81 | 82 | code:empty:before { 83 | content: attr(data-placeholder); 84 | color: gray; 85 | } 86 | 87 | #destination, #disillusion, #inputPlayer, #options, #outputPlayer, #result, #rosen, #source { 88 | display: none; 89 | } 90 | 91 | code > span { 92 | color: gray; 93 | } 94 | 95 | #spinner { 96 | animation: spin 1s linear infinite; 97 | border: 8px solid #008000; 98 | border-radius: 50%; 99 | border-top: 8px solid #00FF00; 100 | float: right; 101 | height: 16px; 102 | -webkit-animation: spin 1s linear infinite; 103 | width: 16px; 104 | } 105 | 106 | @-webkit-keyframes spin { 107 | 0% { -webkit-transform: rotate(0deg); } 108 | 100% { -webkit-transform: rotate(360deg); } 109 | } 110 | 111 | @keyframes spin { 112 | 0% { transform: rotate(0deg); } 113 | 100% { transform: rotate(360deg); } 114 | } -------------------------------------------------------------------------------- /utility/lookup.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const fs = require("fs"); 4 | const convert = require("./convert.js"); 5 | 6 | /*function crossLookup() { 7 | const lookup = {}; 8 | const converted1 = convert({ data: JSON.parse(fs.readFileSync("SOURCE/TextGridExamples/input.json", "utf8")), type: "json", file: "input.wav" }, { matchGeneral: true }); 9 | const converted2 = convert({ data: JSON.parse(fs.readFileSync("SOURCE/TextGridExamples/input.TextGrid", "utf8")), type: "TextGrid", file: "input.wav" }, { matchGeneral: true }); 10 | for (let word in converted1.words) { 11 | if (!converted1.words.hasOwnProperty(word)) continue; 12 | const word1 = converted1.words[word]; 13 | const word2 = converted2.words[word]; 14 | if (word1 && word2) { 15 | for (let i = 0; i < word1.length; i++) { 16 | const phone1 = word1[i]; 17 | const phone2 = word2[i]; 18 | if (phone1 && phone2) { 19 | if (phone1.phones.length === phone2.phones.length) { 20 | for (let j = 0; j < phone1.phones.length; j++) { 21 | const data1 = phone1.phones[j]; 22 | const data2 = phone2.phones[j]; 23 | if (data1 && data2) { 24 | lookup[data2.label] = lookup[data2.label] || {}; 25 | lookup[data2.label][data1.label] = lookup[data2.label][data1.label] || 1; 26 | lookup[data2.label][data1.label]++; 27 | } 28 | } 29 | } 30 | } 31 | } 32 | } 33 | } 34 | const result = {}; 35 | for (let data in lookup) { 36 | if (!lookup.hasOwnProperty(data)) continue; 37 | let final; 38 | let total = 0; 39 | let largest = 0; 40 | for (let symbol in lookup[data]) { 41 | if (!lookup[data].hasOwnProperty(symbol)) continue; 42 | const count = lookup[data][symbol]; 43 | largest = Math.max(largest, count); 44 | if (count >= largest) { 45 | final = symbol; 46 | } 47 | total += count; 48 | } 49 | console.log(data + " " + final); 50 | result[data] = { phone: final, accuracy: Math.floor(largest / total * 100) + "%", alternatives: lookup[data] }; 51 | } 52 | return result; 53 | }*/ 54 | 55 | function generateLookup() { 56 | const result = {}; 57 | const categories = ["gentle", "vdat", "webmaus"]; 58 | for (let i = 0; i < categories.length; i++) { 59 | const name = categories[i]; 60 | result[name] = {}; 61 | const lookup = fs.readFileSync("lookup" + name + ".txt", "utf8"); 62 | const lines = lookup.split("\n"); 63 | for (let j = 0; j < lines.length; j++) { 64 | const delimit = lines[j].split(" "); 65 | result[name][delimit[0]] = delimit[1]; 66 | } 67 | } 68 | return JSON.stringify(result, undefined, "\t"); 69 | } 70 | 71 | //fs.writeFileSync("lookup.json", generateLookup()); 72 | console.log(generateLookup()); -------------------------------------------------------------------------------- /scripts/AuditionSession.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const AuditionSession = (function() { 4 | 5 | function xmlSafe(str) { 6 | return str.replace(/&/g, "&").replace(//g, ">").replace(/"/g, """).replace(/'/g, "'"); 7 | } 8 | 9 | return function(name = "Untitled Session", bitDepth = 16, sampleRate = 44100) { 10 | this.name = name; 11 | this.depth = bitDepth; 12 | this.rate = sampleRate; 13 | this.before = `\n\n\n\t\n\t\t${this.name}.sesx\n\t\t\n\t\t\t\n\t\t\t\t\n\t\t\t\t\tPhomeme\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\n`; 14 | this.middle = "\t\t\t\n\t\t\n\t\n\t\n"; 15 | this.after = "\t\n"; 16 | this.files = []; 17 | this.addFile = function(path) { 18 | return this.files.push({ 19 | xml: `\t\t\n`, 20 | path: path 21 | }); 22 | }; 23 | this.clips = []; 24 | this.addClip = function(path, label, start, end, sourceStart, sourceEnd, stretch = 1, pitchOffset = 0, volume = 1) { 25 | let id; 26 | for (let i = 0; i < this.files.length; i++) { 27 | if (this.files[i].path === path) { 28 | id = i; 29 | } 30 | } 31 | if (id === undefined) { 32 | id = this.addFile(path) - 1; 33 | } 34 | this.clips.push(`\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\n`); 35 | }; 36 | this.compile = function() { 37 | let result = this.before; 38 | for (let i = 0; i < this.clips.length; i++) { 39 | result += this.clips[i]; 40 | } 41 | result += this.middle; 42 | for (let j = 0; j < this.files.length; j++) { 43 | result += this.files[j].xml; 44 | } 45 | result += this.after; 46 | return result; 47 | }; 48 | }; 49 | 50 | }()); -------------------------------------------------------------------------------- /utility/pitch.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const fs = require("fs"); 4 | const wav = require("wav-decoder"); 5 | 6 | function autoCorrelate(buffer, sampleRate) { 7 | let size = buffer.length; 8 | let rms = 0; 9 | for (let i = 0; i < size; i++) { 10 | const val = buffer[i]; 11 | rms += val * val; 12 | } 13 | rms = Math.sqrt(rms / size); 14 | if (rms < 0.01) { 15 | return -1; 16 | } 17 | let r1 = 0; 18 | let r2 = size - 1; 19 | const thres = 0.2; 20 | for (let i = 0; i < size / 2; i++) { 21 | if (Math.abs(buffer[i]) < thres) { 22 | r1 = i; 23 | break; 24 | } 25 | } 26 | for (let i = 1; i < size / 2; i++) { 27 | if (Math.abs(buffer[size - i]) < thres) { 28 | r2 = size - i; 29 | break; 30 | } 31 | } 32 | buffer = buffer.slice(r1, r2); 33 | size = buffer.length; 34 | const c = new Array(size).fill(0); 35 | for (let i = 0; i < size; i++) { 36 | for (let j = 0; j < size - i; j++) { 37 | c[i] += buffer[j] * buffer[j + i]; 38 | } 39 | } 40 | let d = 0; 41 | while (c[d] > c[d + 1]) { 42 | d++; 43 | } 44 | let maxval = -1; 45 | let maxpos = -1; 46 | for (let i = d; i < size; i++) { 47 | if (c[i] > maxval) { 48 | maxval = c[i]; 49 | maxpos = i; 50 | } 51 | } 52 | let T0 = maxpos; 53 | const x1 = c[T0 - 1]; 54 | const x2 = c[T0]; 55 | const x3 = c[T0 + 1]; 56 | const a = (x1 + x3 - 2 * x2) / 2; 57 | const b = (x3 - x1) / 2; 58 | if (a) { 59 | T0 -= b / (2 * a); 60 | } 61 | return sampleRate / T0; 62 | } 63 | 64 | const noteStrings = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"]; 65 | 66 | function noteFromPitch(frequency) { 67 | return Math.round(12 * Math.log2(frequency / 440)) + 69; 68 | } 69 | 70 | const sampleSize = 1024; 71 | 72 | function pitch(file) { 73 | const transcript = { 74 | transcript: "", 75 | words: [] 76 | }; 77 | wav.decode(fs.readFileSync(file + ".wav")).then(function(wave) { 78 | const buffer = wave.channelData[0]; 79 | const sampleRate = wave.sampleRate; 80 | let prev; 81 | for (let i = 0; i < buffer.length / sampleSize; i++) { 82 | const section = buffer.slice(i * sampleSize, (i + 1) * sampleSize); 83 | const result = noteFromPitch(autoCorrelate(section, sampleRate)); 84 | const note = noteStrings[result % 12]; 85 | if (result < prev + 1 && result > prev - 1) { 86 | const word = transcript.words[transcript.words.length - 1]; 87 | const last = word.phones[word.phones.length - 1]; 88 | if (last.phone === note) { 89 | last.duration += sampleSize / sampleRate; 90 | } else { 91 | word.phones.push({ 92 | duration: sampleSize / sampleRate, 93 | phone: note 94 | }); 95 | } 96 | word.end += sampleSize / sampleRate; 97 | } else { 98 | transcript.words.push({ 99 | case: isNaN(result) ? "not-found-in-audio" : "success", 100 | end: ((i + 1) * sampleSize) / sampleRate, 101 | phones: [{ duration: sampleSize / sampleRate, phone: note }], 102 | start: (i * sampleSize) / sampleRate, 103 | alignedWord: result.toString(), 104 | word: result.toString() 105 | }); 106 | } 107 | prev = result; 108 | } 109 | fs.writeFileSync(file + ".json", JSON.stringify(transcript, undefined, "\t")); 110 | }); 111 | } 112 | 113 | pitch("inputmusic"); 114 | pitch("outputmusic"); -------------------------------------------------------------------------------- /scripts/Triphone.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const triphone = (function() { 4 | 5 | const normalizedMethods = { // Maybe try normalizing to minimum as well as maximum 6 | first: function(a, b, max) { // Sort by earliest to latest start time 7 | return (a.start - b.start) / max.start; 8 | }, 9 | last: function(a, b, max) { // Sort by latest to earliest start time 10 | return (b.start - a.start) / max.start; 11 | }, 12 | duration: function(a, b, max, target) { // Sort by closest duration 13 | return (Math.abs(target.dur - a.dur) - Math.abs(target.dur - b.dur)) / max.diff; 14 | }, 15 | shortest: function(a, b, max) { // Sort by shortest to longest duration 16 | return (a.dur - b.dur) / max.dur; 17 | }, 18 | longest: function(a, b, max) { // Sort by longest to shortest duration 19 | return (b.dur - a.dur) / max.dur; 20 | } 21 | }; 22 | 23 | function normalizedContext(a, b, max) { 24 | let normal = 0; 25 | const maxTotal = max.prevTotal + max.nextTotal; 26 | if (maxTotal > 0) { 27 | normal = ((b.prevTotal + b.nextTotal) - (a.prevTotal + a.nextTotal)) / maxTotal; 28 | } 29 | return normal; 30 | } 31 | 32 | function sequenceTotal(direction, phone, target, params) { 33 | let seqTotal = 0; 34 | let sourcePhone = phone[direction]; 35 | let targetPhone = target[direction]; 36 | if (sourcePhone === undefined && targetPhone === undefined) { 37 | seqTotal++; 38 | } else { 39 | while (sourcePhone && targetPhone && sourcePhone.label === targetPhone.label) { 40 | seqTotal++; 41 | // Prevent infinite recursion 42 | if (seqTotal >= params.contextDepth) { 43 | break; 44 | } 45 | sourcePhone = sourcePhone[direction]; 46 | targetPhone = targetPhone[direction]; 47 | if (sourcePhone === undefined && targetPhone === undefined) { 48 | seqTotal++; 49 | break; 50 | } 51 | } 52 | } 53 | return seqTotal; 54 | } 55 | 56 | function updateMax(prop, phone, max) { 57 | if (max[prop] === undefined || phone[prop] > max[prop]) { 58 | max[prop] = phone[prop]; 59 | } 60 | } 61 | 62 | function calculateMaxes(phone, target, params, max) { 63 | phone.prevTotal = sequenceTotal("prev", phone, target, params); 64 | updateMax("prevTotal", phone, max); 65 | phone.nextTotal = sequenceTotal("next", phone, target, params); 66 | updateMax("nextTotal", phone, max); 67 | switch (params.method) { 68 | case "first": 69 | case "last": 70 | updateMax("start", phone, max); 71 | break; 72 | case "duration": { 73 | const diff = Math.abs(target.dur - phone.dur); 74 | if (max.diff === undefined || diff > max.diff) { 75 | max.diff = diff; 76 | } 77 | break; 78 | } 79 | case "shortest": 80 | case "longest": 81 | updateMax("dur", phone, max); 82 | break; 83 | } 84 | } 85 | 86 | return function(phones, target, params) { 87 | const diphones = []; 88 | const triphones = []; 89 | const max = { prevTotal: 0, nextTotal: 0 }; 90 | for (let i = 0; i < phones.length; i++) { 91 | const phone = phones[i]; 92 | calculateMaxes(phone, target, params, max); 93 | const backwardMatch = params.matchOneBackward && phone.prevTotal > 0; 94 | const forwardMatch = params.matchOneForward && phone.nextTotal > 0; 95 | if (backwardMatch && forwardMatch) { 96 | triphones.push(phone); 97 | } else if (backwardMatch || forwardMatch) { 98 | diphones.push(phone); 99 | } 100 | } 101 | let finalPhones = phones; 102 | if (triphones.length > 0) { 103 | finalPhones = triphones; 104 | } else if (diphones.length > 0) { 105 | finalPhones = diphones; 106 | } 107 | finalPhones.sort(function(a, b) { 108 | const method = normalizedMethods[params.method](a, b, max, target); 109 | const context = normalizedContext(a, b, max); 110 | return (method * params.methodWeight) + (context * params.contextWeight); 111 | }); 112 | return finalPhones; 113 | }; 114 | 115 | }()); -------------------------------------------------------------------------------- /scripts/SpeechToSpeech.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const speechToSpeech = (function() { 4 | 5 | function addClip(target, phones, mix, params, func) { 6 | if (phones) { 7 | // For now, choose first candidate (editor should allow multiple choices) 8 | const match = triphone(phones, target, params)[0]; 9 | let stretch = 1; 10 | if (params.transferDuration) { 11 | stretch = Math.min(8, target.dur / match.dur); 12 | } 13 | let startOverlap = params.overlapStart; 14 | let endOverlap = params.overlapEnd; 15 | if (startOverlap + endOverlap > match.dur * stretch) { 16 | startOverlap = 0; 17 | endOverlap = 0; 18 | } 19 | let pitchOffset = 0; 20 | if (params.transferPitch && target.pitch !== undefined && match.pitch !== undefined) { 21 | pitchOffset = target.pitch - match.pitch; 22 | } 23 | let volume = 1; 24 | if (params.transferVolume && target.volume !== undefined && match.volume !== undefined) { 25 | volume += target.volume - match.volume; 26 | } 27 | mix.addClip(match.file, target.label, target.start - startOverlap, target.end + endOverlap, (match.start * stretch) - startOverlap, (match.end * stretch) + endOverlap, stretch, pitchOffset, volume); 28 | } else { 29 | func(target); 30 | } 31 | } 32 | 33 | return function(source, destination, params) { 34 | // Convert input and output to intermediate format 35 | const input = convert(source, params); 36 | const output = convert(destination, params); 37 | const mix = new AuditionSession("session", 32, params.sampleRate); 38 | if (params.matchWords && input.words && output.words) { 39 | // Attempt word level alignment 40 | output.words.forEach((word, label) => { 41 | for (let i = 0; i < word.length; i++) { 42 | addClip(word[i], input.words.get(label), mix, params, function(target) { 43 | // Fallback to phoneme level alignment if word is absent from input 44 | console.log(`USING PHONES FOR: ${target.label}`); 45 | for (let j = 0; j < target.phones.length; j++) { 46 | const data = target.phones[j]; 47 | addClip(data, input.phones[data.label], mix, params, function(phone) { 48 | // Move on if no words or phonemes match 49 | console.log(`MISSING PHONE: ${phone.label}`); 50 | }); 51 | } 52 | }); 53 | } 54 | }); 55 | } else { 56 | // Attempt phoneme level alignment 57 | output.phones.forEach((phone, label) => { 58 | for (let j = 0; j < phone.length; j++) { 59 | addClip(phone[j], input.phones.get(label), mix, params, function(target) { 60 | // Temporary code for testing pitch matching mode 61 | //if (target.pitch === undefined) { 62 | // Move on if no phonemes match 63 | console.log(`MISSING PHONE: ${target.label}`); 64 | /*} else { 65 | // For now, assume MIDI pitch matching mode (temporary) 66 | // This code is garbage, make a better solution 67 | const availablePitches = Object.keys(input.phones); 68 | let smallestDiff; 69 | let closestPitch; 70 | let closestPitch2; 71 | for (let k = 0; k < availablePitches.length; k++) { 72 | const approximatePitch = availablePitches[k]; 73 | const diff = Math.abs(target.pitch - parseInt(approximatePitch)); 74 | if (smallestDiff === undefined || diff < smallestDiff) { 75 | smallestDiff = diff; 76 | closestPitch = approximatePitch; 77 | closestPitch2 = undefined; 78 | } else if (diff === smallestDiff) { 79 | closestPitch2 = approximatePitch; 80 | } 81 | } 82 | // Two candidates can have the same difference 83 | let joinedArray = input.phones[closestPitch]; 84 | if (closestPitch2) { 85 | joinedArray = joinedArray.concat(input.phones[closestPitch2]); 86 | } 87 | console.log(`USING ALTERNATIVE FOR: ${target.label} (${smallestDiff} SEMITONE DIFFERENCE)`); 88 | addClip(phone[j], joinedArray, mix, params, function(target) { 89 | console.log("MISSING ALTERNATIVES!"); 90 | }); 91 | }*/ 92 | }); 93 | } 94 | }); 95 | } 96 | return mix.compile(); 97 | }; 98 | 99 | }()); -------------------------------------------------------------------------------- /lookup.json: -------------------------------------------------------------------------------- 1 | { 2 | "gentle": { 3 | "#0": "SIL", 4 | "#1": "SIL", 5 | "#2": "SIL", 6 | "#3": "SIL", 7 | "#4": "SIL", 8 | "#5": "SIL", 9 | "#6": "SIL", 10 | "#7": "SIL", 11 | "#8": "SIL", 12 | "#9": "SIL", 13 | "": "SIL", 14 | "aa_B": "AA", 15 | "aa_E": "AA", 16 | "aa_I": "AA", 17 | "aa_S": "AA", 18 | "ae_B": "AE", 19 | "ae_E": "AE", 20 | "ae_I": "AE", 21 | "ae_S": "AE", 22 | "ah_B": "AH", 23 | "ah_E": "AH", 24 | "ah_I": "AH", 25 | "ah_S": "AH", 26 | "ao_B": "AO", 27 | "ao_E": "AO", 28 | "ao_I": "AO", 29 | "ao_S": "AO", 30 | "aw_B": "AW", 31 | "aw_E": "AW", 32 | "aw_I": "AW", 33 | "aw_S": "AW", 34 | "ay_B": "AY", 35 | "ay_E": "AY", 36 | "ay_I": "AY", 37 | "ay_S": "AY", 38 | "b_B": "B", 39 | "b_E": "B", 40 | "b_I": "B", 41 | "b_S": "B", 42 | "ch_B": "CH", 43 | "ch_E": "CH", 44 | "ch_I": "CH", 45 | "ch_S": "CH", 46 | "d_B": "D", 47 | "d_E": "D", 48 | "d_I": "D", 49 | "d_S": "D", 50 | "dh_B": "DH", 51 | "dh_E": "DH", 52 | "dh_I": "DH", 53 | "dh_S": "DH", 54 | "eh_B": "EH", 55 | "eh_E": "EH", 56 | "eh_I": "EH", 57 | "eh_S": "EH", 58 | "er_B": "ER", 59 | "er_E": "ER", 60 | "er_I": "ER", 61 | "er_S": "ER", 62 | "ey_B": "EY", 63 | "ey_E": "EY", 64 | "ey_I": "EY", 65 | "ey_S": "EY", 66 | "f_B": "F", 67 | "f_E": "F", 68 | "f_I": "F", 69 | "f_S": "F", 70 | "g_B": "G", 71 | "g_E": "G", 72 | "g_I": "G", 73 | "g_S": "G", 74 | "hh_B": "HH", 75 | "hh_E": "HH", 76 | "hh_I": "HH", 77 | "hh_S": "HH", 78 | "ih_B": "IH", 79 | "ih_E": "IH", 80 | "ih_I": "IH", 81 | "ih_S": "IH", 82 | "iy_B": "IY", 83 | "iy_E": "IY", 84 | "iy_I": "IY", 85 | "iy_S": "IY", 86 | "jh_B": "JH", 87 | "jh_E": "JH", 88 | "jh_I": "JH", 89 | "jh_S": "JH", 90 | "k_B": "K", 91 | "k_E": "K", 92 | "k_I": "K", 93 | "k_S": "K", 94 | "l_B": "L", 95 | "l_E": "L", 96 | "l_I": "L", 97 | "l_S": "L", 98 | "laughter": "OOV", 99 | "laughter_B": "OOV", 100 | "laughter_E": "OOV", 101 | "laughter_I": "OOV", 102 | "laughter_S": "OOV", 103 | "m_B": "M", 104 | "m_E": "M", 105 | "m_I": "M", 106 | "m_S": "M", 107 | "n_B": "N", 108 | "n_E": "N", 109 | "n_I": "N", 110 | "n_S": "N", 111 | "ng_B": "NG", 112 | "ng_E": "NG", 113 | "ng_I": "NG", 114 | "ng_S": "NG", 115 | "noise": "SIL", 116 | "noise_B": "SIL", 117 | "noise_E": "SIL", 118 | "noise_I": "SIL", 119 | "noise_S": "SIL", 120 | "oov": "OOV", 121 | "oov_B": "OOV", 122 | "oov_E": "OOV", 123 | "oov_I": "OOV", 124 | "oov_S": "OOV", 125 | "ow_B": "OW", 126 | "ow_E": "OW", 127 | "ow_I": "OW", 128 | "ow_S": "OW", 129 | "oy_B": "OY", 130 | "oy_E": "OY", 131 | "oy_I": "OY", 132 | "oy_S": "OY", 133 | "p_B": "P", 134 | "p_E": "P", 135 | "p_I": "P", 136 | "p_S": "P", 137 | "r_B": "R", 138 | "r_E": "R", 139 | "r_I": "R", 140 | "r_S": "R", 141 | "s_B": "S", 142 | "s_E": "S", 143 | "s_I": "S", 144 | "s_S": "S", 145 | "sh_B": "SH", 146 | "sh_E": "SH", 147 | "sh_I": "SH", 148 | "sh_S": "SH", 149 | "sil": "SIL", 150 | "sil_B": "SIL", 151 | "sil_E": "SIL", 152 | "sil_I": "SIL", 153 | "sil_S": "SIL", 154 | "t_B": "T", 155 | "t_E": "T", 156 | "t_I": "T", 157 | "t_S": "T", 158 | "th_B": "TH", 159 | "th_E": "TH", 160 | "th_I": "TH", 161 | "th_S": "TH", 162 | "uh_B": "UH", 163 | "uh_E": "UH", 164 | "uh_I": "UH", 165 | "uh_S": "UH", 166 | "uw_B": "UW", 167 | "uw_E": "UW", 168 | "uw_I": "UW", 169 | "uw_S": "UW", 170 | "v_B": "V", 171 | "v_E": "V", 172 | "v_I": "V", 173 | "v_S": "V", 174 | "w_B": "W", 175 | "w_E": "W", 176 | "w_I": "W", 177 | "w_S": "W", 178 | "y_B": "Y", 179 | "y_E": "Y", 180 | "y_I": "Y", 181 | "y_S": "Y", 182 | "z_B": "Z", 183 | "z_E": "Z", 184 | "z_I": "Z", 185 | "z_S": "Z", 186 | "zh_B": "ZH", 187 | "zh_E": "ZH", 188 | "zh_I": "ZH", 189 | "zh_S": "ZH" 190 | }, 191 | "vdat": { 192 | "b": "B", 193 | "m": "M", 194 | "p": "P", 195 | "w": "W", 196 | "f": "F", 197 | "v": "V", 198 | "r": "R", 199 | "r2": "R", 200 | "r3": "R", 201 | "er": "ER", 202 | "er2": "ER", 203 | "dh": "DH", 204 | "th": "TH", 205 | "sh": "SH", 206 | "jh": "JH", 207 | "ch": "CH", 208 | "s": "S", 209 | "z": "Z", 210 | "d": "D", 211 | "d2": "D", 212 | "l": "L", 213 | "l2": "L", 214 | "n": "N", 215 | "t": "T", 216 | "ow": "OW", 217 | "uw": "UW", 218 | "ey": "EY", 219 | "ae": "AE", 220 | "aa": "AA", 221 | "aa2": "AA", 222 | "iy": "IY", 223 | "y": "Y", 224 | "ah": "AH", 225 | "ao": "AO", 226 | "ax": "AH", 227 | "ax2": "AH", 228 | "eh": "EH", 229 | "ih": "IH", 230 | "ih2": "IH", 231 | "uh": "UH", 232 | "g": "G", 233 | "g2": "G", 234 | "hh": "HH", 235 | "hh2": "HH", 236 | "c": "K", 237 | "nx": "NG", 238 | "zh": "ZH", 239 | "h": "HH", 240 | "k": "K", 241 | "ay": "AY", 242 | "ng": "NG", 243 | "aw": "AW", 244 | "oy": "OY", 245 | "": "SIL" 246 | }, 247 | "webmaus": { 248 | "6": "AH", 249 | "3:": "ER", 250 | "6:": "AE", 251 | "": "SIL", 252 | "@": "AH", 253 | "@}": "OW", 254 | "Ae": "AY", 255 | "b": "B", 256 | "d": "D", 257 | "D": "DH", 258 | "dZ": "JH", 259 | "E": "EH", 260 | "e": "EH", 261 | "f": "F", 262 | "g": "G", 263 | "h": "HH", 264 | "I": "IH", 265 | "i": "IY", 266 | "i:": "IY", 267 | "I@": "IH", 268 | "j": "Y", 269 | "k": "K", 270 | "l": "L", 271 | "m": "M", 272 | "n": "N", 273 | "N": "NG", 274 | "O": "AA", 275 | "o:": "AO", 276 | "OI": "OY", 277 | "p": "P", 278 | "r": "ER", 279 | "r\\": "R", 280 | "s": "S", 281 | "S": "SH", 282 | "t": "T", 283 | "T": "TH", 284 | "tS": "CH", 285 | "U": "UH", 286 | "u": "UW", 287 | "V": "AH", 288 | "v": "V", 289 | "w": "W", 290 | "z": "Z", 291 | "{": "AE", 292 | "{I": "EY", 293 | "{O": "AW", 294 | "}:": "UW" 295 | } 296 | } -------------------------------------------------------------------------------- /editor.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Phomeme 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 |

Phomeme

23 | 39 | 46 | 53 |
54 |
55 | 93 |
94 | 148 |
149 | 150 | -------------------------------------------------------------------------------- /convertaudition.jsx: -------------------------------------------------------------------------------- 1 | (function() { 2 | 3 | function absolutePath(base, relative) { 4 | var stack = base.split("/"); 5 | var parts = relative.split("/"); 6 | for (var i = 0; i < parts.length; i++) { 7 | if (parts[i] === ".") { 8 | continue; 9 | } 10 | if (parts[i] === "..") { 11 | stack.pop(); 12 | } else { 13 | stack.push(parts[i]); 14 | } 15 | } 16 | return stack.join("/"); 17 | } 18 | 19 | function importBackup(absolute, relative, session, duration) { 20 | // Attempt to load the file, checking both the absolute and relative paths 21 | var path = new File(absolute); 22 | var backup = new File(absolutePath(session, relative)); 23 | if (path.exists || backup.exists) { 24 | var options = new ImportOptions(path.exists ? path : backup); 25 | return app.project.importFile(options); 26 | } else { 27 | // Fallback to a placeholder solid 28 | return app.project.importPlaceholder(absolute || relative, 1920, 1080, 60, duration); 29 | } 30 | } 31 | 32 | function gainToDecibels(num) { 33 | // Convert gain from a linear scale to a logarithmic scale 34 | return (Math.log(num) / Math.log(10)) * 20; 35 | } 36 | 37 | function panVolume(pan, volume) { 38 | var normal = pan * 0.01; 39 | // Decrease the left and right volume based on the pan 40 | var left = gainToDecibels(volume * Math.min(1, normal + 1)); 41 | var right = gainToDecibels(volume * Math.min(1, -normal + 1)); 42 | return [left, right]; 43 | } 44 | 45 | function addClips(list, comp, files, sampleRate) { 46 | for each (var clip in list) { 47 | var layer = comp.layers.add(files[clip.@fileID.toString()]); 48 | layer.name = clip.@name.toString(); 49 | layer.stretch = (parseFloat(clip.clipStretch.@stretchRatio) || 1) * 100; 50 | var startTime = parseFloat(clip.@sourceInPoint) / sampleRate; 51 | var inPoint = parseFloat(clip.@startPoint) / sampleRate; 52 | var outPoint = parseFloat(clip.@endPoint) / sampleRate; 53 | layer.startTime = inPoint - startTime; 54 | layer.inPoint = inPoint; 55 | layer.outPoint = outPoint; 56 | layer.locked = clip.@lockedInTime.toString() === "true"; 57 | layer.audioEnabled = clip.component.(@name == "Mute").parameter.(@index == "1").@parameterValue.toString() === "0"; 58 | var pan = parseFloat(clip.component.(@name == "StereoPanner").parameter.(@name == "Pan").@parameterValue); 59 | var volume = parseFloat(clip.component.(@name == "volume").parameter.(@name == "volume").@parameterValue); 60 | var gain = parseFloat(clip.component.(@name == "volume").parameter.(@name == "static gain").@parameterValue); 61 | if (layer.hasAudio) { 62 | // Take the pan, volume and gain into account when calculating audio levels (this ignores audio and pan keyframes) 63 | layer.audio.audioLevels.setValue(panVolume(pan, volume * gain)); 64 | } 65 | } 66 | } 67 | 68 | function addMarkers(data) { 69 | // TODO: GET THIS TO ACTUALLY ADD MARKERS 70 | if (!ExternalObject.AdobeXMPScript) { 71 | ExternalObject.AdobeXMPScript = new ExternalObject("lib:AdobeXMPScript"); 72 | } 73 | var rdf = new Namespace("http://www.w3.org/1999/02/22-rdf-syntax-ns#"); 74 | var xmp = new Namespace("http://ns.adobe.com/xap/1.0/"); 75 | var xmpMM = new Namespace("http://ns.adobe.com/xap/1.0/mm/"); 76 | var stEvt = new Namespace("http://ns.adobe.com/xap/1.0/sType/ResourceEvent#"); 77 | var stRef = new Namespace("http://ns.adobe.com/xap/1.0/sType/ResourceRef#"); 78 | var dc = new Namespace("http://purl.org/dc/elements/1.1/"); 79 | var AEScart = new Namespace("http://ns.adobe.com/aes/cart/"); 80 | var xmpDM = new Namespace("http://ns.adobe.com/xmp/1.0/DynamicMedia/"); 81 | var meta = new XMPMeta(data.toXMLString()); 82 | for each (var data in meta) { 83 | alert(data); 84 | } 85 | } 86 | 87 | function parse(xml, session) { 88 | var master; 89 | var comps = []; 90 | var files = {}; 91 | // These could probably be parseInt, but allowing float support for fun 92 | var sampleRate = parseFloat(xml.session.@sampleRate) || 44100; 93 | var duration = parseFloat(xml.session.@duration) || 1323000; 94 | var folder = app.project.items.addFolder(decodeURIComponent(session.name)); 95 | folder.selected = true; 96 | for each (var audio in xml.files) { 97 | var file = importBackup(audio.@absolutePath.toString(), audio.@relativePath.toString(), session.path, duration / sampleRate); 98 | files[audio.@id.toString()] = file; 99 | file.parentFolder = folder; 100 | file.selected = false; 101 | } 102 | for each (var prop in xml.session.tracks) { 103 | var comp = app.project.items.addComp(prop.trackParameters.name.toString(), 1920, 1080, 1, duration / sampleRate, 60); 104 | comp.time = parseFloat(xml.session.sessionState.@ctiPosition) / sampleRate; 105 | comp.parentFolder = folder; 106 | addClips(prop..audioClip, comp, files, sampleRate); 107 | addClips(prop..videoClip, comp, files, sampleRate); 108 | if (prop.name() == "masterTrack") { 109 | // All tracks are routed to the master, so it makes sense to put them in the same comp 110 | master = comp; 111 | } else { 112 | // Add to the end of the array since the loop goes in reverse 113 | comps.unshift({ 114 | comp: comp, 115 | solo: prop.trackAudioParameters.@solo.toString() === "true", 116 | mute: prop.trackAudioParameters.component.(@name == "Mute").parameter.(@index == "1").@parameterValue.toString() === "0", 117 | pan: parseFloat(prop.trackAudioParameters.component.(@name == "StereoPanner").parameter.(@name == "Pan").@parameterValue), 118 | volume: parseFloat(prop.trackAudioParameters.component.(@name == "volume").parameter.(@name == "volume").@parameterValue), 119 | gain: parseFloat(prop.trackAudioParameters.component.(@name == "volume").parameter.(@name == "static gain").@parameterValue) 120 | }); 121 | } 122 | } 123 | if (master) { 124 | // Put all tracks into the master comp 125 | for (var i = 0; i < comps.length; i++) { 126 | var comp = master.layers.add(comps[i].comp); 127 | comp.solo = comps[i].solo; 128 | comp.audioEnabled = comps[i].mute; 129 | if (comp.hasAudio) { 130 | comp.audio.audioLevels.setValue(panVolume(comps[i].pan, comps[i].volume * comps[i].gain)); 131 | } 132 | } 133 | //addMarkers(xml.session.xmpMetadata); 134 | } 135 | } 136 | 137 | function fileFilter(file) { 138 | return (file instanceof Folder) || (file.name.split(".").pop() == "sesx"); 139 | } 140 | 141 | function importSession() { 142 | // File filter doesn't seem to work on Mac, but just in case 143 | var session = File.openDialog("Import Audition Session", File.fs == "Macintosh" ? fileFilter : "Audition Session:*.sesx;All files:*.*"); 144 | if (!session) return; 145 | if (session.open("r")) { 146 | var extension = session.name.split(".").pop(); 147 | if (extension === "sesx") { 148 | var content = session.read(); 149 | var xml = new XML(content); 150 | parse(xml, session); 151 | } else { 152 | alert("Not an Audition Session!\nPlease open .sesx files, not ." + extension + " files!"); 153 | importSession(); 154 | } 155 | session.close(); 156 | } else { 157 | alert("Couldn't read the file!"); 158 | } 159 | } 160 | 161 | importSession(); 162 | 163 | })(); -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Phomeme 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 |

Phomeme

24 |
25 |

Source

26 | 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 |

Destination

72 | 73 | 74 | 75 | 76 | 77 |
78 |
79 |

Result

80 |
81 |

Waiting for the first response. Please be patient, this can take ages...

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 |
107 |
108 | 109 | 110 |
111 |

Influence

112 |
113 | 114 | 121 |
122 |
123 | 124 | 125 | 100% 126 |
127 |
128 | 129 | 130 | 100% 131 |
132 |
133 | 134 | 135 | 0% 136 |
137 |
138 | 139 | 140 | 0% 141 |
142 |

Correction

143 |
144 | 145 | 146 |
147 |
148 | 149 | 150 |
151 |
152 | 153 | 154 |
155 |

Session

156 |
157 | 158 | 159 |
160 |
161 | 162 | 163 |
164 |
165 | 166 | 167 |
168 |

Downloads

169 | 175 | 181 |

Extras

182 | 188 | 194 | 200 | 206 |
207 |
208 | 209 | -------------------------------------------------------------------------------- /scripts/Convert.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const convert = (function() { 4 | 5 | /*function isPunctuation(char) { 6 | return "!?.".indexOf(char) !== -1; 7 | }*/ 8 | 9 | function convertJson(transcript, dataParts, params) { 10 | const prev = {}; 11 | transcript.transcript = dataParts.data.transcript; 12 | for (let i = 0; i < dataParts.data.words.length; i++) { 13 | const wordObj = dataParts.data.words[i]; 14 | if (wordObj.case === "not-found-in-audio") continue; 15 | const word = wordObj.word.toLowerCase(); 16 | //const char = dataParts.data.transcript.charAt(wordObj.endOffset); 17 | if (!transcript.words.has(word)) { 18 | transcript.words.set(word, []); 19 | } 20 | const wordData = { 21 | start: wordObj.start, 22 | end: wordObj.end, 23 | dur: wordObj.end - wordObj.start, 24 | label: word, 25 | //prev: params.matchPunctuation && isPunctuation(prev.char) ? prev.char : prev.word, 26 | prev: prev.word, 27 | phones: [], 28 | file: dataParts.file 29 | }; 30 | if (prev.word) { 31 | //prev.word.next = params.matchPunctuation && isPunctuation(char) ? char : word; 32 | prev.word.next = wordData; 33 | } 34 | transcript.words.get(word).push(wordData); 35 | prev.word = wordData; 36 | //prev.char = char; 37 | if (!params.ignoreWordGaps) { 38 | prev.phone = undefined; 39 | } 40 | let start = wordObj.start; 41 | for (let j = 0; j < wordObj.phones.length; j++) { 42 | const phoneObj = wordObj.phones[j]; 43 | let phone = phoneObj.phone; 44 | if (params.matchGeneral) { 45 | const generalization = lookup.gentle && lookup.gentle[phone]; 46 | if (generalization !== undefined) { 47 | phone = generalization; 48 | } 49 | } 50 | if (!transcript.phones.has(phone)) { 51 | transcript.phones.set(phone, []); 52 | } 53 | const phoneData = { 54 | start: start, 55 | end: start + phoneObj.duration, 56 | dur: phoneObj.duration, 57 | label: phone, 58 | prev: prev.phone, 59 | file: dataParts.file 60 | }; 61 | if (prev.phone) { 62 | prev.phone.next = phoneData; 63 | } 64 | wordData.phones.push(phoneData); 65 | transcript.phones.get(phone).push(phoneData); 66 | start += phoneObj.duration; 67 | prev.phone = phoneData; 68 | } 69 | } 70 | return transcript; 71 | } 72 | 73 | function convertTextGrid(transcript, dataParts, params) { 74 | const lines = dataParts.data.split("\n"); 75 | let mode = "words"; 76 | let intervals = 0; 77 | let size = 2; 78 | let prev; 79 | while (lines.length) { 80 | const line = lines.shift().trim(); 81 | if (line.endsWith("")) { 82 | size = parseInt(lines.shift().split("=").pop().trim()); 83 | } else if (line.endsWith("\"IntervalTier\"")) { 84 | intervals++; 85 | prev = undefined; 86 | for (let i = 0; i < 3; i++) { 87 | lines.shift(); 88 | } 89 | const count = parseInt(lines.shift().split("=").pop()); 90 | for (let j = 0; j < count; j++) { 91 | let xmin = lines.shift(); 92 | if (xmin.match(/\[[0-9]+\]:/)) { 93 | xmin = lines.shift(); 94 | } 95 | xmin = parseFloat(xmin.split("=").pop()); 96 | const xmax = parseFloat(lines.shift().split("=").pop()); 97 | let text = lines.shift().split("=").pop().trim().slice(1, -1); 98 | if (mode === "words") { 99 | if (text === "") continue; 100 | text = text.toLowerCase(); 101 | } else if (params.matchGeneral) { 102 | const generalization = lookup.webmaus && lookup.webmaus[text]; 103 | if (generalization !== undefined) { 104 | text = generalization; 105 | } 106 | } 107 | if (!transcript[mode].has(text)) { 108 | transcript[mode].set(text, []); 109 | } 110 | const data = { 111 | start: xmin, 112 | end: xmax, 113 | dur: xmax - xmin, 114 | label: text, 115 | prev: prev, 116 | file: dataParts.file 117 | }; 118 | if (prev) { 119 | prev.next = data; 120 | } 121 | transcript[mode].get(text).push(data); 122 | if (mode === "phones") { 123 | transcript.words.forEach((value) => { 124 | for (let k = 0; k < value.length; k++) { 125 | const word = value[k]; 126 | if (xmin >= word.start && xmax <= word.end) { 127 | word.phones = word.phones || []; 128 | word.phones.push(data); 129 | } 130 | } 131 | }); 132 | } 133 | prev = data; 134 | } 135 | if (intervals + 1 === size) { 136 | mode = "phones"; 137 | } 138 | } 139 | } 140 | return transcript; 141 | } 142 | 143 | function convertVdat(transcript, dataParts, params) { 144 | const lines = dataParts.data.split("\n"); 145 | const prev = {}; 146 | while (lines.length) { 147 | const line = lines.shift().trim(); 148 | if (line.startsWith("PLAINTEXT")) { 149 | lines.shift(); 150 | transcript.transcript = lines.shift().trim(); 151 | } else if (line.startsWith("WORD") && line !== "WORDS") { 152 | const wordParts = line.split(" "); 153 | if (wordParts.length >= 4) { 154 | const word = wordParts[1].trim().toLowerCase(); 155 | if (!transcript.words.has(word)) { 156 | transcript.words.set(word, []); 157 | } 158 | const wordStart = parseFloat(wordParts[2]); 159 | const wordEnd = parseFloat(wordParts[3]); 160 | const wordData = { 161 | start: wordStart, 162 | end: wordEnd, 163 | dur: wordEnd - wordStart, 164 | label: word, 165 | prev: prev.word, 166 | phones: [], 167 | file: dataParts.file 168 | }; 169 | if (prev.word) { 170 | prev.word.next = wordData; 171 | } 172 | transcript.words.get(word).push(wordData); 173 | prev.word = wordData; 174 | if (!params.ignoreWordGaps) { 175 | prev.phone = undefined; 176 | } 177 | let nextLine = lines.shift(); 178 | while (!nextLine.endsWith("}")) { 179 | const phoneParts = nextLine.split(" "); 180 | if (phoneParts.length >= 4) { 181 | let phone = phoneParts[1].trim(); 182 | if (params.matchGeneral) { 183 | const generalization = lookup.vdat && lookup.vdat[phone]; 184 | if (generalization !== undefined) { 185 | phone = generalization; 186 | } 187 | } 188 | if (!transcript.phones.has(phone)) { 189 | transcript.phones.set(phone, []); 190 | } 191 | const phoneStart = parseFloat(phoneParts[2]); 192 | const phoneEnd = parseFloat(phoneParts[3]); 193 | const phoneData = { 194 | start: phoneStart, 195 | end: phoneEnd, 196 | dur: phoneEnd - phoneStart, 197 | label: phone, 198 | prev: prev.phone, 199 | volume: parseFloat(phoneParts[4]), 200 | file: dataParts.file 201 | }; 202 | if (prev.phone) { 203 | prev.phone.next = phoneData; 204 | } 205 | wordData.phones.push(phoneData); 206 | transcript.phones.get(phone).push(phoneData); 207 | prev.phone = phoneData; 208 | } 209 | nextLine = lines.shift(); 210 | } 211 | } 212 | } 213 | } 214 | return transcript; 215 | } 216 | 217 | function convertCues(transcript, dataParts) { 218 | const points = dataParts.data.listCuePoints(); 219 | let prev; 220 | for (let i = 0; i < points.length; i++) { 221 | const pos = points[i].position; 222 | if (pos === undefined) continue; 223 | const start = pos / 1000; 224 | let phone = points[i].label; 225 | if (phone === undefined) { 226 | phone = ""; 227 | } 228 | if (!transcript.phones.has(phone)) { 229 | transcript.phones.set(phone, []); 230 | } 231 | const phoneData = { 232 | start: start, 233 | label: phone, 234 | prev: prev, 235 | file: dataParts.file 236 | }; 237 | if (prev) { 238 | prev.next = phoneData; 239 | if (!prev.end) { 240 | prev.end = start; 241 | prev.dur = prev.end - prev.start; 242 | } 243 | } 244 | const end = points[i].end; 245 | if (end) { 246 | phoneData.end = end / 1000; 247 | phoneData.dur = phoneData.end - phoneData.start; 248 | } 249 | transcript.phones.get(phone).push(phoneData); 250 | prev = phoneData; 251 | } 252 | if (!prev.end) { 253 | const fileDuration = dataParts.data.data.chunkSize / dataParts.data.fmt.byteRate; 254 | prev.end = fileDuration; 255 | prev.dur = prev.end - prev.start; 256 | } 257 | return transcript; 258 | } 259 | 260 | function convertMidi(transcript, dataParts) { 261 | for (let i = 0; i < dataParts.data.tracks.length; i++) { 262 | const track = dataParts.data.tracks[i]; 263 | let prev; 264 | for (let j = 0; j < track.notes.length; j++) { 265 | const note = track.notes[j]; 266 | if (!transcript.phones.has(note.name)) { 267 | transcript.phones.set(note.name, []); 268 | } 269 | const phoneData = { 270 | start: note.time, 271 | end: note.time + note.duration, 272 | dur: note.duration, 273 | label: note.name, 274 | pitch: note.midi, 275 | volume: note.velocity, 276 | file: dataParts.file, 277 | prev: prev 278 | }; 279 | if (prev) { 280 | prev.next = phoneData; 281 | } 282 | transcript.phones.get(note.name).push(phoneData); 283 | prev = phoneData; 284 | } 285 | } 286 | return transcript; 287 | } 288 | 289 | function convertSentence(dataParts, params) { 290 | //const words = dataParts.data.toLowerCase().match(params.matchPunctuation ? /\w+(?:'\w+)*|[!?.](?![!?.])/g : /\w+(?:'\w+)*/g); 291 | const words = dataParts.data.toLowerCase().match(/\w+(?:'\w+)*/g); 292 | const prev = {}; 293 | const transcript = { 294 | transcript: dataParts.data, 295 | words: [], 296 | phones: [] 297 | }; 298 | for (let i = 0; i < words.length; i++) { 299 | const word = words[i]; 300 | //if (isPunctuation(word)) continue; 301 | const wordData = { 302 | prev: prev.word, 303 | label: word 304 | }; 305 | if (prev.word) { 306 | prev.word.next = wordData; 307 | } 308 | transcript.words.push(wordData); 309 | if (!params.ignoreWordGaps) { 310 | prev.phone = undefined; 311 | } 312 | const phones = dictionary[word]; 313 | if (phones) { 314 | wordData.phones = []; 315 | for (let j = 0; j < phones.length; j++) { 316 | const phoneData = { 317 | prev: prev.phone, 318 | label: phones[j] 319 | }; 320 | if (prev.phone) { 321 | prev.phone.next = phoneData; 322 | } 323 | wordData.phones.push(phoneData); 324 | transcript.phones.push(phoneData); 325 | } 326 | } else { 327 | console.log("MISSING DEFINITION: " + word); 328 | } 329 | } 330 | return transcript; 331 | } 332 | 333 | return function(dataParts, params) { 334 | const transcript = { 335 | words: new Map(), 336 | phones: new Map() 337 | }; 338 | switch (dataParts.type.toLowerCase()) { 339 | case "json": 340 | return convertJson(transcript, dataParts, params); 341 | case "textgrid": 342 | return convertTextGrid(transcript, dataParts, params); 343 | case "vdat": 344 | return convertVdat(transcript, dataParts, params); 345 | case "cues": 346 | return convertCues(transcript, dataParts); 347 | case "midi": 348 | return convertMidi(transcript, dataParts); 349 | default: 350 | return convertSentence(dataParts, params); 351 | } 352 | }; 353 | 354 | }()); -------------------------------------------------------------------------------- /scripts/Main.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | let lookup; 4 | let inputData; 5 | let outputData; 6 | let dictionary; 7 | 8 | function requestFile(method, file, error, func, data) { 9 | const request = new XMLHttpRequest(); 10 | request.open(method, file, true); 11 | request.onreadystatechange = function() { 12 | if (this.readyState === 4 && this.status === 200) { 13 | func(this.responseText); 14 | } 15 | }; 16 | request.onerror = function() { 17 | document.getElementById("title").textContent = "Error"; 18 | document.getElementById("waiting").textContent = error + " Try installing a cross-origin extension."; 19 | document.getElementById("spinner").style.display = "none"; 20 | }; 21 | request.send(data); 22 | } 23 | 24 | function addLink(name, data, type, extension) { 25 | const blob = new Blob([data], { type: type }); 26 | const url = window.URL.createObjectURL(blob); 27 | const element = document.getElementById(name); 28 | element.href = url; 29 | element.download = name + "." + extension; 30 | } 31 | 32 | function readFile(file, func) { 33 | const reader = new FileReader(); 34 | reader.onload = function() { 35 | func(this.result, file.name, file.type); 36 | }; 37 | reader.readAsText(file, "UTF-8"); 38 | } 39 | 40 | function addBlob(file, extension, isInput) { 41 | const url = window.URL.createObjectURL(file); 42 | const player = document.getElementById(isInput ? "inputPlayer" : "outputPlayer"); 43 | player.style.display = "inline-block"; 44 | player.src = url; 45 | const name = isInput ? "input" : "output"; 46 | const link = document.getElementById(name); 47 | link.style.display = "block"; 48 | link.href = url; 49 | link.download = name + "." + extension; 50 | } 51 | 52 | function checkJson(element) { 53 | const isInput = element.id === "inputAudio"; 54 | const file = element.files[0]; 55 | if (!file) return; 56 | const lower = file.name.toLowerCase(); 57 | if (lower.endsWith("json") || lower.endsWith("txt")) { 58 | readFile(file, function(content) { 59 | const transcript = document.getElementById(isInput ? "inputScript" : "outputScript"); 60 | transcript.textContent = lower.endsWith("txt") ? content : JSON.parse(content).transcript; 61 | }); 62 | } else if (file.type.startsWith("audio")) { 63 | addBlob(file, file.name.split(".").pop(), isInput); 64 | } 65 | } 66 | 67 | function updatePresets(element) { 68 | const presets = document.getElementsByClassName("preset"); 69 | const div = document.getElementById(element.value); 70 | for (let i = 0; i < presets.length; i++) { 71 | const preset = presets[i]; 72 | preset.style.display = div === preset ? "block" : "none"; 73 | for (let j = 0; j < preset.childNodes.length; j++) { 74 | preset.childNodes[j].required = div === preset; 75 | } 76 | } 77 | } 78 | 79 | function getText(node) { 80 | if (node.childNodes.length) { 81 | let result = ""; 82 | for (let i = 0; i < node.childNodes.length; i++) { 83 | result += getText(node.childNodes[i]) + " "; 84 | } 85 | return result; 86 | } else { 87 | return node.textContent; 88 | } 89 | } 90 | 91 | function setWeight(elem) { 92 | document.getElementById(elem.id + "Label").textContent = (elem.value * 100) + "%"; 93 | updateDownloads(); 94 | } 95 | 96 | function updateMethodLabel(elem) { 97 | document.getElementById("methodLabel").textContent = "Sort by " + elem.options[elem.selectedIndex].text.toLowerCase(); 98 | updateDownloads(); 99 | } 100 | 101 | function updateDownloads() { 102 | const final = (dictionary ? textToSpeech : speechToSpeech)(inputData, outputData, { 103 | method: document.getElementById("sortMethod").value, 104 | matchWords: document.getElementById("matchWords").checked, 105 | matchOneForward: document.getElementById("matchOneForward").checked, 106 | matchOneBackward: document.getElementById("matchOneBackward").checked, 107 | matchPunctuation: document.getElementById("matchPunctuation").checked, 108 | matchGeneral: document.getElementById("matchGeneral").checked, 109 | ignoreWordGaps: document.getElementById("ignoreWordGaps").checked, 110 | overlapStart: parseFloat(document.getElementById("overlapStart").value), 111 | overlapEnd: parseFloat(document.getElementById("overlapEnd").value), 112 | sampleRate: parseInt(document.getElementById("sampleRate").value), 113 | methodWeight: parseFloat(document.getElementById("methodWeight").value), 114 | contextWeight: parseFloat(document.getElementById("contextWeight").value), 115 | pitchWeight: parseFloat(document.getElementById("pitchWeight").value), 116 | volumeWeight: parseFloat(document.getElementById("volumeWeight").value), 117 | contextDepth: parseInt(document.getElementById("contextDepth").value), 118 | transferPitch: document.getElementById("transferPitch").checked, 119 | transferVolume: document.getElementById("transferVolume").checked, 120 | transferDuration: document.getElementById("transferDuration").checked 121 | }); 122 | addLink("session", final, "application/xml", "sesx"); 123 | } 124 | 125 | function microphone(element) { 126 | const isInput = element.id === "inputMic"; 127 | const transcript = document.getElementById(isInput ? "inputScript" : "outputScript"); 128 | if (element.active) { 129 | if (element.recognition) { 130 | element.recognition.stop(); 131 | } 132 | if (element.recorder) { 133 | element.recorder.stop(); 134 | } 135 | transcript.setAttribute("contenteditable", true); 136 | element.src = "icons/microphone.png"; 137 | element.active = false; 138 | } else { 139 | const Speech = window.SpeechRecognition || window.webkitSpeechRecognition || window.mozSpeechRecognition || window.oSpeechRecognition || window.msSpeechRecognition; 140 | if (Speech) { 141 | transcript.setAttribute("contenteditable", false); 142 | if (!element.recognition) { 143 | element.recognition = new Speech(); 144 | element.recognition.continuous = true; 145 | element.recognition.interimResults = true; 146 | let final = transcript.innerHTML; 147 | element.recognition.onresult = function(e) { 148 | let temp = ""; 149 | for (let i = e.resultIndex; i < e.results.length; i++) { 150 | if (event.results[i].isFinal) { 151 | final += event.results[i][0].transcript; 152 | } else { 153 | temp += event.results[i][0].transcript; 154 | } 155 | } 156 | transcript.innerHTML = final + "" + temp + ""; 157 | }; 158 | element.recognition.onend = function() { 159 | transcript.innerHTML = final; 160 | }; 161 | } 162 | element.recognition.start(); 163 | } 164 | if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) { 165 | navigator.mediaDevices.getUserMedia({ audio: true, video: false }).then(function(stream) { 166 | element.recorder = new MediaRecorder(stream); 167 | const chunks = []; 168 | element.recorder.ondataavailable = function(e) { 169 | chunks.push(e.data); 170 | }; 171 | element.recorder.onstop = function() { 172 | stream.getTracks()[0].stop(); 173 | const blob = new Blob(chunks, { type: "audio/ogg; codecs=opus" }); 174 | //document.getElementById(isInput ? "inputAudio" : "outputAudio").files = new FileList(new File([blob], "filename")); 175 | addBlob(blob, "ogg", isInput); 176 | }; 177 | element.recorder.start(); 178 | }).catch(console.error); 179 | } 180 | element.src = "icons/micactive.png"; 181 | element.active = true; 182 | } 183 | return false; 184 | } 185 | 186 | function complete() { 187 | requestFile("GET", "lookup.json", "Couldn't load lookup table!", function(response) { 188 | lookup = JSON.parse(response); 189 | updateDownloads(); 190 | document.getElementById("spinner").style.display = "none"; 191 | document.getElementById("waiting").style.display = "none"; 192 | document.getElementById("options").style.display = "block"; 193 | }); 194 | } 195 | 196 | function addOutput(data, name, type) { 197 | const extension = name ? name.split(".").pop() : "json"; 198 | addLink("output", data, type || "application/json", extension); 199 | if (extension === "json") { 200 | data = JSON.parse(data); 201 | } 202 | outputData = { data: data, type: extension, file: "output.wav" }; 203 | complete(); 204 | } 205 | 206 | function finalResponse() { 207 | document.getElementById("waiting").textContent = "Response received! Waiting for the final response..."; 208 | const file = document.getElementById("outputAudio").files[0]; 209 | if (file) { 210 | if (file.type.startsWith("audio")) { 211 | const output = new FormData(); 212 | output.append("audio", file); 213 | output.append("transcript", getText(document.getElementById("outputScript"))); 214 | requestFile("POST", "http://gentle-demo.lowerquality.com/transcriptions?async=false", "Couldn't receive a response!", addOutput, output); 215 | } else { 216 | readFile(file, addOutput); 217 | } 218 | } else { 219 | document.getElementById("waiting").textContent = "Loading phone dictionary..."; 220 | document.getElementById("sortMethod").value = "longest"; 221 | requestFile("GET", "phonedictionary.txt", "Couldn't load phone dictionary!", function(response) { 222 | dictionary = {}; 223 | const lines = response.split("\n"); 224 | for (let i = 0; i < lines.length; i++) { 225 | const phones = lines[i].split(" "); 226 | dictionary[phones[0]] = phones.slice(1); 227 | } 228 | const data = getText(document.getElementById("outputScript")); 229 | addLink("output", data, "text/plain", "txt"); 230 | outputData = { data: data, type: "txt", file: "output.wav" }; 231 | complete(); 232 | }); 233 | } 234 | } 235 | 236 | function addInput(data, name, type) { 237 | const extension = name ? name.split(".").pop() : "json"; 238 | addLink("input", data, type || "application/json", extension); 239 | if (extension === "json") { 240 | data = JSON.parse(data); 241 | } 242 | inputData = { data: data, type: extension, file: "input.wav" }; 243 | finalResponse(); 244 | } 245 | 246 | function crossOrigin(func) { 247 | const input = document.getElementById("inputAudio").files[0]; 248 | const output = document.getElementById("outputAudio").files[0]; 249 | if (input && input.type.startsWith("audio") || output && output.type.startsWith("audio")) { 250 | const request = new XMLHttpRequest(); 251 | request.open("GET", "http://gentle-demo.lowerquality.com", true); 252 | request.onreadystatechange = function() { 253 | if (this.readyState === 4 && this.status === 200) { 254 | func(); 255 | } 256 | }; 257 | request.onerror = function() { 258 | window.alert("Please install a cross-origin extension for this to work!"); 259 | }; 260 | request.send(); 261 | } else { 262 | func(); 263 | } 264 | } 265 | 266 | function phomeme() { 267 | crossOrigin(function() { 268 | const preset = document.getElementById("preset").value; 269 | if (preset === "custom") { 270 | const file = document.getElementById("inputAudio").files[0]; 271 | if (file.type.startsWith("audio")) { 272 | const input = new FormData(); 273 | input.append("audio", file); 274 | input.append("transcript", getText(document.getElementById("inputScript"))); 275 | requestFile("POST", "http://gentle-demo.lowerquality.com/transcriptions?async=false", "Couldn't receive a response!", addInput, input); 276 | } else { 277 | readFile(file, addInput); 278 | } 279 | } else { 280 | document.getElementById("waiting").textContent = "Loading " + preset + "..."; 281 | requestFile("GET", preset + "/complete.json", "Couldn't load " + preset + "!", addInput); 282 | } 283 | document.getElementById("form").style.display = "none"; 284 | document.getElementById("result").style.display = "block"; 285 | }); 286 | return false; 287 | } -------------------------------------------------------------------------------- /editor.css: -------------------------------------------------------------------------------- 1 | * { 2 | color: white; 3 | font-family: "Trebuchet MS", Helvetica, sans-serif; 4 | } 5 | 6 | html, body { 7 | background-color: #061306; 8 | margin: 0; 9 | padding: 0; 10 | scrollbar-color: #006000 #003000; 11 | scrollbar-width: thin; 12 | } 13 | 14 | ::-webkit-scrollbar { 15 | height: 10px; 16 | width: 10px; 17 | } 18 | 19 | ::-webkit-scrollbar-track, ::-webkit-scrollbar-corner { 20 | background: #003000; 21 | } 22 | 23 | ::-webkit-scrollbar-thumb { 24 | background: #006000; 25 | } 26 | 27 | ::-webkit-scrollbar-thumb:hover { 28 | background: #008000; 29 | } 30 | 31 | body { 32 | display: grid; 33 | grid-template-columns: 320px auto; 34 | grid-template-rows: 48px calc(100vh - 48px); 35 | } 36 | 37 | input[type="file"] { 38 | height: 0; 39 | opacity: 0; 40 | position: absolute; 41 | right: 0; 42 | top: 0; 43 | width: 0; 44 | } 45 | 46 | .activecontext { 47 | box-shadow: 0 0 6px 3px #00FF0080 inset; 48 | } 49 | 50 | #boxselect { 51 | -moz-user-select: none; 52 | background-color: #00FF001A; 53 | outline: 1px solid #00FF00; 54 | position: fixed; 55 | user-select: none; 56 | z-index: 9999; 57 | } 58 | 59 | .clip { 60 | cursor: move; 61 | height: 128px; 62 | position: relative; 63 | } 64 | 65 | .clipdiv { 66 | height: 100%; 67 | outline: 1px solid #00FF00; 68 | position: absolute; 69 | } 70 | 71 | .clipdiv:hover { 72 | background-color: #00000040; 73 | z-index: 1; 74 | } 75 | 76 | .clipdiv:active { 77 | background-color: #00000080; 78 | z-index: 2; 79 | } 80 | 81 | .clipdiv.active { 82 | background-color: #00FF0040; 83 | box-shadow: 0 0 0 1px #00FF00 inset; 84 | z-index: 3; 85 | } 86 | 87 | .clipdragleft, .clipdragright { 88 | cursor: ew-resize; 89 | height: 100%; 90 | position: absolute; 91 | top: 0; 92 | width: 4px; 93 | z-index: 4; 94 | } 95 | 96 | .clipdragleft { 97 | left: 0; 98 | } 99 | 100 | .clipdragright { 101 | right: 0; 102 | } 103 | 104 | #closebutton { 105 | font-weight: bold; 106 | padding: 8px 16px; 107 | position: absolute; 108 | right: 0; 109 | top: 0; 110 | } 111 | 112 | #createbutton { 113 | border: 1px solid #00FF00; 114 | display: inline-block; 115 | font-size: large; 116 | padding: 8px 64px; 117 | } 118 | 119 | .dropcontent { 120 | background-color: #061306; 121 | border-bottom: 1px solid #00FF00; 122 | border-left: 1px solid #00FF00; 123 | border-right: 1px solid #00FF00; 124 | display: none; 125 | font-size: medium; 126 | min-width: 160px; 127 | position: absolute; 128 | text-align: left; 129 | z-index: 999; 130 | } 131 | 132 | .dropdown { 133 | float: left; 134 | height: 100%; 135 | overflow: hidden; 136 | } 137 | 138 | .dropoption { 139 | display: block; 140 | padding: 16px; 141 | } 142 | 143 | .filegroup > a:after { 144 | color: #808080; 145 | content: "+"; 146 | float: right; 147 | font-size: large; 148 | font-weight: bold; 149 | } 150 | 151 | .filegroup.open > a { 152 | background-color: #004000; 153 | } 154 | 155 | .filegroup.open > a:after { 156 | content: "-"; 157 | } 158 | 159 | .filegroup.error > .presetprogress { 160 | background-color: #FF0000; 161 | } 162 | 163 | .filegroup.error.open > a { 164 | background-color: #500000; 165 | } 166 | 167 | .filegrouplist { 168 | background-color: #001C00; 169 | margin-left: 16px; 170 | } 171 | 172 | .filegrouplist a.error .filedetails { 173 | background-color: #500000; 174 | } 175 | 176 | .filedetails { 177 | -moz-user-select: none; 178 | background-color: #005000; 179 | border-radius: 2px; 180 | color: white; 181 | float: right; 182 | font-size: small; 183 | font-weight: bold; 184 | padding: 2px 6px; 185 | user-select: none; 186 | } 187 | 188 | .filedetails span + span:before { 189 | content: " + "; 190 | } 191 | 192 | #filetabs { 193 | background-color: #002000; 194 | box-shadow: 0 -2px 0 0 #00FF00 inset; 195 | display: flex; 196 | overflow: auto; 197 | z-index: 100; 198 | } 199 | 200 | #filetabs a { 201 | border-bottom: 2px solid #00FF00; 202 | border-right: 2px solid #00FF00; 203 | display: inline-block; 204 | min-width: 128px; 205 | } 206 | 207 | #filetabs a:hover { 208 | background-color: #003000; 209 | } 210 | 211 | #filetabs a:active { 212 | background-color: #004000; 213 | } 214 | 215 | #filetabs a.active { 216 | background-color: #005000; 217 | border-bottom: 2px solid #005000; 218 | } 219 | 220 | .filetabname { 221 | float: left; 222 | font-size: small; 223 | padding: 4px 8px; 224 | } 225 | 226 | .filetabclose { 227 | float: right; 228 | font-weight: bold; 229 | padding: 2px 8px; 230 | } 231 | 232 | #interimtranscript { 233 | color: gray; 234 | } 235 | 236 | .largebutton { 237 | border: 1px solid #00FF00; 238 | display: inline-block; 239 | font-size: x-large; 240 | padding: 16px 64px; 241 | } 242 | 243 | #navigationarea { 244 | position: sticky; 245 | top: 0; 246 | width: calc(100vw - 320px); 247 | z-index: 6; 248 | } 249 | 250 | #navigation { 251 | overflow: hidden; 252 | position: relative; 253 | } 254 | 255 | .padded { 256 | margin: 6px 0; 257 | } 258 | 259 | .playbackbutton { 260 | border: 1px solid #00FF00; 261 | display: inline-block; 262 | font-size: medium; 263 | height: 24px; 264 | line-height: 24px; 265 | padding: 0 24px; 266 | text-align: center; 267 | vertical-align: middle; 268 | } 269 | 270 | #playbutton { 271 | width: 16px; 272 | } 273 | 274 | #playback { 275 | background-color: black; 276 | border-top: 2px solid #00FF00; 277 | bottom: 0; 278 | height: 32px; 279 | padding: 16px; 280 | position: fixed; 281 | width: 100%; 282 | z-index: 6; 283 | } 284 | 285 | #playhead { 286 | -moz-user-select: none; 287 | background-color: white; 288 | height: calc(100vh - 188px); 289 | mix-blend-mode: difference; 290 | pointer-events: none; 291 | position: absolute; 292 | user-select: none; 293 | width: 1px; 294 | z-index: 5; 295 | } 296 | 297 | #playlistarea { 298 | height: calc(100vh - 188px); 299 | overflow: auto; 300 | width: calc(100vw - 320px); 301 | } 302 | 303 | #playlist { 304 | margin-bottom: 64px; 305 | position: relative; 306 | } 307 | 308 | #popupoverlay { 309 | background-color: #00000080; 310 | height: 100%; 311 | position: fixed; 312 | width: 100%; 313 | z-index: 1000; 314 | } 315 | 316 | #popup { 317 | background-color: #061306; 318 | border: 2px solid #00FF00; 319 | box-shadow: 0 0 32px black; 320 | margin: 16px 10%; 321 | position: absolute; 322 | top: 0; 323 | width: 80%; 324 | z-index: 1001; 325 | } 326 | 327 | .preset { 328 | display: block; 329 | padding: 16px; 330 | } 331 | 332 | .preset + .preset { 333 | border-top: 1px solid #00FF00; 334 | } 335 | 336 | .preset span { 337 | display: block; 338 | padding: 2px; 339 | } 340 | 341 | .preset .presettitle { 342 | font-size: x-large; 343 | font-weight: bold; 344 | } 345 | 346 | .preset .presetauthor { 347 | color: darkgray; 348 | font-size: small; 349 | font-style: italic; 350 | } 351 | 352 | .preset .presetdesc { 353 | font-style: italic; 354 | margin-top: 8px; 355 | } 356 | 357 | .presetprogress { 358 | background-color: #00FF00; 359 | float: left; 360 | height: 2px; 361 | } 362 | 363 | #prefsmenu { 364 | padding: 16px; 365 | } 366 | 367 | #prefsmenu h2 { 368 | border-bottom: 1px solid #00FF00; 369 | display: inline-block; 370 | font-weight: normal; 371 | margin: 0; 372 | padding-bottom: 8px; 373 | } 374 | 375 | #prefsmenu h3 { 376 | border-bottom: 1px solid gray; 377 | margin: 16px 0; 378 | padding-bottom: 8px; 379 | } 380 | 381 | #prefsmenu div { 382 | margin: 8px 0; 383 | } 384 | 385 | #recordbutton.active { 386 | background-color: #00FF00; 387 | color: black; 388 | } 389 | 390 | #sidenav { 391 | border-right: 1px solid #00FF00; 392 | overflow-x: hidden; 393 | overflow-y: auto; 394 | padding: 8px; 395 | z-index: 997; 396 | } 397 | 398 | #sidenav a { 399 | border-bottom: 1px solid #004000; 400 | display: block; 401 | overflow-wrap: break-word; 402 | padding: 8px; 403 | } 404 | 405 | #sidenav .filegroup > a:hover { 406 | background-color: #003000; 407 | } 408 | 409 | #sidenav .filegroup.error > a { 410 | border-bottom: 1px solid #400000; 411 | } 412 | 413 | #sidenav .filegroup.error > a:hover { 414 | background-color: #400000; 415 | } 416 | 417 | #sidenav .filegrouplist a.error { 418 | background-color: #290000; 419 | border-bottom: 1px solid #180000; 420 | } 421 | 422 | #sidenav a.error:hover { 423 | background-color: #300000; 424 | } 425 | 426 | #sidenav a.error:active { 427 | background-color: #400000; 428 | } 429 | 430 | .step { 431 | font-size: xx-large; 432 | padding: 16px; 433 | text-align: center; 434 | } 435 | 436 | .steplabel { 437 | display: block; 438 | padding: 8px 0; 439 | } 440 | 441 | #submitbutton { 442 | border: 1px solid #00FF00; 443 | display: inline-block; 444 | font-size: large; 445 | padding: 8px 128px; 446 | } 447 | 448 | #sessionname { 449 | background-color: black; 450 | border: 1px solid #00FF00; 451 | box-sizing: border-box; 452 | display: block; 453 | font-size: medium; 454 | padding: 12px; 455 | width: 100%; 456 | } 457 | 458 | #sessionname:focus { 459 | background-color: #001000; 460 | border: 1px solid white; 461 | } 462 | 463 | #sessionmenu { 464 | padding: 16px; 465 | } 466 | 467 | #sessionmenu > h3 { 468 | border-bottom: 1px solid gray; 469 | margin: 0 0 12px 0; 470 | padding-bottom: 8px; 471 | } 472 | 473 | #time { 474 | font-size: large; 475 | vertical-align: middle; 476 | } 477 | 478 | #timeline { 479 | border-top: 2px solid #00FF00; 480 | cursor: pointer; 481 | display: block; 482 | height: 16px; 483 | width: 100%; 484 | } 485 | 486 | #title { 487 | float: left; 488 | height: 100%; 489 | margin: 0; 490 | mix-blend-mode: screen; 491 | padding: 4px 16px; 492 | } 493 | 494 | #topnav { 495 | background-color: #061306; 496 | border-bottom: 2px solid #00FF00; 497 | box-shadow: 0 -32px 32px -32px #00FF00 inset; 498 | grid-column: 1 / span 2; 499 | overflow: hidden; 500 | z-index: 998; 501 | } 502 | 503 | .topbutton { 504 | box-sizing: border-box; 505 | display: inline-block; 506 | font-size: medium; 507 | height: 100%; 508 | margin-bottom: 2px; 509 | mix-blend-mode: screen; 510 | padding: 16px; 511 | } 512 | 513 | #transcript { 514 | background-color: black; 515 | border: 1px solid #00FF00; 516 | box-sizing: border-box; 517 | display: block; 518 | font-size: medium; 519 | padding: 12px; 520 | resize: vertical; 521 | width: 100%; 522 | } 523 | 524 | #transcript:focus { 525 | background-color: #001000; 526 | border: 1px solid white; 527 | } 528 | 529 | #transcriptbuttons { 530 | align-items: center; 531 | display: flex; 532 | } 533 | 534 | .track { 535 | background-color: black; 536 | border-bottom: 1px solid #00FF00; 537 | display: flex; 538 | height: 128px; 539 | position: relative; 540 | width: 100%; 541 | } 542 | 543 | .track.active { 544 | background-color: #002000; 545 | } 546 | 547 | #transcriptmenu { 548 | padding: 12px; 549 | } 550 | 551 | #transcriptmicrophone { 552 | padding: 4px; 553 | } 554 | 555 | #transcriptplayer { 556 | display: inline-block; 557 | outline: none; 558 | width: 100%; 559 | } 560 | 561 | #uploadbutton { 562 | border: 1px solid #00FF00; 563 | display: inline-block; 564 | font-size: large; 565 | padding: 8px 64px; 566 | } 567 | 568 | #zoom { 569 | display: block; 570 | height: 32px; 571 | width: 100%; 572 | } 573 | 574 | #zoomer { 575 | background-color: #00FF0033; 576 | cursor: grab; 577 | height: 32px; 578 | outline: 2px solid #00FF00; 579 | position: absolute; 580 | top: 0; 581 | width: 100%; 582 | z-index: 420; 583 | } 584 | 585 | #zoomer:active { 586 | cursor: grabbing; 587 | } 588 | 589 | #zoomdragleft, #zoomdragright { 590 | cursor: ew-resize; 591 | height: 100%; 592 | position: absolute; 593 | top: 0; 594 | width: 8px; 595 | z-index: 421; 596 | } 597 | 598 | #zoomdragleft { 599 | left: 0; 600 | } 601 | 602 | #zoomdragright { 603 | right: 0; 604 | } 605 | 606 | #boxselect, #editor, #presetmenu, #step2, #popupoverlay, #popup, #prefsmenu, #transcriptmenu, #transcriptplayer { 607 | display: none; 608 | } 609 | 610 | .blackbutton { 611 | background-color: black; 612 | } 613 | 614 | .blackbutton:hover, .button:hover, .dropoption:hover, .dropdown:hover .topbutton, .preset:hover, #sidenav a:hover, .topbutton:hover { 615 | background-color: #002000; 616 | } 617 | 618 | .blackbutton:active, .button:active, .dropoption:active, .dropdown .topbutton:active, .preset:active, #sidenav a:active, .topbutton:active { 619 | background-color: #004000; 620 | } 621 | 622 | .blackbutton, .button, .dropoption, #filetabs a, #transcriptmicrophone, .preset, #sidenav a, .topbutton { 623 | -moz-user-select: none; 624 | cursor: pointer; 625 | outline: none; 626 | user-select: none; 627 | } 628 | 629 | #sidenav a.active { 630 | background-color: #004000; 631 | } 632 | 633 | #popup h2, #popup h3, #popup div, .step span { 634 | -moz-user-select: none; 635 | user-select: none; 636 | } -------------------------------------------------------------------------------- /scripts/modules/tonal.js: -------------------------------------------------------------------------------- 1 | var Tonal=function(){"use strict";"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self&&self;const t=(t,n)=>Array(Math.abs(n)+1).join(t);function n(t,n,e){return function(...r){return console.warn(`${t} is deprecated. Use ${n}.`),e.apply(this,r)}}function e(t){return null!==t&&"object"==typeof t&&"string"==typeof t.name}function r(t){return null!==t&&"object"==typeof t&&"number"==typeof t.step&&"number"==typeof t.alt}const o=[0,2,4,-1,1,3,5],a=o.map(t=>Math.floor(7*t/12));function m(t){const{step:n,alt:e,oct:r,dir:m=1}=t,i=o[n]+7*e;return void 0===r?[m*i]:[m*i,m*(r-a[n]-4*e)]}const i=[3,0,4,1,5,2,6];function s(t){const[n,e,r]=t,o=i[function(t){const n=(t+1)%7;return n<0?7+n:n}(n)],m=Math.floor((n+1)/7);return void 0===e?{step:o,alt:m,dir:r}:{step:o,alt:m,oct:e+4*m+a[o],dir:r}}const c={empty:!0,name:"",pc:"",acc:""},u=new Map,l=t=>"CDEFGAB".charAt(t),P=n=>n<0?t("b",-n):t("#",n),d=t=>"b"===t[0]?-t.length:t.length;function M(t){const n=u.get(t);if(n)return n;const o="string"==typeof t?function(t){const n=f(t);if(""===n[0]||""!==n[3])return c;const e=n[0],r=n[1],o=n[2],a=(e.charCodeAt(0)+3)%7,i=d(r),s=o.length?+o:void 0,u=m({step:a,alt:i,oct:s}),l=e+r+o,P=e+r,M=(y[a]+i+120)%12,p=void 0===s?(g=y[a]+i,A=12,(g%A+A)%A-1188):y[a]+i+12*(s+1),h=p>=0&&p<=127?p:null,b=void 0===s?null:440*Math.pow(2,(p-69)/12);var g,A;return{empty:!1,acc:r,alt:i,chroma:M,coord:u,freq:b,height:p,letter:e,midi:h,name:l,oct:s,pc:P,step:a}}(t):r(t)?M(function(t){const{step:n,alt:e,oct:r}=t,o=l(n);if(!o)return"";const a=o+P(e);return r||0===r?a+r:a}(t)):e(t)?M(t.name):c;return u.set(t,o),o}const p=/^([a-gA-G]?)(#{1,}|b{1,}|x{1,}|)(-?\d*)\s*(.*)$/;function f(t){const n=p.exec(t);return[n[1].toUpperCase(),n[2].replace(/x/g,"##"),n[3],n[4]]}function h(t){return M(s(t))}const y=[0,2,4,5,7,9,11];const b={empty:!0,name:"",acc:""},g=new RegExp("^([-+]?\\d+)(d{1,4}|m|M|P|A{1,4})|(AA|A|P|M|m|d|dd)([-+]?\\d+)$");function A(t){const n=g.exec(""+t);return null===n?["",""]:n[1]?[n[1],n[2]]:[n[4],n[3]]}const v={};function j(n){return"string"==typeof n?v[n]||(v[n]=function(t){const n=A(t);if(""===n[0])return b;const e=+n[0],r=n[1],o=(Math.abs(e)-1)%7,a="PMMPPMM"[o];if("M"===a&&"P"===r)return b;const i="M"===a?"majorable":"perfectable",s=""+e+r,c=e<0?-1:1,u=8===e||-8===e?e:c*(o+1),l=function(t,n){return"M"===n&&"majorable"===t||"P"===n&&"perfectable"===t?0:"m"===n&&"majorable"===t?-1:/^A+$/.test(n)?n.length:/^d+$/.test(n)?-1*("perfectable"===t?n.length:n.length+1):0}(i,r),P=Math.floor((Math.abs(e)-1)/7),d=c*(I[o]+l+12*P),M=(c*(I[o]+l)%12+12)%12,p=m({step:o,alt:l,oct:P,dir:c});return{empty:!1,name:s,num:e,q:r,step:o,alt:l,dir:c,type:i,simple:u,semitones:d,chroma:M,coord:p,oct:P}}(n)):r(n)?j(function(n){const{step:e,alt:r,oct:o=0,dir:a}=n;if(!a)return"";return(a<0?"-":"")+(e+1+7*o)+function(n,e){return 0===e?"majorable"===n?"M":"P":-1===e&&"majorable"===n?"m":e>0?t("A",e):t("d","perfectable"===n?e:e+1)}("M"==="PMMPPMM"[e]?"majorable":"perfectable",r)}(n)):e(n)?j(n.name):b}const I=[0,2,4,5,7,9,11];function w(t){const[n,e=0]=t;return j(s(7*n+12*e<0?[-n,-e,-1]:[n,e,1]))}function N(t,n){const e=M(t),r=j(n);if(e.empty||r.empty)return"";const o=e.coord,a=r.coord;return h(1===o.length?[o[0]+a[0]]:[o[0]+a[0],o[1]+a[1]]).name}function O(t,n){const e=M(t),r=M(n);if(e.empty||r.empty)return"";const o=e.coord,a=r.coord,m=a[0]-o[0];return w([m,2===o.length&&2===a.length?a[1]-o[1]:-Math.floor(7*m/12)]).name}var x=Object.freeze({__proto__:null,accToAlt:d,altToAcc:P,coordToInterval:w,coordToNote:h,decode:s,deprecate:n,distance:O,encode:m,fillStr:t,interval:j,isNamed:e,isPitch:r,note:M,stepToLetter:l,tokenizeInterval:A,tokenizeNote:f,transpose:N});const T=(t,n)=>Array(n+1).join(t),D=/^(_{1,}|=|\^{1,}|)([abcdefgABCDEFG])([,']*)$/;function S(t){const n=D.exec(t);return n?[n[1],n[2],n[3]]:["","",""]}function V(t){const[n,e,r]=S(t);if(""===e)return"";let o=4;for(let t=0;t96?e.toUpperCase()+a+(o+1):e+a+o}function C(t){const n=M(t);if(n.empty||!n.oct)return"";const{letter:e,acc:r,oct:o}=n;return("b"===r[0]?r.replace(/b/g,"_"):r.replace(/#/g,"^"))+(o>4?e.toLowerCase():e)+(5===o?"":o>4?T("'",o-5):T(",",4-o))}var $={abcToScientificNotation:V,scientificToAbcNotation:C,tokenize:S,transpose:function(t,n){return C(N(V(t),n))},distance:function(t,n){return O(V(t),V(n))}};function q(t){return t.map(t=>M(t)).filter(t=>!t.empty).sort((t,n)=>t.height-n.height).map(t=>t.name)}var k=Object.freeze({__proto__:null,compact:function(t){return t.filter(t=>0===t||t)},permutations:function t(n){return 0===n.length?[[]]:t(n.slice(1)).reduce((t,e)=>t.concat(n.map((t,r)=>{const o=e.slice();return o.splice(r,0,n[0]),o})),[])},range:function(t,n){return t0===n||t!==e[n-1])}});function E(t,n){return t0===t||t)}var z={compact:_,permutations:function t(n){return 0===n.length?[[]]:t(n.slice(1)).reduce((t,e)=>t.concat(n.map((t,r)=>{const o=e.slice();return o.splice(r,0,n[0]),o})),[])},range:E,rotate:F,shuffle:function(t,n=Math.random){let e,r,o=t.length;for(;o;)e=Math.floor(n()*o--),r=t[o],t[o]=t[e],t[e]=r;return t}};const R={empty:!0,name:"",setNum:0,chroma:"000000000000",normalized:"000000000000",intervals:[]},U=t=>Number(t).toString(2),B=t=>parseInt(t,2),G=/^[01]{12}$/;function K(t){return G.test(t)}const L={[R.chroma]:R};function H(t){const n=K(t)?t:"number"==typeof(e=t)&&e>=0&&e<=4095?U(t):Array.isArray(t)?function(t){if(0===t.length)return R.chroma;let n;const e=[0,0,0,0,0,0,0,0,0,0,0,0];for(let r=0;rt&&K(t.chroma))(t)?t.chroma:R.chroma;var e;return L[n]=L[n]||function(t){const n=B(t),e=function(t){const n=t.split("");return n.map((t,e)=>F(e,n).join(""))}(t).map(B).filter(t=>t>=2048).sort()[0],r=U(e),o=W(t);return{empty:!1,name:"",setNum:n,chroma:t,normalized:r,intervals:o}}(n)}const J=n("Pcset.pcset","Pcset.get",H),Q=["1P","2m","2M","3m","3M","4P","5d","5P","6m","6M","7m","7M"];function W(t){const n=[];for(let e=0;e<12;e++)"1"===t.charAt(e)&&n.push(Q[e]);return n}function X(t,n=!0){const e=H(t).chroma.split("");return _(e.map((t,r)=>{const o=F(r,e);return n&&"0"===o[0]?null:o.join("")}))}function Y(t){const n=H(t).setNum;return t=>{const e=H(t).setNum;return n&&n!==e&&(e&n)===e}}function Z(t){const n=H(t).setNum;return t=>{const e=H(t).setNum;return n&&n!==e&&(e|n)===e}}function tt(t){const n=H(t);return t=>{const e=M(t);return n&&!e.empty&&"1"===n.chroma.charAt(e.chroma)}}var nt={get:H,chroma:t=>H(t).chroma,num:t=>H(t).setNum,intervals:t=>H(t).intervals,chromas:function(){return E(2048,4095).map(U)},isSupersetOf:Z,isSubsetOf:Y,isNoteIncludedIn:tt,isEqual:function(t,n){return H(t).setNum===H(n).setNum},filter:function(t){const n=tt(t);return t=>t.filter(n)},modes:X,pcset:J};const et={...R,name:"",quality:"Unknown",intervals:[],aliases:[]};let rt=[],ot={};function at(t){return ot[t]||et}const mt=n("ChordType.chordType","ChordType.get",at);function it(){return rt.slice()}const st=n("ChordType.entries","ChordType.all",it);function ct(t,n,e){const r=function(t){const n=n=>-1!==t.indexOf(n);return n("5A")?"Augmented":n("3M")?"Major":n("5d")?"Diminished":n("3m")?"Minor":"Unknown"}(t),o={...H(t),name:e||"",quality:r,intervals:t,aliases:n};rt.push(o),o.name&&(ot[o.name]=o),ot[o.setNum]=o,ot[o.chroma]=o,o.aliases.forEach(t=>function(t,n){ot[n]=t}(o,t))}[["1P 3M 5P","major","M ^ "],["1P 3M 5P 7M","major seventh","maj7 Δ ma7 M7 Maj7 ^7"],["1P 3M 5P 7M 9M","major ninth","maj9 Δ9 ^9"],["1P 3M 5P 7M 9M 13M","major thirteenth","maj13 Maj13 ^13"],["1P 3M 5P 6M","sixth","6 add6 add13 M6"],["1P 3M 5P 6M 9M","sixth/ninth","6/9 69 M69"],["1P 3M 6m 7M","major seventh flat sixth","M7b6 ^7b6"],["1P 3M 5P 7M 11A","major seventh sharp eleventh","maj#4 Δ#4 Δ#11 M7#11 ^7#11 maj7#11"],["1P 3m 5P","minor","m min -"],["1P 3m 5P 7m","minor seventh","m7 min7 mi7 -7"],["1P 3m 5P 7M","minor/major seventh","m/ma7 m/maj7 mM7 mMaj7 m/M7 -Δ7 mΔ -^7"],["1P 3m 5P 6M","minor sixth","m6 -6"],["1P 3m 5P 7m 9M","minor ninth","m9 -9"],["1P 3m 5P 7M 9M","minor/major ninth","mM9 mMaj9 -^9"],["1P 3m 5P 7m 9M 11P","minor eleventh","m11 -11"],["1P 3m 5P 7m 9M 13M","minor thirteenth","m13 -13"],["1P 3m 5d","diminished","dim ° o"],["1P 3m 5d 7d","diminished seventh","dim7 °7 o7"],["1P 3m 5d 7m","half-diminished","m7b5 ø -7b5 h7 h"],["1P 3M 5P 7m","dominant seventh","7 dom"],["1P 3M 5P 7m 9M","dominant ninth","9"],["1P 3M 5P 7m 9M 13M","dominant thirteenth","13"],["1P 3M 5P 7m 11A","lydian dominant seventh","7#11 7#4"],["1P 3M 5P 7m 9m","dominant flat ninth","7b9"],["1P 3M 5P 7m 9A","dominant sharp ninth","7#9"],["1P 3M 7m 9m","altered","alt7"],["1P 4P 5P","suspended fourth","sus4 sus"],["1P 2M 5P","suspended second","sus2"],["1P 4P 5P 7m","suspended fourth seventh","7sus4 7sus"],["1P 5P 7m 9M 11P","eleventh","11"],["1P 4P 5P 7m 9m","suspended fourth flat ninth","b9sus phryg 7b9sus 7b9sus4"],["1P 5P","fifth","5"],["1P 3M 5A","augmented","aug + +5 ^#5"],["1P 3m 5A","minor augmented","m#5 -#5 m+"],["1P 3M 5A 7M","augmented seventh","maj7#5 maj7+5 +maj7 ^7#5"],["1P 3M 5P 7M 9M 11A","major sharp eleventh (lydian)","maj9#11 Δ9#11 ^9#11"],["1P 2M 4P 5P","","sus24 sus4add9"],["1P 3M 13m","","Mb6"],["1P 3M 5A 7M 9M","","maj9#5 Maj9#5"],["1P 3M 5A 7m","","7#5 +7 7+ 7aug aug7"],["1P 3M 5A 7m 9A","","7#5#9 7#9#5 7alt"],["1P 3M 5A 7m 9M","","9#5 9+"],["1P 3M 5A 7m 9M 11A","","9#5#11"],["1P 3M 5A 7m 9m","","7#5b9 7b9#5"],["1P 3M 5A 7m 9m 11A","","7#5b9#11"],["1P 3M 5A 9A","","+add#9"],["1P 3M 5A 9M","","M#5add9 +add9"],["1P 3M 5P 6M 11A","","M6#11 M6b5 6#11 6b5"],["1P 3M 5P 6M 7M 9M","","M7add13"],["1P 3M 5P 6M 9M 11A","","69#11"],["1P 3m 5P 6M 9M","","m69 -69"],["1P 3M 5P 6m 7m","","7b6"],["1P 3M 5P 7M 9A 11A","","maj7#9#11"],["1P 3M 5P 7M 9M 11A 13M","","M13#11 maj13#11 M13+4 M13#4"],["1P 3M 5P 7M 9m","","M7b9"],["1P 3M 5P 7m 11A 13m","","7#11b13 7b5b13"],["1P 3M 5P 7m 13M","","7add6 67 7add13"],["1P 3M 5P 7m 9A 11A","","7#9#11 7b5#9 7#9b5"],["1P 3M 5P 7m 9A 11A 13M","","13#9#11"],["1P 3M 5P 7m 9A 11A 13m","","7#9#11b13"],["1P 3M 5P 7m 9A 13M","","13#9"],["1P 3M 5P 7m 9A 13m","","7#9b13"],["1P 3M 5P 7m 9M 11A","","9#11 9+4 9#4"],["1P 3M 5P 7m 9M 11A 13M","","13#11 13+4 13#4"],["1P 3M 5P 7m 9M 11A 13m","","9#11b13 9b5b13"],["1P 3M 5P 7m 9m 11A","","7b9#11 7b5b9 7b9b5"],["1P 3M 5P 7m 9m 11A 13M","","13b9#11"],["1P 3M 5P 7m 9m 11A 13m","","7b9b13#11 7b9#11b13 7b5b9b13"],["1P 3M 5P 7m 9m 13M","","13b9"],["1P 3M 5P 7m 9m 13m","","7b9b13"],["1P 3M 5P 7m 9m 9A","","7b9#9"],["1P 3M 5P 9M","","Madd9 2 add9 add2"],["1P 3M 5P 9m","","Maddb9"],["1P 3M 5d","","Mb5"],["1P 3M 5d 6M 7m 9M","","13b5"],["1P 3M 5d 7M","","M7b5"],["1P 3M 5d 7M 9M","","M9b5"],["1P 3M 5d 7m","","7b5"],["1P 3M 5d 7m 9M","","9b5"],["1P 3M 7m","","7no5"],["1P 3M 7m 13m","","7b13"],["1P 3M 7m 9M","","9no5"],["1P 3M 7m 9M 13M","","13no5"],["1P 3M 7m 9M 13m","","9b13"],["1P 3m 4P 5P","","madd4"],["1P 3m 5P 6m 7M","","mMaj7b6"],["1P 3m 5P 6m 7M 9M","","mMaj9b6"],["1P 3m 5P 7m 11P","","m7add11 m7add4"],["1P 3m 5P 9M","","madd9"],["1P 3m 5d 6M 7M","","o7M7"],["1P 3m 5d 7M","","oM7"],["1P 3m 6m 7M","","mb6M7"],["1P 3m 6m 7m","","m7#5"],["1P 3m 6m 7m 9M","","m9#5"],["1P 3m 6m 7m 9M 11P","","m11A"],["1P 3m 6m 9m","","mb6b9"],["1P 2M 3m 5d 7m","","m9b5"],["1P 4P 5A 7M","","M7#5sus4"],["1P 4P 5A 7M 9M","","M9#5sus4"],["1P 4P 5A 7m","","7#5sus4"],["1P 4P 5P 7M","","M7sus4"],["1P 4P 5P 7M 9M","","M9sus4"],["1P 4P 5P 7m 9M","","9sus4 9sus"],["1P 4P 5P 7m 9M 13M","","13sus4 13sus"],["1P 4P 5P 7m 9m 13m","","7sus4b9b13 7b9b13sus4"],["1P 4P 7m 10m","","4 quartal"],["1P 5P 7m 9m 11P","","11b9"]].forEach(([t,n,e])=>ct(t.split(" "),e.split(" "),n)),rt.sort((t,n)=>t.setNum-n.setNum);var ut={names:function(){return rt.map(t=>t.name).filter(t=>t)},symbols:function(){return rt.map(t=>t.aliases[0]).filter(t=>t)},get:at,all:it,add:ct,removeAll:function(){rt=[],ot={}},keys:function(){return Object.keys(ot)},entries:st,chordType:mt};const lt={weight:0,name:""};const Pt={...R,intervals:[],aliases:[]};let dt=[],Mt={};function pt(){return dt.map(t=>t.name)}function ft(t){return Mt[t]||Pt}const ht=n("ScaleDictionary.scaleType","ScaleType.get",ft);function yt(){return dt.slice()}const bt=n("ScaleDictionary.entries","ScaleType.all",yt);function gt(t,n,e=[]){const r={...H(t),name:n,intervals:t,aliases:e};return dt.push(r),Mt[r.name]=r,Mt[r.setNum]=r,Mt[r.chroma]=r,r.aliases.forEach(t=>function(t,n){Mt[n]=t}(r,t)),r}[["1P 2M 3M 5P 6M","major pentatonic","pentatonic"],["1P 3M 4P 5P 7M","ionian pentatonic"],["1P 3M 4P 5P 7m","mixolydian pentatonic","indian"],["1P 2M 4P 5P 6M","ritusen"],["1P 2M 4P 5P 7m","egyptian"],["1P 3M 4P 5d 7m","neopolitan major pentatonic"],["1P 3m 4P 5P 6m","vietnamese 1"],["1P 2m 3m 5P 6m","pelog"],["1P 2m 4P 5P 6m","kumoijoshi"],["1P 2M 3m 5P 6m","hirajoshi"],["1P 2m 4P 5d 7m","iwato"],["1P 2m 4P 5P 7m","in-sen"],["1P 3M 4A 5P 7M","lydian pentatonic","chinese"],["1P 3m 4P 6m 7m","malkos raga"],["1P 3m 4P 5d 7m","locrian pentatonic","minor seven flat five pentatonic"],["1P 3m 4P 5P 7m","minor pentatonic","vietnamese 2"],["1P 3m 4P 5P 6M","minor six pentatonic"],["1P 2M 3m 5P 6M","flat three pentatonic","kumoi"],["1P 2M 3M 5P 6m","flat six pentatonic"],["1P 2m 3M 5P 6M","scriabin"],["1P 3M 5d 6m 7m","whole tone pentatonic"],["1P 3M 4A 5A 7M","lydian #5P pentatonic"],["1P 3M 4A 5P 7m","lydian dominant pentatonic"],["1P 3m 4P 5P 7M","minor #7M pentatonic"],["1P 3m 4d 5d 7m","super locrian pentatonic"],["1P 2M 3m 4P 5P 7M","minor hexatonic"],["1P 2A 3M 5P 5A 7M","augmented"],["1P 2M 3m 3M 5P 6M","major blues"],["1P 2M 4P 5P 6M 7m","piongio"],["1P 2m 3M 4A 6M 7m","prometheus neopolitan"],["1P 2M 3M 4A 6M 7m","prometheus"],["1P 2m 3M 5d 6m 7m","mystery #1"],["1P 2m 3M 4P 5A 6M","six tone symmetric"],["1P 2M 3M 4A 5A 7m","whole tone","messiaen's mode #1"],["1P 2m 4P 4A 5P 7M","messiaen's mode #5"],["1P 3m 4P 5d 5P 7m","minor blues","blues"],["1P 2M 3M 4P 5d 6m 7m","locrian major","arabian"],["1P 2m 3M 4A 5P 6m 7M","double harmonic lydian"],["1P 2M 3m 4P 5P 6m 7M","harmonic minor"],["1P 2m 3m 4d 5d 6m 7m","altered","super locrian","diminished whole tone","pomeroy"],["1P 2M 3m 4P 5d 6m 7m","locrian #2","half-diminished","aeolian b5"],["1P 2M 3M 4P 5P 6m 7m","mixolydian b6","melodic minor fifth mode","hindu"],["1P 2M 3M 4A 5P 6M 7m","lydian dominant","lydian b7","overtone"],["1P 2M 3M 4A 5P 6M 7M","lydian"],["1P 2M 3M 4A 5A 6M 7M","lydian augmented"],["1P 2m 3m 4P 5P 6M 7m","dorian b2","phrygian #6","melodic minor second mode"],["1P 2M 3m 4P 5P 6M 7M","melodic minor"],["1P 2m 3m 4P 5d 6m 7m","locrian"],["1P 2m 3m 4d 5d 6m 7d","ultralocrian","superlocrian bb7","·superlocrian diminished"],["1P 2m 3m 4P 5d 6M 7m","locrian 6","locrian natural 6","locrian sharp 6"],["1P 2A 3M 4P 5P 5A 7M","augmented heptatonic"],["1P 2M 3m 5d 5P 6M 7m","romanian minor"],["1P 2M 3m 4A 5P 6M 7m","dorian #4"],["1P 2M 3m 4A 5P 6M 7M","lydian diminished"],["1P 2m 3m 4P 5P 6m 7m","phrygian"],["1P 2M 3M 4A 5A 7m 7M","leading whole tone"],["1P 2M 3M 4A 5P 6m 7m","lydian minor"],["1P 2m 3M 4P 5P 6m 7m","phrygian dominant","spanish","phrygian major"],["1P 2m 3m 4P 5P 6m 7M","balinese"],["1P 2m 3m 4P 5P 6M 7M","neopolitan major"],["1P 2M 3m 4P 5P 6m 7m","aeolian","minor"],["1P 2M 3M 4P 5P 6m 7M","harmonic major"],["1P 2m 3M 4P 5P 6m 7M","double harmonic major","gypsy"],["1P 2M 3m 4P 5P 6M 7m","dorian"],["1P 2M 3m 4A 5P 6m 7M","hungarian minor"],["1P 2A 3M 4A 5P 6M 7m","hungarian major"],["1P 2m 3M 4P 5d 6M 7m","oriental"],["1P 2m 3m 3M 4A 5P 7m","flamenco"],["1P 2m 3m 4A 5P 6m 7M","todi raga"],["1P 2M 3M 4P 5P 6M 7m","mixolydian","dominant"],["1P 2m 3M 4P 5d 6m 7M","persian"],["1P 2M 3M 4P 5P 6M 7M","major","ionian"],["1P 2m 3M 5d 6m 7m 7M","enigmatic"],["1P 2M 3M 4P 5A 6M 7M","major augmented","major #5","ionian augmented","ionian #5"],["1P 2A 3M 4A 5P 6M 7M","lydian #9"],["1P 2m 2M 4P 4A 5P 6m 7M","messiaen's mode #4"],["1P 2m 3M 4P 4A 5P 6m 7M","purvi raga"],["1P 2m 3m 3M 4P 5P 6m 7m","spanish heptatonic"],["1P 2M 3M 4P 5P 6M 7m 7M","bebop"],["1P 2M 3m 3M 4P 5P 6M 7m","bebop minor"],["1P 2M 3M 4P 5P 5A 6M 7M","bebop major"],["1P 2m 3m 4P 5d 5P 6m 7m","bebop locrian"],["1P 2M 3m 4P 5P 6m 7m 7M","minor bebop"],["1P 2M 3m 4P 5d 6m 6M 7M","diminished","whole-half diminished"],["1P 2M 3M 4P 5d 5P 6M 7M","ichikosucho"],["1P 2M 3m 4P 5P 6m 6M 7M","minor six diminished"],["1P 2m 3m 3M 4A 5P 6M 7m","half-whole diminished","dominant diminished","messiaen's mode #2"],["1P 3m 3M 4P 5P 6M 7m 7M","kafi raga"],["1P 2M 3M 4P 4A 5A 6A 7M","messiaen's mode #6"],["1P 2M 3m 3M 4P 5d 5P 6M 7m","composite blues"],["1P 2M 3m 3M 4A 5P 6m 7m 7M","messiaen's mode #3"],["1P 2m 2M 3m 4P 4A 5P 6m 6M 7M","messiaen's mode #7"],["1P 2m 2M 3m 3M 4P 5d 5P 6m 6M 7m 7M","chromatic"]].forEach(([t,n,...e])=>gt(t.split(" "),n,e));var At={names:pt,get:ft,all:yt,add:gt,removeAll:function(){dt=[],Mt={}},keys:function(){return Object.keys(Mt)},entries:bt,scaleType:ht};const vt={empty:!0,name:"",symbol:"",root:"",rootDegree:0,type:"",tonic:null,setNum:NaN,quality:"Unknown",chroma:"",normalized:"",aliases:[],notes:[],intervals:[]},jt=/^(6|64|7|9|11|13)$/;function It(t){const[n,e,r,o]=f(t);return""===n?["",t]:"A"===n&&"ug"===o?["","aug"]:o||"4"!==r&&"5"!==r?jt.test(r)?[n+e,r+o]:[n+e+r,o]:[n+e,r]}function wt(t){if(""===t)return vt;if(Array.isArray(t)&&2===t.length)return Nt(t[1],t[0]);{const[n,e]=It(t),r=Nt(e,n);return r.empty?Nt(t):r}}function Nt(t,n,e){const r=at(t),o=M(n||""),a=M(e||"");if(r.empty||n&&o.empty||e&&a.empty)return vt;const m=O(o.pc,a.pc),i=r.intervals.indexOf(m)+1;if(!a.empty&&!i)return vt;const s=o.empty?[]:r.intervals.map(t=>N(o,t));t=-1!==r.aliases.indexOf(t)?t:r.aliases[0];const c=`${o.empty?"":o.pc}${t}${a.empty?"":"/"+a.pc}`,u=`${n?o.pc+" ":""}${r.name}${e?" over "+a.pc:""}`;return{...r,name:u,symbol:c,type:r.name,root:a.name,rootDegree:i,tonic:o.name,notes:s}}var Ot={getChord:Nt,get:wt,detect:function(t){const n=t.map(t=>M(t).pc).filter(t=>t);return 0===M.length?[]:function(t,n){const e=t[0],r=M(e).chroma,o=(t=>{const n=t.reduce((t,n)=>{const e=M(n).chroma;return void 0!==e&&(t[e]=t[e]||M(n).name),t},{});return t=>n[t]})(t);return X(t,!1).map((t,a)=>{const m=at(t).aliases[0];if(!m)return lt;const i=o(a);return a!==r?{weight:.5*n,name:`${i}${m}/${e}`}:{weight:1*n,name:`${i}${m}`}})}(n,1).filter(t=>t.weight).sort((t,n)=>n.weight-t.weight).map(t=>t.name)},chordScales:function(t){const n=Z(wt(t).chroma);return yt().filter(t=>n(t.chroma)).map(t=>t.name)},extended:function(t){const n=wt(t),e=Z(n.chroma);return it().filter(t=>e(t.chroma)).map(t=>n.tonic+t.aliases[0])},reduced:function(t){const n=wt(t),e=Y(n.chroma);return it().filter(t=>e(t.chroma)).map(t=>n.tonic+t.aliases[0])},tokenize:It,transpose:function(t,n){const[e,r]=It(t);return e?N(e,n)+r:t},chord:n("Chord.chord","Chord.get",wt)};const xt=[];[[.125,"dl",["large","duplex longa","maxima","octuple","octuple whole"]],[.25,"l",["long","longa"]],[.5,"d",["double whole","double","breve"]],[1,"w",["whole","semibreve"]],[2,"h",["half","minim"]],[4,"q",["quarter","crotchet"]],[8,"e",["eighth","quaver"]],[16,"s",["sixteenth","semiquaver"]],[32,"t",["thirty-second","demisemiquaver"]],[64,"sf",["sixty-fourth","hemidemisemiquaver"]],[128,"h",["hundred twenty-eighth"]],[256,"th",["two hundred fifty-sixth"]]].forEach(([t,n,e])=>function(t,n,e){xt.push({empty:!1,dots:"",name:"",value:1/t,fraction:t<1?[1/t,1]:[1,t],shorthand:n,names:e})}(t,n,e));const Tt={empty:!0,name:"",value:0,fraction:[0,0],shorthand:"",dots:"",names:[]};const Dt=/^([^.]+)(\.*)$/;function St(t){const[n,e,r]=Dt.exec(t)||[],o=xt.find(t=>t.shorthand===e||t.names.includes(e));if(!o)return Tt;const a=function(t,n){const e=Math.pow(2,n);let r=t[0]*e,o=t[1]*e;const a=r;for(let t=0;t(n.names.forEach(n=>t.push(n)),t),[])},shorthands:function(){return xt.map(t=>t.shorthand)},get:St,value:t=>St(t).value,fraction:t=>St(t).fraction};const Ct=j;const $t=[1,2,2,3,3,4,5,5,6,6,7,7],qt="P m M m M P d P m M m M".split(" ");const kt=O,Et=zt((t,n)=>[t[0]+n[0],t[1]+n[1]]),Ft=zt((t,n)=>[t[0]-n[0],t[1]-n[1]]);var _t={names:function(){return"1P 2M 3M 4P 5P 6m 7m".split(" ")},get:Ct,name:t=>j(t).name,num:t=>j(t).num,semitones:t=>j(t).semitones,quality:t=>j(t).q,fromSemitones:function(t){const n=t<0?-1:1,e=Math.abs(t),r=e%12,o=Math.floor(e/12);return n*($t[r]+7*o)+qt[r]},distance:kt,invert:function(t){const n=j(t);return n.empty?"":j({step:(7-n.step)%7,alt:"perfectable"===n.type?-n.alt:-(n.alt+1),oct:n.oct,dir:n.dir}).name},simplify:function(t){const n=j(t);return n.empty?"":n.simple+n.q},add:Et,addTo:t=>n=>Et(t,n),substract:Ft};function zt(t){return(n,e)=>{const r=j(n).coord,o=j(e).coord;if(r&&o){return w(t(r,o)).name}}}function Rt(t){return+t>=0&&+t<=127}function Ut(t){if(Rt(t))return+t;const n=M(t);return n.empty?null:n.midi}const Bt=Math.log(2),Gt=Math.log(440);function Kt(t){const n=12*(Math.log(t)-Gt)/Bt+69;return Math.round(100*n)/100}const Lt="C C# D D# E F F# G G# A A# B".split(" "),Ht="C Db D Eb E F Gb G Ab A Bb B".split(" ");function Jt(t,n={}){if(isNaN(t)||t===-1/0||t===1/0)return"";t=Math.round(t);const e=(!0===n.sharps?Lt:Ht)[t%12];return n.pitchClass?e:e+(Math.floor(t/12)-1)}var Qt={isMidi:Rt,toMidi:Ut,midiToFreq:function(t,n=440){return Math.pow(2,(t-69)/12)*n},midiToNoteName:Jt,freqToMidi:Kt};const Wt=["C","D","E","F","G","A","B"],Xt=t=>t.name,Yt=t=>t.map(M).filter(t=>!t.empty);const Zt=M;const tn=N,nn=N,en=t=>n=>tn(n,t),rn=en,on=t=>n=>tn(t,n),an=on;function mn(t,n){const e=Zt(t);if(e.empty)return"";const[r,o]=e.coord;return h(void 0===o?[r+n]:[r+n,o]).name}const sn=mn,cn=(t,n)=>t.height-n.height;function un(t,n){return n=n||cn,Yt(t).sort(n).map(Xt)}function ln(t){return un(t,cn).filter((t,n,e)=>0===n||t!==e[n-1])}const Pn=Mn(!0),dn=Mn(!1);function Mn(t){return n=>{const e=Zt(n);if(e.empty)return"";const r=t?e.alt>0:e.alt<0,o=null===e.midi;return Jt(e.midi||e.chroma,{sharps:r,pitchClass:o})}}var pn={names:function(t){return void 0===t?Wt.slice():Array.isArray(t)?Yt(t).map(Xt):[]},get:Zt,name:t=>Zt(t).name,pitchClass:t=>Zt(t).pc,accidentals:t=>Zt(t).acc,octave:t=>Zt(t).oct,midi:t=>Zt(t).midi,ascending:cn,descending:(t,n)=>n.height-t.height,sortedNames:un,sortedUniqNames:ln,fromMidi:function(t){return Jt(t)},fromMidiSharps:function(t){return Jt(t,{sharps:!0})},freq:t=>Zt(t).freq,fromFreq:function(t){return Jt(Kt(t))},fromFreqSharps:function(t){return Jt(Kt(t),{sharps:!0})},chroma:t=>Zt(t).chroma,transpose:tn,tr:nn,transposeBy:en,trBy:rn,transposeFrom:on,trFrom:an,transposeFifths:mn,trFifths:sn,simplify:Pn,enharmonic:dn};const fn={empty:!0,name:"",chordType:""},hn={};function yn(t){return"string"==typeof t?hn[t]||(hn[t]=function(t){const[n,e,r,o]=(a=t,gn.exec(a)||["","","",""]);var a;if(!r)return fn;const m=r.toUpperCase(),i=vn.indexOf(m),s=d(e);return{empty:!1,name:n,roman:r,interval:j({step:i,alt:s,dir:1}).name,acc:e,chordType:o,alt:s,step:i,major:r===m,oct:0,dir:1}}(t)):"number"==typeof t?yn(vn[t]||""):r(t)?yn(P((n=t).alt)+vn[n.step]):e(t)?yn(t.name):fn;var n}const bn=n("RomanNumeral.romanNumeral","RomanNumeral.get",yn);const gn=/^(#{1,}|b{1,}|x{1,}|)(IV|I{1,3}|VI{0,2}|iv|i{1,3}|vi{0,2})([^IViv]*)$/;const An="I II III IV V VI VII",vn=An.split(" "),jn=An.toLowerCase().split(" ");var In={names:function(t=!0){return(t?vn:jn).slice()},get:yn,romanNumeral:bn};const wn=Object.freeze([]),Nn={type:"major",tonic:"",alteration:0,keySignature:""},On={tonic:"",grades:wn,intervals:wn,scale:wn,chords:wn,chordsHarmonicFunction:wn,chordScales:wn},xn={...Nn,...On,type:"major",minorRelative:"",scale:wn,secondaryDominants:wn,secondaryDominantsMinorRelative:wn,substituteDominants:wn,substituteDominantsMinorRelative:wn},Tn={...Nn,type:"minor",relativeMajor:"",natural:On,harmonic:On,melodic:On},Dn=(t,n,e="")=>n.map((n,r)=>`${t[r]}${e}${n}`);function Sn(t,n,e,r){return o=>{const a=t.map(t=>yn(t).interval||""),m=a.map(t=>N(o,t));return{tonic:o,grades:t,intervals:a,scale:m,chords:Dn(m,n),chordsHarmonicFunction:Dn(m,e),chordScales:Dn(m,r," ")}}}const Vn=(t,n)=>{const e=M(t),r=M(n);return e.empty||r.empty?0:r.coord[0]-e.coord[0]},Cn=Sn("I II III IV V VI VII".split(" "),"maj7 m7 m7 maj7 7 m7 m7b5".split(" "),"T SD T SD D T D".split(" "),"major,dorian,phrygian,lydian,mixolydian,minor,locrian".split(",")),$n=Sn("I II bIII IV V bVI bVII".split(" "),"m7 m7b5 maj7 m7 m7 maj7 7".split(" "),"T SD T SD D SD SD".split(" "),"minor,locrian,major,dorian,phrygian,lydian,mixolydian".split(",")),qn=Sn("I II bIII IV V bVI VII".split(" "),"mMaj7 m7b5 +maj7 m7 7 maj7 o7".split(" "),"T SD T SD D SD D".split(" "),"harmonic minor,locrian 6,major augmented,lydian diminished,phrygian dominant,lydian #9,ultralocrian".split(",")),kn=Sn("I II bIII IV V VI VII".split(" "),"m6 m7 +maj7 7 7 m7b5 m7b5".split(" "),"T SD T SD D - -".split(" "),"melodic minor,dorian b2,lydian augmented,lydian dominant,mixolydian b6,locrian #2,altered".split(","));var En={majorKey:function(t){const n=M(t).pc;if(!n)return xn;const e=Cn(n),r=Vn("C",n),o=n=>{const e=yn(n);return e.empty?"":N(t,e.interval)+e.chordType};return{...e,type:"major",minorRelative:N(n,"-3m"),alteration:r,keySignature:P(r),secondaryDominants:"- VI7 VII7 I7 II7 III7 -".split(" ").map(o),secondaryDominantsMinorRelative:"- IIIm7b5 IV#m7 Vm7 VIm7 VIIm7b5 -".split(" ").map(o),substituteDominants:"- bIII7 IV7 bV7 bVI7 bVII7 -".split(" ").map(o),substituteDominantsMinorRelative:"- IIIm7 Im7 IIbm7 VIm7 IVm7 -".split(" ").map(o)}},majorTonicFromKeySignature:function(t){return"number"==typeof t?mn("C",t):"string"==typeof t&&/^b+|#+$/.test(t)?mn("C",d(t)):null},minorKey:function(t){const n=M(t).pc;if(!n)return Tn;const e=Vn("C",n)-3;return{type:"minor",tonic:n,relativeMajor:N(n,"3m"),alteration:e,keySignature:P(e),natural:$n(n),harmonic:qn(n),melodic:kn(n)}}};const Fn={...R,name:"",alt:0,modeNum:NaN,triad:"",seventh:"",aliases:[]},_n=[[0,2773,0,"ionian","","Maj7","major"],[1,2902,2,"dorian","m","m7"],[2,3418,4,"phrygian","m","m7"],[3,2741,-1,"lydian","","Maj7"],[4,2774,1,"mixolydian","","7"],[5,2906,3,"aeolian","m","m7","minor"],[6,3434,5,"locrian","dim","m7b5"]].map((function(t){const[n,e,r,o,a,m,i]=t,s=i?[i]:[],c=Number(e).toString(2);return{empty:!1,intervals:W(c),modeNum:n,chroma:c,normalized:c,name:o,setNum:e,alt:r,triad:a,seventh:m,aliases:s}})),zn={};function Rn(t){return"string"==typeof t?zn[t.toLowerCase()]||Fn:t&&t.name?Rn(t.name):Fn}_n.forEach(t=>{zn[t.name]=t,t.aliases.forEach(n=>{zn[n]=t})});const Un=n("Mode.mode","Mode.get",Rn);function Bn(){return _n.slice()}var Gn={get:Rn,names:function(){return _n.map(t=>t.name)},all:Bn,entries:n("Mode.mode","Mode.all",Bn),mode:Un};var Kn={fromRomanNumerals:function(t,n){return n.map(yn).map(n=>N(t,j(n))+n.chordType)},toRomanNumerals:function(t,n){return n.map(n=>{const[e,r]=It(n);return yn(j(O(t,e))).name+r})}};function Ln(t){const n=_(t.map(Ut));return t.length&&n.length===t.length?n.reduce((t,n)=>{const e=t[t.length-1];return t.concat(E(e,n).slice(1))},[n[0]]):[]}var Hn={numeric:Ln,chromatic:function(t,n){return Ln(t).map(t=>Jt(t,n))}};const Jn={empty:!0,name:"",type:"",tonic:null,setNum:NaN,chroma:"",normalized:"",aliases:[],notes:[],intervals:[]};function Qn(t){if("string"!=typeof t)return["",""];const n=t.indexOf(" "),e=M(t.substring(0,n));if(e.empty){const n=M(t);return n.empty?["",t]:[n.name,""]}const r=t.substring(e.name.length+1);return[e.name,r.length?r:""]}function Wn(t){const n=Array.isArray(t)?t:Qn(t),e=M(n[0]).name,r=ft(n[1]);if(r.empty)return Jn;const o=r.name,a=e?r.intervals.map(t=>N(e,t)):[],m=e?e+" "+o:o;return{...r,name:m,type:o,tonic:e,notes:a}}var Xn={get:Wn,names:pt,extended:function(t){const n=Z(Wn(t).chroma);return yt().filter(t=>n(t.chroma)).map(t=>t.name)},modeNames:function(t){const n=Wn(t);if(n.empty)return[];const e=n.tonic?n.notes:n.intervals;return X(n.chroma).map((t,n)=>{const r=Wn(t).name;return r?[e[n],r]:["",""]}).filter(t=>t[0])},reduced:function(t){const n=Y(Wn(t).chroma);return yt().filter(t=>n(t.chroma)).map(t=>t.name)},scaleChords:function(t){const n=Y(Wn(t).chroma);return it().filter(t=>n(t.chroma)).map(t=>t.aliases[0])},scaleNotes:function(t){const n=t.map(t=>M(t).pc).filter(t=>t),e=n[0],r=ln(n);return F(r.indexOf(e),r)},tokenize:Qn,scale:n("Scale.scale","Scale.get",Wn)};const Yn={empty:!0,name:"",upper:void 0,lower:void 0,type:void 0,additive:[]},Zn=["4/4","3/4","2/4","2/2","12/8","9/8","6/8","3/8"];const te=/^(\d?\d(?:\+\d)*)\/(\d)$/,ne=new Map;function ee(t){if("string"==typeof t){const[n,e,r]=te.exec(t)||[];return ee([e,r])}const[n,e]=t,r=+e;if("number"==typeof n)return[n,r];const o=n.split("+").map(t=>+t);return 1===o.length?[o[0],r]:[o,r]}var re={names:function(){return Zn.slice()},parse:ee,get:function(t){const n=ne.get(t);if(n)return n;const e=function([t,n]){const e=Array.isArray(t)?t.reduce((t,n)=>t+n,0):t,r=n;if(0===e||0===r)return Yn;const o=Array.isArray(t)?`${t.join("+")}/${n}`:`${t}/${n}`,a=Array.isArray(t)?t:[];return{empty:!1,name:o,type:4===r||2===r?"simple":8===r&&e%3==0?"compound":"irregular",upper:e,lower:r,additive:a}}(ee(t));return ne.set(t,e),e}};var oe,ae,me=(function(t,n){!function(t,n,e,r,o,a,m,i,s,c,u,l,P,d,M,p,f,h,y,b){n=n&&Object.prototype.hasOwnProperty.call(n,"default")?n.default:n,r=r&&Object.prototype.hasOwnProperty.call(r,"default")?r.default:r,o=o&&Object.prototype.hasOwnProperty.call(o,"default")?o.default:o,a=a&&Object.prototype.hasOwnProperty.call(a,"default")?a.default:a,i=i&&Object.prototype.hasOwnProperty.call(i,"default")?i.default:i,s=s&&Object.prototype.hasOwnProperty.call(s,"default")?s.default:s,c=c&&Object.prototype.hasOwnProperty.call(c,"default")?c.default:c,u=u&&Object.prototype.hasOwnProperty.call(u,"default")?u.default:u,l=l&&Object.prototype.hasOwnProperty.call(l,"default")?l.default:l,P=P&&Object.prototype.hasOwnProperty.call(P,"default")?P.default:P,d=d&&Object.prototype.hasOwnProperty.call(d,"default")?d.default:d,M=M&&Object.prototype.hasOwnProperty.call(M,"default")?M.default:M,p=p&&Object.prototype.hasOwnProperty.call(p,"default")?p.default:p,f=f&&Object.prototype.hasOwnProperty.call(f,"default")?f.default:f,h=h&&Object.prototype.hasOwnProperty.call(h,"default")?h.default:h,y=y&&Object.prototype.hasOwnProperty.call(y,"default")?y.default:y,b=b&&Object.prototype.hasOwnProperty.call(b,"default")?b.default:b;var g=m,A=d,v=o,j=y;Object.keys(m).forEach((function(n){"default"!==n&&Object.defineProperty(t,n,{enumerable:!0,get:function(){return m[n]}})})),t.AbcNotation=n,t.Array=e,t.Chord=r,t.ChordType=o,t.Collection=a,t.Core=m,t.DurationValue=i,t.Interval=s,t.Key=c,t.Midi=u,t.Mode=l,t.Note=P,t.Pcset=d,t.Progression=M,t.Range=p,t.RomanNumeral=f,t.Scale=h,t.ScaleType=y,t.TimeSignature=b,t.ChordDictionary=v,t.PcSet=A,t.ScaleDictionary=j,t.Tonal=g,Object.defineProperty(t,"__esModule",{value:!0})}(n,$,k,Ot,ut,z,x,Vt,_t,En,Qt,Gn,pn,nt,Kn,Hn,In,Xn,At,re)}(oe={exports:{}},oe.exports),oe.exports);return(ae=me)&&ae.__esModule&&Object.prototype.hasOwnProperty.call(ae,"default")?ae.default:ae}(); -------------------------------------------------------------------------------- /scripts/modules/midi.js: -------------------------------------------------------------------------------- 1 | !function(t,e){if("object"==typeof exports&&"object"==typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var r=e();for(var n in r)("object"==typeof exports?exports:t)[n]=r[n]}}("undefined"!=typeof self?self:this,(function(){return function(t){var e={};function r(n){if(e[n])return e[n].exports;var i=e[n]={i:n,l:!1,exports:{}};return t[n].call(i.exports,i,i.exports,r),i.l=!0,i.exports}return r.m=t,r.c=e,r.d=function(t,e,n){r.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:n})},r.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},r.t=function(t,e){if(1&e&&(t=r(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var i in t)r.d(n,i,function(e){return t[e]}.bind(null,i));return n},r.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return r.d(e,"a",e),e},r.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},r.p="",r(r.s=5)}([function(t,e,r){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var n=r(2),i=new WeakMap;e.keySignatureKeys=["Cb","Gb","Db","Ab","Eb","Bb","F","C","G","D","A","E","B","F#","C#"];var a=function(){function t(t){var r=this;this.tempos=[],this.timeSignatures=[],this.keySignatures=[],this.meta=[],this.name="",i.set(this,480),t&&(i.set(this,t.header.ticksPerBeat),t.tracks.forEach((function(t){return t.forEach((function(t){t.meta&&("timeSignature"===t.type?r.timeSignatures.push({ticks:t.absoluteTime,timeSignature:[t.numerator,t.denominator]}):"setTempo"===t.type?r.tempos.push({bpm:6e7/t.microsecondsPerBeat,ticks:t.absoluteTime}):"keySignature"===t.type&&r.keySignatures.push({key:e.keySignatureKeys[t.key+7],scale:0===t.scale?"major":"minor",ticks:t.absoluteTime}))}))})),t.tracks[0].forEach((function(t){t.meta&&("trackName"===t.type?r.name=t.text:"text"!==t.type&&"cuePoint"!==t.type&&"marker"!==t.type&&"lyrics"!==t.type||r.meta.push({text:t.text,ticks:t.absoluteTime,type:t.type}))})),this.update())}return t.prototype.update=function(){var t=this,e=0,r=0;this.tempos.sort((function(t,e){return t.ticks-e.ticks})),this.tempos.forEach((function(n,i){var a=i>0?t.tempos[i-1].bpm:t.tempos[0].bpm,o=n.ticks/t.ppq-r,s=60/a*o;n.time=s+e,e=n.time,r+=o})),this.timeSignatures.sort((function(t,e){return t.ticks-e.ticks})),this.timeSignatures.forEach((function(e,r){var n=r>0?t.timeSignatures[r-1]:t.timeSignatures[0],i=(e.ticks-n.ticks)/t.ppq/n.timeSignature[0]/(n.timeSignature[1]/4);n.measures=n.measures||0,e.measures=i+n.measures}))},t.prototype.ticksToSeconds=function(t){var e=n.search(this.tempos,t);if(-1!==e){var r=this.tempos[e],i=r.time,a=(t-r.ticks)/this.ppq;return i+60/r.bpm*a}return.5*(t/this.ppq)},t.prototype.ticksToMeasures=function(t){var e=n.search(this.timeSignatures,t);if(-1!==e){var r=this.timeSignatures[e],i=(t-r.ticks)/this.ppq;return r.measures+i/(r.timeSignature[0]/r.timeSignature[1])/4}return t/this.ppq/4},Object.defineProperty(t.prototype,"ppq",{get:function(){return i.get(this)},enumerable:!0,configurable:!0}),t.prototype.secondsToTicks=function(t){var e=n.search(this.tempos,t,"time");if(-1!==e){var r=this.tempos[e],i=(t-r.time)/(60/r.bpm);return Math.round(r.ticks+i*this.ppq)}var a=t/.5;return Math.round(a*this.ppq)},t.prototype.toJSON=function(){return{keySignatures:this.keySignatures,meta:this.meta,name:this.name,ppq:this.ppq,tempos:this.tempos.map((function(t){return{bpm:t.bpm,ticks:t.ticks}})),timeSignatures:this.timeSignatures}},t.prototype.fromJSON=function(t){this.name=t.name,this.tempos=t.tempos.map((function(t){return Object.assign({},t)})),this.timeSignatures=t.timeSignatures.map((function(t){return Object.assign({},t)})),this.keySignatures=t.keySignatures.map((function(t){return Object.assign({},t)})),this.meta=t.meta.map((function(t){return Object.assign({},t)})),i.set(this,t.ppq),this.update()},t.prototype.setTempo=function(t){this.tempos=[{bpm:t,ticks:0}],this.update()},t}();e.Header=a},function(t,e,r){e.parseMidi=r(6),e.writeMidi=r(7)},function(t,e,r){"use strict";function n(t,e,r){void 0===r&&(r="ticks");var n=0,i=t.length,a=i;if(i>0&&t[i-1][r]<=e)return i-1;for(;ne)return o;s[r]>e?a=o:s[r]=t.absoluteTime}));if(-1!==e){var r=o.splice(e,1)[0];h.addNote({durationTicks:r.absoluteTime-t.absoluteTime,midi:t.noteNumber,noteOffVelocity:r.velocity/127,ticks:t.absoluteTime,velocity:t.velocity/127})}},h=this;i.length;)u();t.filter((function(t){return"controller"===t.type})).forEach((function(t){r.addCC({number:t.controllerType,ticks:t.absoluteTime,value:t.value/127})})),t.filter((function(t){return"pitchBend"===t.type})).forEach((function(t){r.addPitchBend({ticks:t.absoluteTime,value:t.value/Math.pow(2,13)})}))}}return t.prototype.addNote=function(t){var e=c.get(this),r=new u.Note({midi:0,ticks:0,velocity:1},{ticks:0,velocity:0},e);return Object.assign(r,t),n.insert(this.notes,r,"ticks"),this},t.prototype.addCC=function(t){var e=c.get(this),r=new i.ControlChange({controllerType:t.number},e);return delete t.number,Object.assign(r,t),Array.isArray(this.controlChanges[r.number])||(this.controlChanges[r.number]=[]),n.insert(this.controlChanges[r.number],r,"ticks"),this},t.prototype.addPitchBend=function(t){var e=c.get(this),r=new o.PitchBend({},e);return Object.assign(r,t),n.insert(this.pitchBends,r,"ticks"),this},Object.defineProperty(t.prototype,"duration",{get:function(){if(!this.notes.length)return 0;for(var t=this.notes[this.notes.length-1].time+this.notes[this.notes.length-1].duration,e=0;e0&&i[i.length-1])||6!==a[0]&&2!==a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]>4;switch(t.channel=15&n,u){case 8:return t.type="noteOff",t.noteNumber=s,t.velocity=r.readUInt8(),t;case 9:var c=r.readUInt8();return t.type=0===c?"noteOff":"noteOn",t.noteNumber=s,t.velocity=c,0===c&&(t.byte9=!0),t;case 10:return t.type="noteAftertouch",t.noteNumber=s,t.amount=r.readUInt8(),t;case 11:return t.type="controller",t.controllerType=s,t.value=r.readUInt8(),t;case 12:return t.type="programChange",t.programNumber=s,t;case 13:return t.type="channelAftertouch",t.amount=s,t;case 14:return t.type="pitchBend",t.value=s+(r.readUInt8()<<7)-8192,t;default:throw"Unrecognised MIDI event type: "+u}}}}function n(t){this.buffer=t,this.bufferLen=this.buffer.length,this.pos=0}n.prototype.eof=function(){return this.pos>=this.bufferLen},n.prototype.readUInt8=function(){var t=this.buffer[this.pos];return this.pos+=1,t},n.prototype.readInt8=function(){var t=this.readUInt8();return 128&t?t-256:t},n.prototype.readUInt16=function(){return(this.readUInt8()<<8)+this.readUInt8()},n.prototype.readInt16=function(){var t=this.readUInt16();return 32768&t?t-65536:t},n.prototype.readUInt24=function(){return(this.readUInt8()<<16)+(this.readUInt8()<<8)+this.readUInt8()},n.prototype.readInt24=function(){var t=this.readUInt24();return 8388608&t?t-16777216:t},n.prototype.readUInt32=function(){return(this.readUInt8()<<24)+(this.readUInt8()<<16)+(this.readUInt8()<<8)+this.readUInt8()},n.prototype.readBytes=function(t){var e=this.buffer.slice(this.pos,this.pos+t);return this.pos+=t,e},n.prototype.readString=function(t){var e=this.readBytes(t);return String.fromCharCode.apply(null,e)},n.prototype.readVarInt=function(){for(var t=0;!this.eof();){var e=this.readUInt8();if(!(128&e))return t+e;t+=127&e,t<<=7}return t},n.prototype.readChunk=function(){var t=this.readString(4),e=this.readUInt32();return{id:t,length:e,data:this.readBytes(e)}},t.exports=function(t){var e=new n(t),i=e.readChunk();if("MThd"!=i.id)throw"Bad MIDI file. Expected 'MHdr', got: '"+i.id+"'";for(var a=function(t){var e=new n(t),r=e.readUInt16(),i=e.readUInt16(),a={format:r,numTracks:i},o=e.readUInt16();32768&o?(a.framesPerSecond=256-(o>>8),a.ticksPerFrame=255&o):a.ticksPerBeat=o;return a}(i.data),o=[],s=0;!e.eof()&&s>7&127;t.writeUInt8(p),t.writeUInt8(l);break;default:throw"Unrecognized event type: "+i}return u}function i(){this.buffer=[]}i.prototype.writeUInt8=function(t){this.buffer.push(255&t)},i.prototype.writeInt8=i.prototype.writeUInt8,i.prototype.writeUInt16=function(t){var e=t>>8&255,r=255&t;this.writeUInt8(e),this.writeUInt8(r)},i.prototype.writeInt16=i.prototype.writeUInt16,i.prototype.writeUInt24=function(t){var e=t>>16&255,r=t>>8&255,n=255&t;this.writeUInt8(e),this.writeUInt8(r),this.writeUInt8(n)},i.prototype.writeInt24=i.prototype.writeUInt24,i.prototype.writeUInt32=function(t){var e=t>>24&255,r=t>>16&255,n=t>>8&255,i=255&t;this.writeUInt8(e),this.writeUInt8(r),this.writeUInt8(n),this.writeUInt8(i)},i.prototype.writeInt32=i.prototype.writeUInt32,i.prototype.writeBytes=function(t){this.buffer=this.buffer.concat(Array.prototype.slice.call(t,0))},i.prototype.writeString=function(t){var e,r=t.length,n=[];for(e=0;e>=7;e;){var n=127&e|128;r.push(n),e>>=7}this.writeBytes(r.reverse())}},i.prototype.writeChunk=function(t,e){this.writeString(t),this.writeUInt32(e.length),this.writeBytes(e)},t.exports=function(t,e){if("object"!=typeof t)throw"Invalid MIDI data";e=e||{};var n,a=t.header||{},o=t.tracks||[],s=o.length,u=new i;for(function(t,e,r){var n=null==e.format?1:e.format,a=128;e.timeDivision?a=e.timeDivision:e.ticksPerFrame&&e.framesPerSecond?a=-(255&e.framesPerSecond)<<8|255&e.ticksPerFrame:e.ticksPerBeat&&(a=32767&e.ticksPerBeat);var o=new i;o.writeUInt16(n),o.writeUInt16(r),o.writeUInt16(a),t.writeChunk("MThd",o.buffer)}(u,a,s),n=0;n-1&&Array.isArray(a)?t(a,r,n):r.push(a)}return r}(t,[],e)}t.exports=function(t){if(!Array.isArray(t))throw new TypeError("Expected value to be an array");return n(t)},t.exports.from=n,t.exports.depth=function(t,e){if(!Array.isArray(t))throw new TypeError("Expected value to be an array");return i(t,e)},t.exports.fromDepth=i},function(t,e,r){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var n=r(4);e.createControlChanges=function(){return new Proxy({},{get:function(t,e){return t[e]?t[e]:n.controlChangeIds.hasOwnProperty(e)?t[n.controlChangeIds[e]]:void 0},set:function(t,e,r){return n.controlChangeIds.hasOwnProperty(e)?t[n.controlChangeIds[e]]=r:t[e]=r,!0}})}},function(t,e,r){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var n=new WeakMap,i=function(){function t(t,e){n.set(this,e),this.ticks=t.absoluteTime,this.value=t.value}return Object.defineProperty(t.prototype,"time",{get:function(){return n.get(this).ticksToSeconds(this.ticks)},set:function(t){var e=n.get(this);this.ticks=e.secondsToTicks(t)},enumerable:!0,configurable:!0}),t.prototype.toJSON=function(){return{ticks:this.ticks,time:this.time,value:this.value}},t}();e.PitchBend=i},function(t,e,r){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var n=r(13),i=new WeakMap,a=function(){function t(t,e){if(this.number=0,i.set(this,e),this.number=0,t){var r=t.find((function(t){return"programChange"===t.type}));r&&(this.number=r.programNumber)}}return Object.defineProperty(t.prototype,"name",{get:function(){return this.percussion?n.DrumKitByPatchID[this.number]:n.instrumentByPatchID[this.number]},set:function(t){var e=n.instrumentByPatchID.indexOf(t);-1!==e&&(this.number=e)},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"family",{get:function(){return this.percussion?"drums":n.InstrumentFamilyByID[Math.floor(this.number/8)]},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"percussion",{get:function(){return 9===i.get(this).channel},enumerable:!0,configurable:!0}),t.prototype.toJSON=function(){return{family:this.family,name:this.name,number:this.number}},t.prototype.fromJSON=function(t){this.number=t.number},t}();e.Instrument=a},function(t,e,r){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.instrumentByPatchID=["acoustic grand piano","bright acoustic piano","electric grand piano","honky-tonk piano","electric piano 1","electric piano 2","harpsichord","clavi","celesta","glockenspiel","music box","vibraphone","marimba","xylophone","tubular bells","dulcimer","drawbar organ","percussive organ","rock organ","church organ","reed organ","accordion","harmonica","tango accordion","acoustic guitar (nylon)","acoustic guitar (steel)","electric guitar (jazz)","electric guitar (clean)","electric guitar (muted)","overdriven guitar","distortion guitar","guitar harmonics","acoustic bass","electric bass (finger)","electric bass (pick)","fretless bass","slap bass 1","slap bass 2","synth bass 1","synth bass 2","violin","viola","cello","contrabass","tremolo strings","pizzicato strings","orchestral harp","timpani","string ensemble 1","string ensemble 2","synthstrings 1","synthstrings 2","choir aahs","voice oohs","synth voice","orchestra hit","trumpet","trombone","tuba","muted trumpet","french horn","brass section","synthbrass 1","synthbrass 2","soprano sax","alto sax","tenor sax","baritone sax","oboe","english horn","bassoon","clarinet","piccolo","flute","recorder","pan flute","blown bottle","shakuhachi","whistle","ocarina","lead 1 (square)","lead 2 (sawtooth)","lead 3 (calliope)","lead 4 (chiff)","lead 5 (charang)","lead 6 (voice)","lead 7 (fifths)","lead 8 (bass + lead)","pad 1 (new age)","pad 2 (warm)","pad 3 (polysynth)","pad 4 (choir)","pad 5 (bowed)","pad 6 (metallic)","pad 7 (halo)","pad 8 (sweep)","fx 1 (rain)","fx 2 (soundtrack)","fx 3 (crystal)","fx 4 (atmosphere)","fx 5 (brightness)","fx 6 (goblins)","fx 7 (echoes)","fx 8 (sci-fi)","sitar","banjo","shamisen","koto","kalimba","bag pipe","fiddle","shanai","tinkle bell","agogo","steel drums","woodblock","taiko drum","melodic tom","synth drum","reverse cymbal","guitar fret noise","breath noise","seashore","bird tweet","telephone ring","helicopter","applause","gunshot"],e.InstrumentFamilyByID=["piano","chromatic percussion","organ","guitar","bass","strings","ensemble","brass","reed","pipe","synth lead","synth pad","synth effects","world","percussive","sound effects"],e.DrumKitByPatchID={0:"standard kit",8:"room kit",16:"power kit",24:"electronic kit",25:"tr-808 kit",32:"jazz kit",40:"brush kit",48:"orchestra kit",56:"sound fx kit"}},function(t,e,r){"use strict";function n(t){return["C","C#","D","D#","E","F","F#","G","G#","A","A#","B"][t%12]}Object.defineProperty(e,"__esModule",{value:!0});var i,a,o=(i=/^([a-g]{1}(?:b|#|x|bb)?)(-?[0-9]+)/i,a={cbb:-2,cb:-1,c:0,"c#":1,cx:2,dbb:0,db:1,d:2,"d#":3,dx:4,ebb:2,eb:3,e:4,"e#":5,ex:6,fbb:3,fb:4,f:5,"f#":6,fx:7,gbb:5,gb:6,g:7,"g#":8,gx:9,abb:7,ab:8,a:9,"a#":10,ax:11,bbb:9,bb:10,b:11,"b#":12,bx:13},function(t){var e=i.exec(t),r=e[1],n=e[2];return a[r.toLowerCase()]+12*(parseInt(n,10)+1)}),s=new WeakMap,u=function(){function t(t,e,r){s.set(this,r),this.midi=t.midi,this.velocity=t.velocity,this.noteOffVelocity=e.velocity,this.ticks=t.ticks,this.durationTicks=e.ticks-t.ticks}return Object.defineProperty(t.prototype,"name",{get:function(){return t=this.midi,e=Math.floor(t/12)-1,n(t)+e.toString();var t,e},set:function(t){this.midi=o(t)},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"octave",{get:function(){return Math.floor(this.midi/12)-1},set:function(t){var e=t-this.octave;this.midi+=12*e},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"pitch",{get:function(){return n(this.midi)},set:function(t){this.midi=12*(this.octave+1)+["C","C#","D","D#","E","F","F#","G","G#","A","A#","B"].indexOf(t)},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"duration",{get:function(){var t=s.get(this);return t.ticksToSeconds(this.ticks+this.durationTicks)-t.ticksToSeconds(this.ticks)},set:function(t){var e=s.get(this).secondsToTicks(this.time+t);this.durationTicks=e-this.ticks},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"time",{get:function(){return s.get(this).ticksToSeconds(this.ticks)},set:function(t){var e=s.get(this);this.ticks=e.secondsToTicks(t)},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"bars",{get:function(){return s.get(this).ticksToMeasures(this.ticks)},enumerable:!0,configurable:!0}),t.prototype.toJSON=function(){return{duration:this.duration,durationTicks:this.durationTicks,midi:this.midi,name:this.name,ticks:this.ticks,time:this.time,velocity:this.velocity}},t}();e.Note=u}])})); 2 | //# sourceMappingURL=Midi.js.map -------------------------------------------------------------------------------- /scripts/modules/wavefile.js: -------------------------------------------------------------------------------- 1 | try{if(!Uint8Array.prototype.slice)Object.defineProperty(Uint8Array.prototype,"slice",{value:function(begin,end){return new Uint8Array(Array.prototype.slice.call(this,begin,end))}})}catch(err){}var ka="function"==typeof Object.create?Object.create:function(n){function m(){}m.prototype=n;return new m},D; 2 | if("function"==typeof Object.setPrototypeOf)D=Object.setPrototypeOf;else{var K;a:{var la={D:!0},N={};try{N.__proto__=la;K=N.D;break a}catch(n){}K=!1}D=K?function(n,m){n.__proto__=m;if(n.__proto__!==m)throw new TypeError(n+" is not extensible");return n}:null}var O=D; 3 | function P(n,m){n.prototype=ka(m.prototype);n.prototype.constructor=n;if(O)O(n,m);else for(var l in m)if("prototype"!=l)if(Object.defineProperties){var p=Object.getOwnPropertyDescriptor(m,l);p&&Object.defineProperty(n,l,p)}else n[l]=m[l]}var ma="function"==typeof Object.defineProperties?Object.defineProperty:function(n,m,l){n!=Array.prototype&&n!=Object.prototype&&(n[m]=l.value)},na="undefined"!=typeof window&&window===this?this:"undefined"!=typeof global&&null!=global?global:this; 4 | function Q(n,m){if(m){for(var l=na,p=n.split("."),y=0;yv&&(v=Math.max(v+y,0));vp||56319m||57343c;c++)b["ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".charCodeAt(c)]=c;c=.75*a.length;"="===a[a.length-1]&&(c--,"="===a[a.length-2]&&c--);c=new Uint8Array(c);for(var d= 14 | 0,e=0;d>4;c[e++]=(g&15)<<4|k>>2;c[e++]=(k&3)<<6|r&63}return c}function sa(a,b){return a=0parseInt(a,10)||"53">4;z.push(ba(aa<<4^Z,r));z.push(ba(aa,r))}e=z;d.set(e,f);f+=e.length;e=[]}e.push(a[g])}return d}function xa(a,b){var c=a[0];S(c,b);var d=[];d.push(c&255);d.push(c>>8&255);d.push(b.index);d.push(0);c=3;for(var e=a.length;c>3;c>e&&(d|=4,c-=e,f+=e);e>>=1;c>e&&(d|=2,c-=e,f+=e);e>>=1;c>e&&(d|=1,f+=e);c=d;b.i=c&8?b.i-f:b.i+f;-32768>b.i?b.i=-32768:32767b.index?b.index=0:88>1);a&1&&(c+=b.step>>2);c+=b.step>>3;a&8&&(c=-c);b.i+=c;32767b.i&&(b.i=-32767);b.index+=ca[a];0>b.index?b.index=0:88>8&128;g||(f*=-1);32635>8&127];f=k<<4|f>>k+3&15}else f>>=4;b[e]=f^g^85}return b}function Ca(a){for(var b=new Int16Array(a.length),c=0,d=a.length;c>4)+4;f=4!=k?1<>8&128;0!=g&&(f=-f);f+=132;32635>7&255];b[e]=~(g|k<<4|f>>k+3&15)}return b}function Fa(a){for(var b=new Int16Array(a.length),c=0,d=a.length;c>4&7;g=Ga[g]+((f&15)<f)b[c]=f,c++;else{var g=0,k=0;2047>=f?(g=1,k=192):65535>=f?(g=2,k=224):1114111>=f&&(g=3,k=240,d++);b[c]=(f>>6*g)+k;for(c++;0>6*(g-1)&63,c++,g--}d++}return c}function U(a){var b=Math.floor(a);a-=b;return.5>a?b:.5=k)b+=String.fromCharCode(k);else{var r= 21 | 0;194<=k&&223>=k?r=1:224<=k&&239>=k?(r=2,224===a[d]&&(e=160),237===a[d]&&(f=159)):240<=k&&244>=k?(r=3,240===a[d]&&(e=144),244===a[d]&&(f=143)):g=!0;k&=(1<<8-r-1)-1;for(var z=0;zf)g=!0;k=k<<6|a[d]&63;d++}g?b+=String.fromCharCode(65533):65535>=k?b+=String.fromCharCode(k):(k-=65536,b+=String.fromCharCode((k>>10&1023)+55296,(k&1023)+56320))}}return b}function x(a){var b=[];T(a,b);return b}function L(a,b,c,d){d=void 0===d?0:d;b=b||{};for(var e=ea(b.h,b.R,b.O),f=Math.ceil(b.h/8), 22 | g=0,k=d,r=a.length;gb){b=new g(d.LPForder||ia[d.LPFType],c,b/2);c=0;for(d=e.length;cthis.max?this.max:athis.max&&(a-=2*this.max+2);return a};H.prototype.ga=function(a,b,c){Math.abs(b)>this.f-2*this.g&&(b=0>b?-Infinity:Infinity);var d=0>((b=+b)||1/b)?1:0>b?1:0;b=Math.abs(b);var e=Math.min(Math.floor(Math.log(b)/Math.LN2),1023),f=U(b/Math.pow(2,e)*Math.pow(2,this.c));b!== 29 | b?(f=Math.pow(2,this.c-1),e=(1<=Math.pow(2,1-this.a)?(2<=f/Math.pow(2,this.c)&&(e+=1,f=1),e>this.a?(e=(1<=b;)a[f]=parseInt(e.substring(0,8),2),e=e.substring(8),f--,d++;return d};B.prototype.va=function(a){this.c=0;this.container=this.u(a,4);if(-1===this.Z.indexOf(this.container))throw Error("Not a supported format."); 31 | this.a.o="RIFX"===this.container;this.chunkSize=this.b(a);this.format=this.u(a,4);this.Y={chunkId:this.container,chunkSize:this.chunkSize,format:this.format,subChunks:this.V(a)}};B.prototype.s=function(a,b){b=void 0===b?!1:b;for(var c=this.Y.subChunks,d=[],e=0;ethis.data.samples.length)throw Error("Range error");return M(this.data.samples.slice(a,a+this.f.h/8),this.f)};t.prototype.setSample=function(a,b){a*=this.f.h/ 57 | 8;if(a+this.f.h/8>this.data.samples.length)throw Error("Range error");L([b],this.f,this.data.samples,void 0===a?0:a)};t.prototype.getiXML=function(){return this.iXML.value};t.prototype.setiXML=function(a){if("string"!==typeof a)throw new TypeError("iXML value must be a string.");this.iXML.value=a;this.iXML.chunkId="iXML"};t.prototype.get_PMX=function(){return this._PMX.value};t.prototype.set_PMX=function(a){if("string"!==typeof a)throw new TypeError("_PMX value must be a string.");this._PMX.value= 58 | a;this._PMX.chunkId="_PMX"};t.prototype.W=function(a,b,c,d,e){e.container||(e.container="RIFF");this.container=e.container;this.bitDepth=c;var f=[];if(0parseInt(this.bitDepth,10)))throw Error("Invalid bit depth.");};t.prototype.$=function(){this.f={h:(parseInt(this.bitDepth,10)-1|7)+1,R:"32f"==this.bitDepth||"64"==this.bitDepth,O:"8"!=this.bitDepth,o:"RIFX"==this.container};-1<["4","8a","8m"].indexOf(this.bitDepth)&&(this.f.h=8,this.f.O=!1)};t.prototype.aa=function(){this.wa(); 63 | var a=this.fmt.numChannels;if(1>a||65535a||4294967295c.length)for(var d=0,e=4-c.length;db.dwSampleOffset&&!c?(this.B(b,d+1),this.B(a[d],d+2),c=!0):this.B(a[d],c?d+2:d+1);c||this.B(b,this.cue.points.length+1)};w.prototype.I=function(){for(var a=0,b=this.LIST.length;aa||4294967295>2],b+="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[(a[c]&3)<<4|a[c+1]>>4],b+="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[(a[c+1]&15)<<2|a[c+2]>>6],b+="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[a[c+2]&63];2===a.length%3?b=b.substring(0,b.length-1)+"=":1===a.length%3&&(b=b.substring(0,b.length-2)+"==");return b};m.prototype.toDataURI= 84 | function(){return"data:audio/wav;base64,"+this.toBase64()};m.prototype.fromDataURI=function(a){this.fromBase64(a.replace("data:audio/wav;base64,",""))};n.WaveFile=m;Object.defineProperty(n,"__esModule",{value:!0})}"object"===typeof exports&&"undefined"!==typeof module?W(exports):"function"===typeof define&&define.amd?define(["exports"],W):(V=V||self,W(V.wavefile={})); 85 | --------------------------------------------------------------------------------