├── .DS_Store
├── .cursorignore.txt
├── .gitattributes
├── .github
└── FUNDING.yml
├── .gitignore
├── .vscode
└── launch.json
├── DEVELOPMENT.md
├── LICENSE
├── README.md
├── bin
├── favicon.ico
├── favicon.png
├── img
│ ├── bg.png
│ ├── delete.png
│ ├── locked.png
│ ├── logo_bfxr.png
│ ├── logo_footsteppr.png
│ ├── logo_transfxr.png
│ └── unlocked.png
└── index.html
├── compile.js
├── css
├── index.css
├── slider.css
└── third_party
│ ├── bootstrap-slider.css
│ └── bootstrap-tooltip.css
├── favicon.ico
├── favicon.png
├── gzipper
├── img
├── bg.png
├── delete.png
├── locked.png
├── logo_bfxr.png
├── logo_footsteppr.png
├── logo_transfxr.png
└── unlocked.png
├── index.html
├── insert_templates.js
├── js
├── .DS_Store
├── SaveLoad.js
├── Tab.js
├── audio
│ ├── AKWF.js
│ ├── Bfxr_DSP.js
│ ├── RealizedSound.js
│ ├── audio_globals.js
│ ├── puredata.js
│ ├── puredata_modules.js
│ ├── puredata_parser.js
│ └── riffwave.js
├── globals.js
├── index.js
├── synths
│ ├── Bfxr.js
│ ├── Footsteppr.js
│ ├── SynthBase.js
│ ├── Transfxr.js
│ └── templates.js
└── third_party
│ ├── bootstrap-slider.js
│ ├── input-knobs.js
│ ├── jszip.js
│ └── jszip.min.js
├── package-lock.json
├── package.json
├── template_links.txt
├── templates
└── Bfxr
│ └── pickup_coin.bcol
└── upload
/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/increpare/bfxr2/9e38f296ec80b8010a87443a682cfcbf6c6df06f/.DS_Store
--------------------------------------------------------------------------------
/.cursorignore.txt:
--------------------------------------------------------------------------------
1 | bin/
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: [increpare]
4 | patreon: increpare
5 | custom: ['paypal.me/increparegames']
6 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules
3 | *.gz
4 | /node_modules
5 | /node_modules
6 |
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // Use IntelliSense to learn about possible attributes.
3 | // Hover to view descriptions of existing attributes.
4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5 | "version": "0.2.0",
6 | "configurations": [
7 | {
8 | "type": "node",
9 | "name": "Run Current File",
10 | "request": "launch",
11 | "program": "c:\\Users\\Anwender\\Documents\\GitHub\\bfxr2\\insert_prefabs.js"
12 | }
13 | ]
14 | }
--------------------------------------------------------------------------------
/DEVELOPMENT.md:
--------------------------------------------------------------------------------
1 | ## How to run
2 |
3 | Uh, it should work just fine. There's an optional compilation step if you want to make everything tiny, but as a default just hosting a local http server and loading index.html should work...
4 |
5 | ## How to compile
6 |
7 | ```node compile.js```, then everything should be in the bin directory.
8 |
9 | ## How to add new sound templates.
10 |
11 | So, a template sound effect ('jump', say) is specified in Bfxr (any of the synths) as a .bcol file. This is then stored in "./templates/[Synth Name]/[Template_Name].bcol" to be referenced in the templates list of the synthesizer you are using.
12 |
13 | Sound names in a template look like "varietyname_suffix".
14 |
15 | 
16 |
17 | The sounds are grouped together into varieties, with the idea being that their range of values is the range of possible values of that variety.
18 |
19 | 
20 |
21 | (Duplicate values are combined already at this stage.)
22 |
23 | So in the end we have that a template is a group of 'varieties'. In Bfxr when you hit the generate button, Bfxr picks a variety at at random, then generates a sound with paramaters within the ranges it finds in the exemplar sounds.
24 |
25 | (The only reason you'd ever _need_ more than 2 example sounds for a given variety is to allow for more than two BUTTONSELECT values (wave shapes, terrains or what have yous)).
26 |
27 | So ok once you've save the .bcol files in the folder, you need to run ```node insert_templates.js" to generate ```js/synths/tempaltes.js```. It's a bit annoying, but the whole point is being able to easily load/save/tweak .bcol files, which IMO makes the annoyance worthwhile.
28 |
29 | If you wish to weight a variety so it appears more often than others, just put a number at the start (multiple digits allowed, e.g. "22coin" will appear ten times more often than "2coin").
30 |
31 | (There are some hard-coded tempaltes, i.e. Randomize and Mutate - but any ones defined in ./templates will overwrite existing ones - e.g. pickup_coin.bcol will override any existing generate_pickup_coin method in Bfxr.js)
32 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 Stephen Lavelle
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 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## About
2 | [Bfxr2](https://www.bfxr.net/) a tool for making sound effects for games, a rewrite/refresh of the flash tool Bfxr in javascript.
3 |
4 | 
5 |
6 | It's currently BETA, and new things should be coming to it, but the main addition right now is Obiwannabe's subtly wonderful footstep generator, ported from puredata to javsacript:
7 |
8 | 
9 |
10 | ## Development
11 | cf. [DEVELOPMENT.md](https://github.com/increpare/bfxr2/blob/master/DEVELOPMENT.md).
12 |
13 | ## Archaeology
14 |
15 | I don't know if it's genetically related, but I believe that _why made a program called sound foley (which I haven't been able to get to work) which looks quite similar to Sfxr in design based on what I've seen of his _why's presentation of it.
16 | The darling DrPetter made the program this is based on, Sfxr:
17 |
18 | * http://drpetter.se/
19 | * http://drpetter.se/article_sound.html
20 | * http://drpetter.se/project_sfxr.html
21 | The fabulous Tom Vian did a flash port of this, called as3sfxr
22 | * http://www.tomvian.com/
23 | * http://www.superflashbros.net/as3sfxr/
24 | There's also a port of Sfxr to OS X that's quite loved by people (and was a little influential) called cfxr:
25 | * http://thirdcog.eu/apps/cfxr
26 | I did a mod of as3sfxr that introduced some new features, and called it as3sfxr-b:
27 | * http://ded.increpare.com/~locus/as3sfxr-b/
28 | After asking for feedback, I spent some time adding and changing more things, making something new. Which is Bfxr. Which is what you see on this page.
29 |
30 | Code from
31 | Bulk of coding of this version done by poor little me. In addition to code from Tom/DrPetter, code snippits were taken from
32 |
33 | * http://www.firstpr.com.au/dsp/pink-noise/#Filtering for pink-noise related synthesis
34 |
35 | Thanks + Acknolwedgements
36 | * DrPetter, for Sfxr.
37 | * Tom Vian, for his elegantly constructed as3sfxr port.
38 | * @docky, @Draknek, @KommanderKlobb, @eigenbo, @GrimFang4, @nyarla, @bfod, @jasperbyrne, @draknek, @hybridmind, @RinkuHero, DustinGunn, mcc, and agj for feedback + suggestions. And Derek for some early encouragement.
39 | The people from the #flex irc room on freenode for technical help in my hour of need, especially J_A_X.
40 |
41 | Other software that can be software for making sounds:
42 | * Audacity - http://audacity.sourceforge.net/
43 | * HighC - http://highc.org/
44 | * PXTone - http://buzinkai.net/PXTone/tutorial/
45 | * Sound Effects Generator - http://www.windowsgames.co.uk/effects.html (windows only. I nicked a couple of things from this. )
46 | * Freesound - http://www.freesound.org - Okay, not sound software, but a really amazing resource.
47 | * ChipTone - https://sfbgames.itch.io/chiptone
48 |
--------------------------------------------------------------------------------
/bin/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/increpare/bfxr2/9e38f296ec80b8010a87443a682cfcbf6c6df06f/bin/favicon.ico
--------------------------------------------------------------------------------
/bin/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/increpare/bfxr2/9e38f296ec80b8010a87443a682cfcbf6c6df06f/bin/favicon.png
--------------------------------------------------------------------------------
/bin/img/bg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/increpare/bfxr2/9e38f296ec80b8010a87443a682cfcbf6c6df06f/bin/img/bg.png
--------------------------------------------------------------------------------
/bin/img/delete.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/increpare/bfxr2/9e38f296ec80b8010a87443a682cfcbf6c6df06f/bin/img/delete.png
--------------------------------------------------------------------------------
/bin/img/locked.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/increpare/bfxr2/9e38f296ec80b8010a87443a682cfcbf6c6df06f/bin/img/locked.png
--------------------------------------------------------------------------------
/bin/img/logo_bfxr.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/increpare/bfxr2/9e38f296ec80b8010a87443a682cfcbf6c6df06f/bin/img/logo_bfxr.png
--------------------------------------------------------------------------------
/bin/img/logo_footsteppr.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/increpare/bfxr2/9e38f296ec80b8010a87443a682cfcbf6c6df06f/bin/img/logo_footsteppr.png
--------------------------------------------------------------------------------
/bin/img/logo_transfxr.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/increpare/bfxr2/9e38f296ec80b8010a87443a682cfcbf6c6df06f/bin/img/logo_transfxr.png
--------------------------------------------------------------------------------
/bin/img/unlocked.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/increpare/bfxr2/9e38f296ec80b8010a87443a682cfcbf6c6df06f/bin/img/unlocked.png
--------------------------------------------------------------------------------
/compile.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs');
2 | var UglifyJS = require("uglify-js");
3 | var CleanCSS = require('clean-css');
4 | const { exec, execSync } = require('child_process');
5 |
6 | //bring the templates up to date
7 | var output = execSync("node insert_templates.js");
8 | //print stdout of output
9 | console.log(output.toString());
10 |
11 | //load index.html
12 | var index = fs.readFileSync('index.html', 'utf8');
13 | console.log("processing index.html...");
14 |
15 | //all the scripts are contained between and
16 | var script_start_tag = '';
17 | var script_end_tag = '';
18 | const script_start = index.indexOf(script_start_tag);
19 | const script_end = index.indexOf(script_end_tag);
20 | const scripts = index.substring(script_start, script_end+script_end_tag.length);
21 |
22 | //all the css is contained between and
23 | var css_start_tag = '';
24 | var css_end_tag = '';
25 | const css_start = index.indexOf(css_start_tag);
26 | const css_end = index.indexOf(css_end_tag);
27 | var css = index.substring(css_start, css_end+css_end_tag.length);
28 |
29 | //extract those sections
30 | const css_includes = css.split('\n');
31 | const script_includes = scripts.split('\n');
32 |
33 | // console.log(css_includes);
34 | // console.log(script_includes);
35 |
36 |
37 | //construct css file list
38 | var css_files = [];
39 | for (var i = 0; i < css_includes.length; i++) {
40 | const line = css_includes[i];
41 | var match = line.match(/';
115 | var css_include_line = '';
116 |
117 | //replace the old includes with the new ones
118 | index = index.replace( scripts, js_include_line);
119 | index = index.replace( css, css_include_line);
120 |
121 | //write the new index.html
122 | fs.writeFileSync('bin/index.html', index);
123 |
124 | console.log("crushing images...");
125 | fs.mkdirSync('bin/img');
126 | fs.readdirSync('img').forEach(file => {
127 | // First copy the file to bin/img
128 | fs.copyFileSync(`img/${file}`, `bin/img/${file}`);
129 | var command = `pngcrush -rem allb -brute -reduce -ow ./bin/img/${file}`;
130 | exec(command);
131 | });
132 |
133 | //run ./gzipper perl script
134 | exec("perl gzipper");
135 |
136 | //move favicon.icon and favicon.png to bin
137 | fs.copyFileSync('favicon.ico', 'bin/favicon.ico');
138 | fs.copyFileSync('favicon.png', 'bin/favicon.png');
139 |
140 |
--------------------------------------------------------------------------------
/css/index.css:
--------------------------------------------------------------------------------
1 | /*the structure of the page is HEADER, CONTENT, FOOTER */
2 |
3 | :root {
4 | --page-background-color: #ccbda1;
5 | --panel-background-color: #ccbda1;
6 | --panel-border-color: #5c574c;
7 | --panel-text-color: #black;
8 | --panel-disabled-text-color: #675f51;
9 | --tab-background-color-unselected: #887c66;
10 | --tab-unselected-border-color: #3c3831;
11 | --tab-background-color-selected: var(--panel-background-color);
12 | --tab-selected-border-color: var(--panel-border-color);
13 | --panel-border-thickness: 1px;
14 | --ui-margin: 5px;
15 | --font-size: 16px;
16 | --font-size-small: 12px;
17 | --font-size-bigger: 24px;
18 | --file-item-selected-background-color:#a8c6ee;
19 | --tick-width: 2px;
20 |
21 |
22 | --slider-border-color: var(--tab-selected-border-color);
23 | --slider-tick-color: #0000002c;
24 | --slider-tick-defaultval-color: #5c574c;
25 | --slider-background-color: transparent;
26 | --col-sliderhandle: black;
27 |
28 | --alternating-table-row-color: #e7d1a7;
29 | --button-background-color: var(--panel-background-color);
30 | --button-pressed-background-color: #e7d1a7;
31 |
32 | --tween-canvas-width: 75;
33 | --tween-canvas-height: 32;
34 |
35 | --buttongrid-pressed-background-color: grey;
36 |
37 | --about-dialog-width: 300px;
38 | --about-dialog-backdrop-color: #ccbda188;
39 | }
40 | /* kludge - cf https://stackoverflow.com/questions/7492062/css-overflow-scroll-always-show-vertical-scroll-bar */
41 | /* ::-webkit-scrollbar {
42 | -webkit-appearance: none;
43 | width: 7px;
44 | }
45 |
46 | ::-webkit-scrollbar-thumb {
47 | border-radius: 4px;
48 | background-color: rgba(0, 0, 0, .5);
49 | box-shadow: 0 0 1px rgba(255, 255, 255, .5);
50 | } */
51 | /* end kludge */
52 |
53 | body {
54 | user-select: none;
55 | width:100%;
56 | height:100%;
57 | margin:0;
58 | padding:0;
59 | background-color: var(--page-background-color);
60 | background-image: url("../img/bg.png") ;
61 | font-size: var(--font-size);
62 | /* min-height: 1000px; */
63 | overflow-y:auto;
64 | /*vertically center content*/
65 | display: flex;
66 | flex-direction: column;
67 | justify-content: center;
68 | }
69 |
70 | #main_container {
71 | width:fit-content;
72 | background-color: transparent;
73 |
74 | display: flex;
75 | flex-direction: column;
76 |
77 |
78 | /*horizontally center content*/
79 | margin: 0 auto;
80 |
81 | }
82 |
83 | #tab_bar {
84 | padding-left: 10px;
85 | height: 50px;
86 | width: 100%;
87 | background-color: transparent;
88 | display: flex;
89 | flex-direction: row;
90 |
91 | }
92 |
93 | .tab_button {
94 | /* width: 100px; */
95 | margin-top: 15px;
96 | padding-left: 10px;
97 | padding-right: 10px;
98 | font-size: var(--font-size-bigger);
99 |
100 | background-color: var(--tab-background-color-unselected);
101 |
102 | border-style:solid;
103 | border-color: var(--tab-unselected-border-color);
104 | border-width: var(--panel-border-thickness);
105 | border-bottom:none;
106 |
107 | margin-left: 5px;
108 | margin-right: 5px;
109 |
110 | padding-top: 3px;
111 | cursor: pointer;
112 |
113 | }
114 | .tab_button:first-child {
115 | margin-left: 0;
116 | }
117 | .tab_button:last-child {
118 | margin-right: 0;
119 | }
120 |
121 | .tab_button.active_tab {
122 | margin-top: 10px;
123 |
124 | background-color: var(--tab-background-color-selected);
125 |
126 | border-style:solid;
127 | border-color: var(--tab-selected-border-color);
128 | border-width: var(--panel-border-thickness);
129 | border-bottom:none;
130 |
131 | /*to remove the border between the active tab
132 | and the main panel, we extend it down a bit: */
133 | margin-bottom: calc(-1*var(--panel-border-thickness));
134 | /* margin-left: calc(-1*var(--panel-border-thickness)); */
135 | /* margin-right: calc(-1*var(--panel-border-thickness)); */
136 | /*expand the width to overlap the next/previous tabs a bit*/
137 | z-index: 1;
138 |
139 | padding-top: 6px;
140 |
141 | cursor: default;
142 | }
143 |
144 |
145 | .tab_page {
146 | height: auto;
147 | /* max-height: 1200px; */
148 | width: 100%;
149 | background-color: var(--panel-background-color);
150 | border-style:solid;
151 | border-color: var(--panel-border-color);
152 | border-width: var(--panel-border-thickness);
153 | display: none;
154 | flex-direction: row;
155 | /*big thick drop-shadow*/
156 | filter: drop-shadow(10px 10px 20px rgba(0, 0, 0, 0.5));
157 |
158 |
159 | height: 606px;
160 | overflow-y: clip;
161 | overflow-x: visible;
162 | }
163 |
164 | .tab_page.active_tab_page {
165 | display: flex;
166 | }
167 |
168 | #footer {
169 | height: fit-content;
170 | width: fit-content;
171 | padding-top:5px;
172 | text-align: center;
173 | /*align center*/
174 | margin: 0 auto;
175 | background-color: var(--alternating-table-row-color);
176 | border-radius: 5px;
177 | padding: 5px;
178 | padding-left: 10px;
179 | padding-right: 10px;
180 | margin-top:5px;
181 | box-shadow: 0 0 3px 0 var(--alternating-table-row-color);
182 | }
183 |
184 | .left_panel {
185 | /* height: 100%; */
186 | width: 150px;
187 | display: flex;
188 | flex-direction: column;
189 | gap: var(--ui-margin);
190 | }
191 |
192 | .template_list {
193 | display: flex;
194 | flex-direction: column;
195 | gap: var(--ui-margin);
196 | margin: var(--ui-margin);
197 | }
198 |
199 | .right_panel_button_list{
200 | display: flex;
201 | flex-direction: column;
202 | gap: var(--ui-margin);
203 | margin: var(--ui-margin);
204 | margin-left: 0;
205 |
206 | }
207 |
208 | .save_commands {
209 | display: flex;
210 | flex-direction: column;
211 | gap: var(--ui-margin);
212 | margin: var(--ui-margin);
213 | }
214 |
215 | .centre_panel {
216 | width: 350px;
217 | max-height: 600px;
218 |
219 | margin-top: var(--ui-margin);
220 | margin-bottom: 0;
221 | margin-left: 0;
222 | margin-right: var(--ui-margin);
223 |
224 | background-color: transparent;
225 | border: var(--panel-border-thickness) solid var(--panel-border-color);
226 | border-bottom:none;
227 |
228 | display: flex;
229 | flex-direction: column;
230 | }
231 |
232 | .centre_header {
233 | border-bottom: var(--panel-border-thickness) solid var(--panel-border-color);
234 | margin-top: var(--ui-margin);
235 | padding-bottom: var(--ui-margin);
236 | padding-right: var(--ui-margin);
237 | }
238 |
239 | .right_panel {
240 | width: 120px;
241 | background-color: transparent;
242 | }
243 |
244 | .scroll_container {
245 | /* overflow-x:scroll;
246 | overflow-y:scroll; */
247 | overflow-x: hidden;
248 | overflow-y:auto;
249 | }
250 |
251 | .filelist {
252 | position:relative;
253 | padding-top: var(--ui-margin);
254 | /* margin-bottom: var(--ui-margin); */
255 | /* padding: var(--ui-margin); */
256 | display: flex;
257 | flex-direction: column;
258 | /* gap: var(--ui-margin); */
259 | height:100%;
260 |
261 | border-style:solid;
262 | border-width:0;
263 | border-color: var(--panel-border-color);
264 | border-top-width: var(--panel-border-thickness);
265 |
266 | /* min-height: 100px;
267 | max-height: 200px; */
268 | scrollbar-gutter: stable;
269 | padding-right:13px;
270 | }
271 |
272 | .file_item {
273 | display: flex;
274 | padding:0;
275 | padding-right: var(--ui-margin);
276 |
277 | /* height: 25px; */
278 | /*align the text vertically*/
279 | /* padding-top: 5px; */
280 | }
281 |
282 | /*unselected: */
283 | .file_item.modified {
284 | color: var(--panel-disabled-text-color);
285 | }
286 |
287 | .file_item.file_selected {
288 | background-color: var(--file-item-selected-background-color);
289 | }
290 |
291 |
292 | .file_item_name {
293 | padding-left: 3px;
294 | display:inline-block;
295 | text-overflow: ellipsis;
296 | overflow: hidden;
297 | white-space: nowrap;
298 | width: calc(100% - 18px);
299 | height:100%;
300 | }
301 |
302 | .file_item_name.modified_filename {
303 | color:rgb(78, 78, 78);
304 | font-style: italic;
305 | }
306 |
307 | .file_item_name.modified_filename::after {
308 | content: "*";
309 | }
310 |
311 | .delete_button {
312 | display:flex;
313 | /* float:right; */
314 | font-size: var(--font-size);
315 | padding:0;
316 | margin:0;
317 |
318 | width:15px;
319 | height:15px;
320 |
321 | text-align: center;
322 | }
323 |
324 | .delete_button img {
325 | width:10px;
326 | height:10px;
327 | }
328 | table.paramtable {
329 | width:100%;
330 | border-collapse: collapse;
331 | }
332 |
333 | .centre_params .paramtable tr {
334 | border-bottom: var(--panel-border-thickness) solid var(--panel-border-color);
335 | }
336 |
337 | /* alternating row colors - cludged to deal with the fact that there are two tables...*/
338 | .paramtable tr:nth-child(even) {
339 | background-color: var(--alternating-table-row-color);
340 | }
341 |
342 | .centre_params .paramtable tr:nth-child(even) {
343 | background-color: var(--panel-background-color);
344 | }
345 | .centre_params .paramtable tr:nth-child(odd) {
346 | background-color: var(--alternating-table-row-color);
347 | }
348 | /* cludge end */
349 |
350 | .padded_item {
351 | padding-left: var(--ui-margin);
352 | padding-right: var(--ui-margin);
353 | white-space: nowrap;
354 | }
355 |
356 | /* 3 column grid */
357 | .button_grid_3c {
358 | width:100%;
359 | display: grid;
360 | grid-template-columns: repeat(4, 1fr);
361 | gap: var(--ui-margin);
362 |
363 | }
364 |
365 | /* 4 column grid */
366 | .button_grid_4c {
367 | width:100%;
368 | display: grid;
369 | grid-template-columns: repeat(4, 1fr);
370 | gap: var(--ui-margin);
371 | }
372 |
373 | /* 5 column grid */
374 | .button_grid_5c {
375 | width:100%;
376 | display: grid;
377 | grid-template-columns: repeat(5, 1fr);
378 | gap: var(--ui-margin);
379 | }
380 |
381 | .button_grid_button.selected {
382 | /* background-color: var(--buttongrid-pressed-background-color); */
383 | /* set disabled */
384 | /* opacity: 1.0; */
385 | /* color: var(--panel-text-color); */
386 | /* border-color: transparent; */
387 | /* border-radius:2px; */
388 | }
389 | .lock_icon.unlocked {
390 | opacity: 0.4;
391 | }
392 |
393 |
394 | .slider_container {
395 | position: relative;
396 | width: var(--sliderwidth);
397 | padding-right: var(--ui-margin);
398 | padding-left: var(--ui-margin);
399 | }
400 |
401 | .transition_container {
402 | position: relative;
403 | width: var(--sliderwidth);
404 | padding-right: var(--ui-margin);
405 | padding-left: var(--ui-margin);
406 | }
407 |
408 | .parameter_slider {
409 | width: 100%;
410 | margin: 0;
411 | position: relative;
412 | z-index: 2; /* Place above ticks for interaction */
413 | }
414 |
415 | .slider_ticks {
416 | position: absolute;
417 | top: 20px; /* Position below the slider */
418 | left: 0;
419 | right: 0;
420 | display: flex;
421 | justify-content: space-between; /* Even spacing */
422 | padding: 0 10px; /* Adjust based on browser's track start/end */
423 | }
424 |
425 | .slider_tick {
426 | width: var(--tick-width);
427 | height: 10px;
428 | background-color: var(--panel-border-color);
429 | }
430 |
431 |
432 | .tooltip{
433 | position: absolute;
434 | }
435 |
436 | td.lockcolumn {
437 | width: 15px;
438 | }
439 |
440 | td.labelcolumn {
441 | width:max-content;
442 |
443 | }
444 |
445 | .centre_params {
446 | position:relative;
447 | height:100%;
448 | width:100%;
449 | }
450 |
451 |
452 |
453 | .parameter_name {
454 | float: right;
455 | font-size: var(--font-size-small);
456 | width: 100%;
457 | text-align: right;
458 | cursor: default;
459 | user-select: none;
460 | }
461 |
462 | .display_canmvas {
463 | }
464 | .display_canvas_container {
465 | margin-top: var(--ui-margin);
466 | margin-right: var(--ui-margin);
467 | margin-bottom: var(--ui-margin);
468 | border: var(--panel-border-thickness) solid var(--panel-border-color);
469 | background-size: cover;
470 | /* background-position: center; */
471 | background-repeat: no-repeat;
472 | image-rendering: pixelated;
473 | }
474 | /*
475 | button {
476 | background-color: var(--button-background-color);
477 | border: var(--panel-border-thickness) solid var(--panel-border-color);
478 | border-radius: var(--ui-margin);
479 | }
480 |
481 | button:active {
482 | background-color: var(--button-pressed-background-color);
483 | }
484 | */
485 |
486 | .lockcolumn {
487 | /*not selectable*/
488 | user-select: none;
489 | }
490 |
491 | .normie_checkbox {
492 | margin:0;
493 | margin-right: var(--ui-margin);
494 | }
495 |
496 | .padleft {
497 | padding-left: var(--ui-margin);
498 | }
499 |
500 | .lockimage {
501 | width: 15px;
502 | height: 15px;
503 | margin:0;
504 | padding:0;
505 | margin-left: var(--ui-margin);
506 | background-image: url("../img/locked.png");
507 | background-repeat: no-repeat;
508 | background-position: center;
509 | }
510 |
511 | .lockimage.unlocked {
512 | background-image: url("../img/unlocked.png");
513 | opacity: 0.4;
514 | }
515 |
516 |
517 |
518 | input[type=range].input-knob:focus,input[type=range].input-slider:focus{
519 | outline:none;
520 | box-shadow:none;
521 | }
522 |
523 | .tween_select_canvas {
524 | background-color: transparent;
525 | display: inline-block;
526 | width: var(--tween-canvas-width);
527 | height: var(--tween-canvas-height);
528 | border: var(--panel-border-thickness) solid var(--panel-border-color);
529 | margin-top: var(--ui-margin);
530 | /*stretch the image to fit the width*/
531 | object-fit: contain;
532 | object-position: center;
533 | }
534 |
535 |
536 |
537 |
538 |
539 | /* Dropdown Button */
540 | .dropbtn {
541 | background-color: #04AA6D;
542 | color: white;
543 | padding: 16px;
544 | font-size: 16px;
545 | border: none;
546 | cursor: pointer;
547 | }
548 |
549 | /* Dropdown button on hover & focus */
550 | .dropbtn:hover, .dropbtn:focus {
551 | background-color: #3e8e41;
552 | }
553 |
554 | /* The search field */
555 | #myInput {
556 | box-sizing: border-box;
557 | background-image: url('searchicon.png');
558 | background-position: 14px 12px;
559 | background-repeat: no-repeat;
560 | font-size: 16px;
561 | padding: 14px 20px 12px 45px;
562 | border: none;
563 | border-bottom: 1px solid #ddd;
564 | }
565 |
566 | /* The search field when it gets focus/clicked on */
567 | #myInput:focus {outline: 3px solid #ddd;}
568 |
569 | /* The container
- needed to position the dropdown content */
570 | .dropdown {
571 | position: relative;
572 | display: inline-block;
573 | }
574 |
575 | /* Dropdown Content (Hidden by Default) */
576 | .dropdown-content {
577 | padding: 0;
578 | display: none;
579 | position: absolute;
580 | background-color: var(--panel-background-color) ;
581 | /* min-width: 230px; */
582 | /* border: var(--panel-border-thickness) solid var(--panel-border-color); */
583 | z-index: 1;
584 | /*no gaps between the images*/
585 | flex-direction: row;
586 | gap: 0;
587 | }
588 |
589 | .dropdown-content .tween_select_canvas {
590 | padding:0;
591 | margin:0;
592 | }
593 |
594 | .dropdown-content img:hover {
595 | background-color: var(--button-pressed-background-color);
596 | }
597 | /* Links inside the dropdown */
598 | .dropdown-content a {
599 | color: black;
600 | padding: 12px 16px;
601 | text-decoration: none;
602 | display: block;
603 | }
604 |
605 | /* Change color of dropdown links on hover */
606 | .dropdown-content a:hover {background-color: #f1f1f1}
607 |
608 | /* Show the dropdown menu (use JS to add this class to the .dropdown-content container when the user clicks on the dropdown button) */
609 | .dropdown-content.show {
610 | left:0;
611 | transform: translateX(50%);
612 | width:var(--tween-canvas-width);
613 | display:flex;
614 | flex-direction: column;
615 | gap: 0;
616 | }
617 |
618 | .dropdown-content img {
619 | display:block;
620 | }
621 |
622 | /*don't double-up borders between two adjacent images in a dropdown-content*/
623 | .dropdown-content img:not(:last-child) {
624 | border-bottom: none;
625 | }
626 |
627 | form{
628 | display:inline;
629 | padding:0;
630 | margin:0;
631 | display:flex;
632 | flex-direction: column;
633 | }
634 |
635 |
636 | /* MODAL DIALOG STUFF BEGIN */
637 | ::backdrop {
638 | background-color: var(--about-dialog-backdrop-color);
639 | backdrop-filter: blur(5px);
640 | }
641 |
642 |
643 | dialog {
644 | background-color: var(--panel-background-color);
645 | border: var(--panel-border-thickness) solid var(--panel-border-color);
646 | padding: var(--ui-margin);
647 | /*drop shadow*/
648 | filter: drop-shadow(10px 10px 20px rgba(0, 0, 0, 0.5));
649 | width: fit-content;
650 | }
651 |
652 | dialog button {
653 | display: block;
654 | margin-left: auto;
655 | margin-right: auto;
656 | margin-bottom: var(--ui-margin);
657 | }
658 |
659 | dialog h2 {
660 | margin-top: var(--ui-margin);
661 | }
662 |
663 | /* About Dialog Tabs */
664 | #about-dialog {
665 | width: fit-content;
666 | height: fit-content;
667 | padding: var(--ui-margin);
668 | overflow: hidden;
669 | }
670 |
671 | .dialog-tabs {
672 | display: flex;
673 | padding-left: 10px;
674 | background-color: transparent;
675 | }
676 |
677 | .dialog-tab {
678 | margin-top: 0;
679 | padding-left: 10px;
680 | padding-right: 10px;
681 | font-size: var(--font-size);
682 | background-color: var(--tab-background-color-unselected);
683 | border-style: solid;
684 | border-color: var(--tab-unselected-border-color);
685 | border-width: var(--panel-border-thickness);
686 | border-bottom: none;
687 | margin-left: 5px;
688 | margin-right: 5px;
689 | padding-top: 3px;
690 | cursor: pointer;
691 | }
692 |
693 | .dialog-tab:first-child {
694 | margin-left: 0;
695 | }
696 |
697 | .dialog-tab:last-child {
698 | margin-right: 0;
699 | }
700 |
701 | /*non-active tabs*/
702 | .dialog-tab:not(.active) {
703 | /* transform: translateY(var(--panel-border-thickness)); */
704 | transform: translateY(var(--ui-margin));
705 | }
706 |
707 | .dialog-tab.active {
708 | background-color: var(--tab-background-color-selected);
709 | border-color: var(--tab-selected-border-color);
710 | margin-bottom: calc(-1*var(--panel-border-thickness));
711 | z-index: 1;
712 | padding-top: 6px;
713 | cursor: default;
714 | }
715 |
716 | .dialog-content {
717 | height: 320px;
718 | width: 400px;
719 | overflow-y: auto;
720 | overflow-x: hidden;
721 | border-style: solid;
722 | border-color: var(--panel-border-color);
723 | border-width: var(--panel-border-thickness);
724 | padding: var(--ui-margin);
725 | }
726 |
727 | .tab-content {
728 | display: none;
729 | }
730 |
731 | .tab-content.active {
732 | display: block;
733 | }
734 |
735 | #about-dialog button[autofocus] {
736 | margin: var(--ui-margin) auto;
737 | }
738 | /* MODAL DIALOG STUFF END */
739 |
740 | /* SHORTCUTS DL STUFF BEGIN */
741 | .shortcuts-list {
742 | display: grid;
743 | grid-template-columns: max-content auto;
744 | grid-gap: 10px;
745 | }
746 |
747 | .shortcuts-list dt {
748 | grid-column-start: 1;
749 | margin: 0;
750 | }
751 |
752 | .shortcuts-list dd {
753 | grid-column-start: 2;
754 | margin: 0;
755 | }
756 | /* SHORTCUTS DL STUFF END */
757 |
758 |
759 | img {
760 | /*pixelated*/
761 | image-rendering: pixelated;
762 | }
763 |
764 | .dropzone {
765 | box-sizing: border-box;
766 | position: fixed;
767 | z-index: 99999;
768 | width: 20em;
769 | height: 20em;
770 | left: 50%;
771 | top: 50%;
772 | transform: translate(-50%, -50%);
773 | background-color: var(--panel-background-color);
774 | border: 5px dashed var(--panel-border-color);
775 | border-radius: 10px;
776 | opacity: 0.8;
777 | /*center text vertically and horizontally*/
778 | display: none;/*flex to make visible*/
779 | align-items: center;
780 | justify-content: center;
781 | font-size: var(--font-size-bigger);
782 | /*mouse passthrough*/
783 | pointer-events: none;
784 | }
--------------------------------------------------------------------------------
/css/slider.css:
--------------------------------------------------------------------------------
1 | :root {
2 | --sliderheight: 15px;
3 | --sliderwidth: 150px;
4 | --sliderwidthnarrow: 106px;
5 | --col-white: #ffffff;
6 | --col-tanlight: #dbaf85;
7 | --col-tan: #c89057;
8 | --col-lightbrown: #a96742;
9 | --col-mediumbrown: #8f563b;
10 | --col-shadowbrown: #824e37;
11 | --col-shadowbrowner: #7a4836;
12 | --col-darkbrown: #663931;
13 | --col-lipflesh: #964e41;
14 | --col-black: #201010;
15 | --border-thickness: 1px;
16 |
17 | --slider-border-color: var(--col-darkbrown);
18 | --slider-tick-defaultval-color: var(--col-tan);
19 | --slider-tick-color: var(--col-lipflesh);
20 | --slider-background-color: var(--col-tanlight);
21 | --col-sliderhandle: var(--col-darkbrown);
22 |
23 | --tooltip-background-color: #fdfdcc;
24 |
25 | }
26 |
27 | .slidercell .btn-check:checked+.btn-outline-primary,
28 | .slidercell .btn-check:active+.btn-outline-primary,
29 | .btn-outline-primary:active,
30 | .btn-outline-primary.active,
31 | .btn-outline-primary.dropdown-toggle.show {
32 | background-color: var(--col-lipflesh);
33 | /* outline-color: var(--col-black); */
34 | }
35 |
36 |
37 | .slider.slider-horizontal {
38 | margin-left: 1.5px;
39 | margin-right: 1.5px;
40 | position: relative;
41 | top: -4px;
42 | }
43 |
44 | .slider-track {
45 | background-image: none;
46 | background-color: transparent;
47 | border-radius: 0px;
48 | /* outline: 3px solid red; */
49 | outline-offset: -3px;
50 | box-shadow: none;
51 | }
52 |
53 | .slider.slider-disabled {
54 | opacity: 0.3;
55 | }
56 | .slider.slider-disabled .slider-track {
57 | background-color: transparent;
58 | background-image: none;
59 | }
60 |
61 | .slider.slider-disabled .slider-tick {
62 | cursor: not-allowed;
63 | }
64 |
65 | .slider-handle.round {}
66 |
67 | .slider.slider-horizontal .slider-handle {
68 | border-radius: 0px;
69 | width: 6px;
70 | margin-left: 0px;
71 | box-shadow: none;
72 | }
73 |
74 | .slider.slider-horizontal {
75 | width: var(--sliderwidth);
76 | height: var(--sliderheight);
77 | }
78 |
79 | .slider.slider-horizontal.slidernarrow {
80 | width: var(--sliderwidthnarrow);
81 | height: var(--sliderheight);
82 | }
83 |
84 | .slider-handle {
85 | height: var(--sliderheight);
86 | }
87 |
88 | .slider.slider-horizontal .min-slider-handle {
89 | background-image: none;
90 | background-color: transparent;
91 | border-left: var(--border-thickness) solid var(--col-black);
92 | border-top: var(--border-thickness) solid var(--col-black);
93 | border-bottom: var(--border-thickness) solid var(--col-black);
94 | margin-left: -4.5px;
95 | }
96 |
97 | .slider.slider-horizontal .max-slider-handle {
98 | background-image: none;
99 | background-color: transparent;
100 | border-right: var(--border-thickness) solid var(--col-black);
101 | border-top: var(--border-thickness) solid var(--col-black);
102 | border-bottom: var(--border-thickness) solid var(--col-black);
103 | margin-left: -1.5px;
104 | }
105 |
106 | .slider.slider-horizontal .slider-track {
107 | width: 100%;
108 | margin-top: calc(var(--sliderheight) / -2);
109 | top: 50%;
110 | left: 0;
111 | }
112 |
113 | .slider.slider-horizontal .slider-tick.round {
114 | border-radius: 0;
115 | background-color: var(--slider-tick-color);
116 | background-image: none;
117 | width: 3px;
118 | margin-left: -1.5px;
119 | margin-top: var(--sliderheight);
120 | box-shadow: none;
121 | height: 4px;
122 | opacity: 1.0;
123 | }
124 |
125 | .slider.slider-horizontal .slider-tick.round.defaulttick {
126 | background-color: var(--slider-tick-defaultval-color);
127 | height: 4px;
128 | }
129 |
130 | .slider-track-high {
131 | background-color: transparent;
132 | border-radius: 0;
133 | display: none;
134 | box-shadow: none;
135 | }
136 |
137 | .slider-track-low {
138 | background-color: transparent;
139 | border-radius: 0;
140 | display: none;
141 | box-shadow: none;
142 | }
143 |
144 | .slider-selection {
145 | background: transparent;
146 | border-radius: 0;
147 | display: none;
148 | box-shadow: none;
149 | }
150 |
151 | .slider.slider-horizontal .slider-selection {
152 | background: var(--col-lipflesh);
153 | border-radius: 0;
154 | display: block;
155 | top: 5px;
156 | height: calc(var(--sliderheight) - 10px);
157 | outline: 2.2px solid var(--col-lipflesh);
158 | }
159 |
160 | .slider.slider-horizontal.singleselect .slider-selection {
161 | background: #2b242457;
162 | border-radius: 0;
163 | display: block;
164 | top: 5px;
165 | height: calc(var(--sliderheight) - 10px);
166 | outline: 2.2px solid transparent;
167 | }
168 |
169 | .slider.slider-horizontal.singleselect .min-slider-handle {
170 | background-image: none;
171 | background-color: var(--col-sliderhandle);
172 | border: none;
173 | margin-left: -1.5px;
174 | top: 3px;
175 | width: 3px;
176 | height: calc(var(--sliderheight) - 6px);
177 | }
178 |
179 | .slider.slider-horizontal .slider-track {
180 | height: calc(var(--sliderheight));
181 | }
182 |
183 | .slider.slider-horizontal .slider-rangeHighlight.category1 {
184 | background: var(--col-tanlight);
185 | border: 1.5px solid var(--col-tanlight);
186 | transform: translate(-1.5px, 0);
187 | outline: 0;
188 | top: 3px;
189 | height: calc(var(--sliderheight) - 6px);
190 | }
191 |
192 | .slider-border {
193 | position: relative;
194 | float: left;
195 | width: calc(var(--sliderwidth) + 9px);
196 | height: 100%;
197 | background-color: var(--slider-background-color);
198 | left: -4.5px;
199 | border: var(--border-thickness) solid var(--slider-border-color);
200 | box-sizing: border-box;
201 | }
202 |
203 | .slidernarrow>.slider-border {
204 | position: relative;
205 | float: left;
206 | width: calc(var(--sliderwidthnarrow) + 9px);
207 | height: 100%;
208 | /* background-color: blue; */
209 | left: -4.5px;
210 | border: var(--border-thickness) solid var(--slider-border-color);
211 | }
212 |
213 |
214 | .slider-horizontal {
215 | margin-bottom: 3px;
216 | margin-top: 6px;
217 | }
218 |
219 |
220 | .slidercell {
221 | width: calc( var(--sliderwidth) + 13px);
222 | padding-right: 0 !important;
223 | }
224 |
225 |
226 | .globalvolsliderdiv>.fieldtext {
227 | font-weight: 500;
228 | color: var(--col-black);
229 | }
230 |
231 | .tableborder.slidercell {
232 | border-left: 0;
233 | border-right: 0;
234 | }
235 |
236 |
237 |
238 | .slidercell {
239 | border-right: none;
240 | }
241 |
242 |
243 | .globalvolsliderdiv {
244 | margin-left: 3px;
245 | margin-bottom: 6px;
246 | }
247 |
248 | .slider .tooltip.bs-tooltip-top {
249 | margin-top: -36px;
250 | }
251 |
252 | .timevarying_expanded>.slidercell>div {
253 | display: none;
254 | }
255 |
256 |
257 | /* .data-tooltip is a tooltip that is displayed when its parent
258 | is hovered over.*/
259 |
260 | .data-tooltip{
261 | position: absolute;
262 | display: block;
263 |
264 | max-width: 200px;
265 |
266 | opacity: 0;
267 | content: attr(data-tooltip);
268 | pointer-events: none;
269 | transform: translate(-50%,calc(-100% - 10px));
270 | width:max-content;
271 | background: var(--tooltip-background-color);
272 | color: var(--tooltip-text-color);
273 | z-index: 100;
274 |
275 | border-radius: 3px;
276 | padding: 5px;
277 | margin-left: 4px;
278 |
279 | transition: opacity 0.3s ease-in-out;
280 | transition-delay: 0.0s;
281 |
282 | /*add drop shadow*/
283 | box-shadow: 0 0 10px 0 rgba(0, 0, 0, 0.5);
284 | }
285 |
286 | /*when parent hovered*/
287 | .parameter_name:hover > .data-tooltip{
288 | opacity: 1;
289 | transition-delay: 0.5s;
290 | }
291 |
292 | /*when parent hovered*/
293 | .lockcolumn:hover > .data-tooltip{
294 | opacity: 1;
295 | transition-delay: 0.5s;
296 | }
297 |
298 | button:hover > .data-tooltip{
299 | opacity: 1;
300 | transition-delay: 0.5s;
301 | }
302 |
303 | .right_col_label{
304 | text-align: left;
305 | width:100%;
306 | }
307 |
308 | .tooltip-inner{
309 | background-color: var(--tooltip-background-color);
310 | color: var(--tooltip-text-color);
311 | }
312 |
313 | .slidernarrow{
314 | padding-left:2px;
315 | }
316 |
--------------------------------------------------------------------------------
/css/third_party/bootstrap-slider.css:
--------------------------------------------------------------------------------
1 | /*! =======================================================
2 | VERSION 11.0.2
3 | ========================================================= */
4 | /*! =========================================================
5 | * bootstrap-slider.js
6 | *
7 | * Maintainers:
8 | * Kyle Kemp
9 | * - Twitter: @seiyria
10 | * - Github: seiyria
11 | * Rohit Kalkur
12 | * - Twitter: @Rovolutionary
13 | * - Github: rovolution
14 | *
15 | * =========================================================
16 | *
17 | * bootstrap-slider is released under the MIT License
18 | * Copyright (c) 2019 Kyle Kemp, Rohit Kalkur, and contributors
19 | *
20 | * Permission is hereby granted, free of charge, to any person
21 | * obtaining a copy of this software and associated documentation
22 | * files (the "Software"), to deal in the Software without
23 | * restriction, including without limitation the rights to use,
24 | * copy, modify, merge, publish, distribute, sublicense, and/or sell
25 | * copies of the Software, and to permit persons to whom the
26 | * Software is furnished to do so, subject to the following
27 | * conditions:
28 | *
29 | * The above copyright notice and this permission notice shall be
30 | * included in all copies or substantial portions of the Software.
31 | *
32 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
33 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
34 | * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
35 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
36 | * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
37 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
38 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
39 | * OTHER DEALINGS IN THE SOFTWARE.
40 | *
41 | * ========================================================= */
42 | .slider {
43 | display: inline-block;
44 | vertical-align: middle;
45 | position: relative;
46 | }
47 |
48 | .slider.slider-horizontal {
49 | width: 210px;
50 | height: 20px;
51 | }
52 |
53 | .slider.slider-horizontal .slider-track {
54 | height: 10px;
55 | width: 100%;
56 | margin-top: -5px;
57 | top: 50%;
58 | left: 0;
59 | }
60 |
61 | .slider.slider-horizontal .slider-selection, .slider.slider-horizontal .slider-track-low, .slider.slider-horizontal .slider-track-high {
62 | height: 100%;
63 | top: 0;
64 | bottom: 0;
65 | }
66 |
67 | .slider.slider-horizontal .slider-tick,
68 | .slider.slider-horizontal .slider-handle {
69 | margin-left: -10px;
70 | }
71 |
72 | .slider.slider-horizontal .slider-tick.triangle,
73 | .slider.slider-horizontal .slider-handle.triangle {
74 | position: relative;
75 | top: 50%;
76 | transform: translateY(-50%);
77 | border-width: 0 10px 10px 10px;
78 | width: 0;
79 | height: 0;
80 | border-bottom-color: #036fa5;
81 | margin-top: 0;
82 | }
83 |
84 | .slider.slider-horizontal .slider-tick-container {
85 | white-space: nowrap;
86 | position: absolute;
87 | top: 0;
88 | left: 0;
89 | width: 100%;
90 | }
91 |
92 | .slider.slider-horizontal .slider-tick-label-container {
93 | white-space: nowrap;
94 | margin-top: 20px;
95 | }
96 |
97 | .slider.slider-horizontal .slider-tick-label-container .slider-tick-label {
98 | display: inline-block;
99 | text-align: center;
100 | }
101 |
102 | .slider.slider-horizontal.slider-rtl .slider-track {
103 | left: initial;
104 | right: 0;
105 | }
106 |
107 | .slider.slider-horizontal.slider-rtl .slider-tick,
108 | .slider.slider-horizontal.slider-rtl .slider-handle {
109 | margin-left: initial;
110 | margin-right: -10px;
111 | }
112 |
113 | .slider.slider-horizontal.slider-rtl .slider-tick-container {
114 | left: initial;
115 | right: 0;
116 | }
117 |
118 | .slider.slider-vertical {
119 | height: 210px;
120 | width: 20px;
121 | }
122 |
123 | .slider.slider-vertical .slider-track {
124 | width: 10px;
125 | height: 100%;
126 | left: 25%;
127 | top: 0;
128 | }
129 |
130 | .slider.slider-vertical .slider-selection {
131 | width: 100%;
132 | left: 0;
133 | top: 0;
134 | bottom: 0;
135 | }
136 |
137 | .slider.slider-vertical .slider-track-low, .slider.slider-vertical .slider-track-high {
138 | width: 100%;
139 | left: 0;
140 | right: 0;
141 | }
142 |
143 | .slider.slider-vertical .slider-tick,
144 | .slider.slider-vertical .slider-handle {
145 | margin-top: -10px;
146 | }
147 |
148 | .slider.slider-vertical .slider-tick.triangle,
149 | .slider.slider-vertical .slider-handle.triangle {
150 | border-width: 10px 0 10px 10px;
151 | width: 1px;
152 | height: 1px;
153 | border-left-color: #036fa5;
154 | margin-left: 0;
155 | }
156 |
157 | .slider.slider-vertical .slider-tick-label-container {
158 | white-space: nowrap;
159 | }
160 |
161 | .slider.slider-vertical .slider-tick-label-container .slider-tick-label {
162 | padding-left: 4px;
163 | }
164 |
165 | .slider.slider-vertical.slider-rtl .slider-track {
166 | left: initial;
167 | right: 25%;
168 | }
169 |
170 | .slider.slider-vertical.slider-rtl .slider-selection {
171 | left: initial;
172 | right: 0;
173 | }
174 |
175 | .slider.slider-vertical.slider-rtl .slider-tick.triangle,
176 | .slider.slider-vertical.slider-rtl .slider-handle.triangle {
177 | border-width: 10px 10px 10px 0;
178 | }
179 |
180 | .slider.slider-vertical.slider-rtl .slider-tick-label-container .slider-tick-label {
181 | padding-left: initial;
182 | padding-right: 4px;
183 | }
184 |
185 | .slider.slider-disabled .slider-handle {
186 | background-color: #cfcfcf;
187 | background-image: -moz-linear-gradient(top, #DFDFDF, #BEBEBE);
188 | background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#DFDFDF), to(#BEBEBE));
189 | background-image: -webkit-linear-gradient(top, #DFDFDF, #BEBEBE);
190 | background-image: -o-linear-gradient(top, #DFDFDF, #BEBEBE);
191 | background-image: linear-gradient(to bottom, #DFDFDF, #BEBEBE);
192 | background-repeat: repeat-x;
193 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#DFDFDF', endColorstr='#BEBEBE',GradientType=0);
194 | }
195 |
196 | .slider.slider-disabled .slider-track {
197 | background-color: #e7e7e7;
198 | background-image: -moz-linear-gradient(top, #E5E5E5, #E9E9E9);
199 | background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#E5E5E5), to(#E9E9E9));
200 | background-image: -webkit-linear-gradient(top, #E5E5E5, #E9E9E9);
201 | background-image: -o-linear-gradient(top, #E5E5E5, #E9E9E9);
202 | background-image: linear-gradient(to bottom, #E5E5E5, #E9E9E9);
203 | background-repeat: repeat-x;
204 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#E5E5E5', endColorstr='#E9E9E9',GradientType=0);
205 | cursor: not-allowed;
206 | }
207 |
208 | .slider input {
209 | display: none;
210 | }
211 |
212 | .slider .tooltip-inner {
213 | white-space: nowrap;
214 | max-width: none;
215 | }
216 |
217 | .slider .bs-tooltip-top .tooltip-inner,
218 | .slider .bs-tooltip-bottom .tooltip-inner {
219 | position: relative;
220 | left: -50%;
221 | }
222 |
223 | .slider.bs-tooltip-left .tooltip-inner, .slider.bs-tooltip-right .tooltip-inner {
224 | position: relative;
225 | top: -100%;
226 | }
227 |
228 | .slider .tooltip {
229 | pointer-events: none;
230 | }
231 |
232 | .slider .tooltip.bs-tooltip-top .arrow, .slider .tooltip.bs-tooltip-bottom .arrow {
233 | left: -.4rem;
234 | }
235 |
236 | .slider .tooltip.bs-tooltip-top {
237 | margin-top: -44px;
238 | }
239 |
240 | .slider .tooltip.bs-tooltip-bottom {
241 | margin-top: 2px;
242 | }
243 |
244 | .slider .tooltip.bs-tooltip-left, .slider .tooltip.bs-tooltip-right {
245 | margin-top: -14px;
246 | }
247 |
248 | .slider .tooltip.bs-tooltip-left .arrow, .slider .tooltip.bs-tooltip-right .arrow {
249 | top: 8px;
250 | }
251 |
252 | .slider .hide {
253 | display: none;
254 | }
255 |
256 | .slider-track {
257 | background-color: #f7f7f7;
258 | background-image: -moz-linear-gradient(top, #F5F5F5, #F9F9F9);
259 | background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#F5F5F5), to(#F9F9F9));
260 | background-image: -webkit-linear-gradient(top, #F5F5F5, #F9F9F9);
261 | background-image: -o-linear-gradient(top, #F5F5F5, #F9F9F9);
262 | background-image: linear-gradient(to bottom, #F5F5F5, #F9F9F9);
263 | background-repeat: repeat-x;
264 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#F5F5F5', endColorstr='#F9F9F9',GradientType=0);
265 | -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
266 | -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
267 | box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
268 | -webkit-border-radius: 4px;
269 | -moz-border-radius: 4px;
270 | border-radius: 4px;
271 | position: absolute;
272 | cursor: pointer;
273 | }
274 |
275 | .slider-selection {
276 | background-color: #f7f7f7;
277 | background-image: -moz-linear-gradient(top, #F9F9F9, #F5F5F5);
278 | background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#F9F9F9), to(#F5F5F5));
279 | background-image: -webkit-linear-gradient(top, #F9F9F9, #F5F5F5);
280 | background-image: -o-linear-gradient(top, #F9F9F9, #F5F5F5);
281 | background-image: linear-gradient(to bottom, #F9F9F9, #F5F5F5);
282 | background-repeat: repeat-x;
283 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#F9F9F9', endColorstr='#F5F5F5',GradientType=0);
284 | -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);
285 | -moz-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);
286 | box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);
287 | -webkit-box-sizing: border-box;
288 | -moz-box-sizing: border-box;
289 | box-sizing: border-box;
290 | -webkit-border-radius: 4px;
291 | -moz-border-radius: 4px;
292 | border-radius: 4px;
293 | position: absolute;
294 | }
295 |
296 | .slider-selection.tick-slider-selection {
297 | background-color: #46c1fe;
298 | background-image: -moz-linear-gradient(top, #52c5ff, #3abcfd);
299 | background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#52c5ff), to(#3abcfd));
300 | background-image: -webkit-linear-gradient(top, #52c5ff, #3abcfd);
301 | background-image: -o-linear-gradient(top, #52c5ff, #3abcfd);
302 | background-image: linear-gradient(to bottom, #52c5ff, #3abcfd);
303 | background-repeat: repeat-x;
304 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#52c5ff', endColorstr='#3abcfd',GradientType=0);
305 | }
306 |
307 | .slider-track-low, .slider-track-high {
308 | -webkit-box-sizing: border-box;
309 | -moz-box-sizing: border-box;
310 | box-sizing: border-box;
311 | -webkit-border-radius: 4px;
312 | -moz-border-radius: 4px;
313 | border-radius: 4px;
314 | position: absolute;
315 | background: transparent;
316 | }
317 |
318 | .slider-handle {
319 | background-color: #0478b2;
320 | background-image: -moz-linear-gradient(top, #0480BE, #036fa5);
321 | background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0480BE), to(#036fa5));
322 | background-image: -webkit-linear-gradient(top, #0480BE, #036fa5);
323 | background-image: -o-linear-gradient(top, #0480BE, #036fa5);
324 | background-image: linear-gradient(to bottom, #0480BE, #036fa5);
325 | background-repeat: repeat-x;
326 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#0480BE', endColorstr='#036fa5',GradientType=0);
327 | -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
328 | -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
329 | box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
330 | position: absolute;
331 | top: 0;
332 | width: 20px;
333 | height: 20px;
334 | background-color: #0480BE;
335 | border: 0px solid transparent;
336 | }
337 |
338 | .slider-handle:hover {
339 | cursor: pointer;
340 | }
341 |
342 | .slider-handle.round {
343 | -webkit-border-radius: 20px;
344 | -moz-border-radius: 20px;
345 | border-radius: 20px;
346 | }
347 |
348 | .slider-handle.triangle {
349 | background: transparent none;
350 | }
351 |
352 | .slider-handle.custom {
353 | background: transparent none;
354 | }
355 |
356 | .slider-handle.custom::before {
357 | line-height: 20px;
358 | font-size: 20px;
359 | content: '\2605';
360 | color: #726204;
361 | }
362 |
363 | .slider-tick {
364 | background-color: #f7f7f7;
365 | background-image: -moz-linear-gradient(top, #F5F5F5, #F9F9F9);
366 | background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#F5F5F5), to(#F9F9F9));
367 | background-image: -webkit-linear-gradient(top, #F5F5F5, #F9F9F9);
368 | background-image: -o-linear-gradient(top, #F5F5F5, #F9F9F9);
369 | background-image: linear-gradient(to bottom, #F5F5F5, #F9F9F9);
370 | background-repeat: repeat-x;
371 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#F5F5F5', endColorstr='#F9F9F9',GradientType=0);
372 | -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);
373 | -moz-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);
374 | box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);
375 | -webkit-box-sizing: border-box;
376 | -moz-box-sizing: border-box;
377 | box-sizing: border-box;
378 | position: absolute;
379 | cursor: pointer;
380 | width: 20px;
381 | height: 20px;
382 | filter: none;
383 | opacity: 0.8;
384 | border: 0px solid transparent;
385 | }
386 |
387 | .slider-tick.round {
388 | border-radius: 50%;
389 | }
390 |
391 | .slider-tick.triangle {
392 | background: transparent none;
393 | }
394 |
395 | .slider-tick.custom {
396 | background: transparent none;
397 | }
398 |
399 | .slider-tick.custom::before {
400 | line-height: 20px;
401 | font-size: 20px;
402 | content: '\2605';
403 | color: #726204;
404 | }
405 |
406 | .slider-tick.in-selection {
407 | background-color: #46c1fe;
408 | background-image: -moz-linear-gradient(top, #52c5ff, #3abcfd);
409 | background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#52c5ff), to(#3abcfd));
410 | background-image: -webkit-linear-gradient(top, #52c5ff, #3abcfd);
411 | background-image: -o-linear-gradient(top, #52c5ff, #3abcfd);
412 | background-image: linear-gradient(to bottom, #52c5ff, #3abcfd);
413 | background-repeat: repeat-x;
414 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#52c5ff', endColorstr='#3abcfd',GradientType=0);
415 | opacity: 1;
416 | }
--------------------------------------------------------------------------------
/css/third_party/bootstrap-tooltip.css:
--------------------------------------------------------------------------------
1 | :root {
2 | --bs-font-sans-serif: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", "Liberation Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
3 | }
4 |
5 | .input-group> :not(:first-child):not(.dropdown-menu):not(.valid-tooltip):not(.valid-feedback):not(.invalid-tooltip):not(.invalid-feedback) {
6 | margin-left: -1px;
7 | border-top-left-radius: 0;
8 | border-bottom-left-radius: 0;
9 | }
10 |
11 |
12 | .valid-tooltip {
13 | position: absolute;
14 | top: 100%;
15 | z-index: 5;
16 | display: none;
17 | max-width: 100%;
18 | padding: 0.25rem 0.5rem;
19 | margin-top: 0.1rem;
20 | font-size: 0.875rem;
21 | color: #fff;
22 | background-color: rgba(25, 135, 84, 0.9);
23 | border-radius: 0.25rem;
24 | }
25 |
26 | .was-validated :valid~.valid-feedback,
27 | .was-validated :valid~.valid-tooltip,
28 | .is-valid~.valid-feedback,
29 | .is-valid~.valid-tooltip {
30 | display: block;
31 | }
32 |
33 |
34 |
35 | .invalid-tooltip {
36 | position: absolute;
37 | top: 100%;
38 | z-index: 5;
39 | display: none;
40 | max-width: 100%;
41 | padding: 0.25rem 0.5rem;
42 | margin-top: 0.1rem;
43 | font-size: 0.875rem;
44 | color: #fff;
45 | background-color: rgba(220, 53, 69, 0.9);
46 | border-radius: 0.25rem;
47 | }
48 |
49 | .was-validated :invalid~.invalid-feedback,
50 | .was-validated :invalid~.invalid-tooltip,
51 | .is-invalid~.invalid-feedback,
52 | .is-invalid~.invalid-tooltip {
53 | display: block;
54 | }
55 |
56 |
57 | .tooltip {
58 | position: absolute;
59 | z-index: 1070;
60 | display: block;
61 | margin: 0;
62 | font-family: var(--bs-font-sans-serif);
63 | font-style: normal;
64 | font-weight: 400;
65 | line-height: 1.5;
66 | text-align: left;
67 | text-align: start;
68 | text-decoration: none;
69 | text-shadow: none;
70 | text-transform: none;
71 | letter-spacing: normal;
72 | word-break: normal;
73 | word-spacing: normal;
74 | white-space: normal;
75 | line-break: auto;
76 | font-size: 0.875rem;
77 | word-wrap: break-word;
78 | opacity: 0;
79 | }
80 |
81 | .tooltip.show {
82 | opacity: 0.9;
83 | }
84 |
85 | .tooltip .tooltip-arrow {
86 | position: absolute;
87 | display: block;
88 | width: 0.8rem;
89 | height: 0.4rem;
90 | }
91 |
92 | .tooltip .tooltip-arrow::before {
93 | position: absolute;
94 | content: "";
95 | border-color: transparent;
96 | border-style: solid;
97 | }
98 |
99 | .bs-tooltip-top,
100 | .bs-tooltip-auto[data-popper-placement^=top] {
101 | padding: 0.4rem 0;
102 | }
103 |
104 | .bs-tooltip-top .tooltip-arrow,
105 | .bs-tooltip-auto[data-popper-placement^=top] .tooltip-arrow {
106 | bottom: 0;
107 | }
108 |
109 | .bs-tooltip-top .tooltip-arrow::before,
110 | .bs-tooltip-auto[data-popper-placement^=top] .tooltip-arrow::before {
111 | top: -1px;
112 | border-width: 0.4rem 0.4rem 0;
113 | border-top-color: #000;
114 | }
115 |
116 | .bs-tooltip-end,
117 | .bs-tooltip-auto[data-popper-placement^=right] {
118 | padding: 0 0.4rem;
119 | }
120 |
121 | .bs-tooltip-end .tooltip-arrow,
122 | .bs-tooltip-auto[data-popper-placement^=right] .tooltip-arrow {
123 | left: 0;
124 | width: 0.4rem;
125 | height: 0.8rem;
126 | }
127 |
128 | .bs-tooltip-end .tooltip-arrow::before,
129 | .bs-tooltip-auto[data-popper-placement^=right] .tooltip-arrow::before {
130 | right: -1px;
131 | border-width: 0.4rem 0.4rem 0.4rem 0;
132 | border-right-color: #000;
133 | }
134 |
135 | .bs-tooltip-bottom,
136 | .bs-tooltip-auto[data-popper-placement^=bottom] {
137 | padding: 0.4rem 0;
138 | }
139 |
140 | .bs-tooltip-bottom .tooltip-arrow,
141 | .bs-tooltip-auto[data-popper-placement^=bottom] .tooltip-arrow {
142 | top: 0;
143 | }
144 |
145 | .bs-tooltip-bottom .tooltip-arrow::before,
146 | .bs-tooltip-auto[data-popper-placement^=bottom] .tooltip-arrow::before {
147 | bottom: -1px;
148 | border-width: 0 0.4rem 0.4rem;
149 | border-bottom-color: #000;
150 | }
151 |
152 | .bs-tooltip-start,
153 | .bs-tooltip-auto[data-popper-placement^=left] {
154 | padding: 0 0.4rem;
155 | }
156 |
157 | .bs-tooltip-start .tooltip-arrow,
158 | .bs-tooltip-auto[data-popper-placement^=left] .tooltip-arrow {
159 | right: 0;
160 | width: 0.4rem;
161 | height: 0.8rem;
162 | }
163 |
164 | .bs-tooltip-start .tooltip-arrow::before,
165 | .bs-tooltip-auto[data-popper-placement^=left] .tooltip-arrow::before {
166 | left: -1px;
167 | border-width: 0.4rem 0 0.4rem 0.4rem;
168 | border-left-color: #000;
169 | }
170 |
171 | .tooltip-inner {
172 | max-width: 200px;
173 | padding: 0.25rem 0.5rem;
174 | color: #fff;
175 | text-align: center;
176 | background-color: #000;
177 | border-radius: 0.25rem;
178 | }
--------------------------------------------------------------------------------
/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/increpare/bfxr2/9e38f296ec80b8010a87443a682cfcbf6c6df06f/favicon.ico
--------------------------------------------------------------------------------
/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/increpare/bfxr2/9e38f296ec80b8010a87443a682cfcbf6c6df06f/favicon.png
--------------------------------------------------------------------------------
/gzipper:
--------------------------------------------------------------------------------
1 | #!/usr/bin/perl
2 |
3 | # This script should be uploaded to the web server.
4 |
5 | use warnings;
6 | use strict;
7 | use File::Find;
8 | system("rm bin/*.gz");
9 | find (\&wanted, ("bin"));
10 | sub wanted
11 | {
12 | if (/(.*\.(?:html|css|txt|js)$)/i) {
13 | print "Compressing $File::Find::name\n";
14 | if (! -f "$_.gz") {
15 | system ("gzip -cf --best \"$_\" > \"$_.gz\"");
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/img/bg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/increpare/bfxr2/9e38f296ec80b8010a87443a682cfcbf6c6df06f/img/bg.png
--------------------------------------------------------------------------------
/img/delete.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/increpare/bfxr2/9e38f296ec80b8010a87443a682cfcbf6c6df06f/img/delete.png
--------------------------------------------------------------------------------
/img/locked.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/increpare/bfxr2/9e38f296ec80b8010a87443a682cfcbf6c6df06f/img/locked.png
--------------------------------------------------------------------------------
/img/logo_bfxr.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/increpare/bfxr2/9e38f296ec80b8010a87443a682cfcbf6c6df06f/img/logo_bfxr.png
--------------------------------------------------------------------------------
/img/logo_footsteppr.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/increpare/bfxr2/9e38f296ec80b8010a87443a682cfcbf6c6df06f/img/logo_footsteppr.png
--------------------------------------------------------------------------------
/img/logo_transfxr.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/increpare/bfxr2/9e38f296ec80b8010a87443a682cfcbf6c6df06f/img/logo_transfxr.png
--------------------------------------------------------------------------------
/img/unlocked.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/increpare/bfxr2/9e38f296ec80b8010a87443a682cfcbf6c6df06f/img/unlocked.png
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
Bfxr. Make sound effects for your games.
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
Drag file to load
53 |
54 |
55 |
56 |
57 |
58 |
61 |
62 |
101 |
102 |
110 |
111 |
112 |
--------------------------------------------------------------------------------
/insert_templates.js:
--------------------------------------------------------------------------------
1 | /* this script loads up all the json files
2 | templates/[synth]/templatesname.bcol
3 | and generates a javscript dictionary of the form
4 | templates = {
5 | "synthname": {
6 | "templatesname": data
7 | }
8 | }
9 |
10 | */
11 |
12 | const fs = require('fs');
13 | const path = require('path');
14 |
15 | const templates_dir = './templates';
16 | const templates = {};
17 |
18 | console.log("Inserting templates...");
19 |
20 | // Read all synth directories
21 | const synthDirs = fs.readdirSync(templates_dir, { withFileTypes: true })
22 | .filter(dirent => dirent.isDirectory())
23 | .map(dirent => dirent.name);
24 |
25 | // Process each synth directory
26 | synthDirs.forEach(synthDir => {
27 | const synthPath = path.join(templates_dir, synthDir);
28 | templates[synthDir] = {};
29 |
30 | // Read all templates files in the synth directory
31 | const templatesFiles = fs.readdirSync(synthPath, { withFileTypes: true })
32 | .filter(dirent => dirent.isFile())
33 | .filter(dirent => path.extname(dirent.name) === '.bcol')
34 | .map(dirent => dirent.name);
35 |
36 | // Process each templates file
37 | templatesFiles.forEach(templatesFile => {
38 | const filePath = path.join(synthPath, templatesFile);
39 | const fileContent = fs.readFileSync(filePath, 'utf8');
40 | try {
41 | const jsonContent = JSON.parse(fileContent);
42 | // Extract templates name without extension
43 | const templatesName = path.parse(templatesFile).name;
44 | var files = jsonContent[synthDir].files;
45 | // a file is an array of [name, data, data]
46 | //delete the third element
47 |
48 | //then convert to a dictionary
49 | var dict = {};
50 | files.forEach(file => {
51 | dict[file[0]] = JSON.parse(file[1]);
52 | });
53 |
54 | // dict is a dictionary of variety_suffix: dict
55 | // we want to collate all the data for each variety to give
56 | // [ variety, merged_dict (with arrays as keys)]
57 | // (we don't care about the suffix)
58 | var varieties = {};
59 | for (var variety_name_extended in dict) {
60 | var variety_name = variety_name_extended.split("_")[0];
61 | var variety_data = dict[variety_name_extended];
62 | var this_variety = {};
63 | if (!varieties[variety_name]) {
64 | varieties[variety_name] = {};
65 | this_variety = varieties[variety_name];
66 | for (var key in variety_data) {
67 | this_variety[key] = [variety_data[key]];
68 | }
69 | continue;
70 | }
71 | this_variety = varieties[variety_name];
72 | for (var key in variety_data) {
73 | var list = this_variety[key]
74 | var new_value = variety_data[key];
75 | if (list.indexOf(new_value) === -1) {
76 | this_variety[key].push(new_value);
77 | }
78 | }
79 | }
80 | templates[synthDir][templatesName] = varieties;
81 |
82 | } catch (error) {
83 | console.error(`Error parsing ${filePath}: ${error.message}`);
84 | }
85 | });
86 | });
87 |
88 | // Write the templates to a file
89 | fs.writeFileSync('./js/synths/templates.js', `const TEMPLATES_JSON = ${JSON.stringify(templates, null, 2)};`);
90 |
91 | console.log("Templates inserted.");
--------------------------------------------------------------------------------
/js/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/increpare/bfxr2/9e38f296ec80b8010a87443a682cfcbf6c6df06f/js/.DS_Store
--------------------------------------------------------------------------------
/js/SaveLoad.js:
--------------------------------------------------------------------------------
1 | class SaveLoad {
2 | static collection_save_enabled=true;
3 |
4 | static loaded_data = {};
5 |
6 | static save_all_collections(){
7 | if (!SaveLoad.collection_save_enabled){
8 | return;
9 | }
10 | //collect all file info together
11 | var save_str = SaveLoad.serialize_collection();
12 | //save to local storage
13 | localStorage.setItem("save_data", save_str);
14 | console.log("saved all collections (length " + save_str.length + ")");
15 | }
16 |
17 |
18 | static load_all_collections(){
19 | //check if there is any save data
20 | if (!localStorage.getItem("save_data")){
21 | return;
22 | }
23 | SaveLoad.loaded_data = JSON.parse(localStorage.getItem("save_data"));
24 | }
25 |
26 | static check_url_for_sfxr_params(){
27 | var url = window.location.href;
28 | var querystring = window.location.search;
29 | var params = new URLSearchParams(querystring);
30 | if (!params.has("sfx")){
31 | return;
32 | }
33 | var synth_dat_str = params.get("sfx");
34 | var [synth_name,filename,params] = SaveLoad.shallow_dict_deserialize(synth_dat_str);
35 | var tab = tabs.find(tab => tab.synth.name == synth_name);
36 | if (!tab){
37 | console.error("No tab found for synth_name: " + synth_name);
38 | return;
39 | }
40 | tab.set_active_tab();
41 | tab.create_new_sound_from_params(filename,params,true);
42 | //having loaded it, we can update the url to remove the sfx parameter
43 | var new_url = window.location.href.split("?")[0];
44 | window.history.replaceState({}, '', new_url);
45 | }
46 |
47 | static shallow_dict_serialize(synth_name,filename,dict){
48 | //instead of returning a csv string, returns a csv string (with commas delimited by \)
49 | var result = synth_name + "~" + filename + "~";
50 | var keys = Object.keys(dict);
51 | console.log("exporting keys: " + keys);
52 | keys.sort();
53 | for (var i = 0; i < keys.length; i++){
54 | result += dict[keys[i]] + "~";
55 | }
56 | //trim final ","
57 | result = result.slice(0, -1);
58 | return result;
59 | }
60 |
61 | static shallow_dict_deserialize(str){
62 | var entries = str.split("~");
63 | var synth_name = entries[0];
64 | var filename = entries[1];
65 | //need to find the tab that matches the synth_name
66 | var tab = tabs.find(tab => tab.synth.name == synth_name);
67 | if (!tab){
68 | console.error("No tab found for synth_name: " + synth_name);
69 | return;
70 | }
71 | var default_params = tab.synth.default_params();
72 | var keys = Object.keys(default_params);
73 | keys.sort();
74 | console.log("importing keys: " + keys);
75 | var dict = {};
76 | for (var i = 0; i < keys.length; i++){
77 | dict[keys[i]] = parseFloat(entries[i+2]);
78 | }
79 | return [synth_name,filename,dict];
80 | }
81 |
82 |
83 | static load_serialized_synth(str){
84 | var data = JSON.parse(str);
85 |
86 | var synth_name = data.synth_type;
87 | var synth_version = data.version;
88 | var file_name = data.file_name;
89 | var params = data.params;
90 |
91 | var tab = tabs.find(tab => tab.synth.name == synth_name);
92 | if (!tab){
93 | console.error("No tab found for synth_name: " + synth_name);
94 | return;
95 | }
96 | tab.set_active_tab();
97 | tab.create_new_sound_from_params(file_name,params,true);
98 | }
99 |
100 | static load_serialized_collection(str){
101 | var data = JSON.parse(str);
102 | var active_tab_index = data.active_tab_index;
103 | for (var i = 0; i < tabs.length; i++){
104 | var tab = tabs[i];
105 | var files = data[tab.synth.name].files;
106 | var selected_file_index = data[tab.synth.name].selected_file_index;
107 | var create_new_sound = data[tab.synth.name].create_new_sound;
108 | var play_on_change = data[tab.synth.name].play_on_change;
109 | var locked_params = data[tab.synth.name].locked_params;
110 | tab.files = files;
111 | tab.selected_file_index = -1;
112 | tab.create_new_sound = create_new_sound;
113 | tab.play_on_change = play_on_change;
114 | tab.synth.locked_params = locked_params;
115 | tab.update_ui();
116 | if (files[selected_file_index]!=null && files[selected_file_index].length>0){
117 | tab.set_selected_file(files[selected_file_index][0]);
118 | tab.synth.generate_sound();
119 | tab.redraw_waveform();
120 | }
121 | tab.update_ui();
122 | }
123 | tabs[active_tab_index].set_active_tab();
124 | }
125 |
126 | static serialize_collection(){
127 | var save_data = {};
128 | var active_tab_index=-1;
129 | for (var i = 0; i < tabs.length; i++){
130 | var tab = tabs[i];
131 | var files = tab.files;
132 | var selected_file_index = tab.selected_file_index;
133 | var compiled_data = {
134 | files: files,
135 | selected_file_index: selected_file_index,
136 | create_new_sound: tab.create_new_sound,
137 | play_on_change: tab.play_on_change,
138 | locked_params: tab.synth.locked_params
139 | }
140 | save_data[tab.synth.name] = compiled_data;
141 | if (tab.active){
142 | active_tab_index = i;
143 | }
144 | }
145 | save_data.active_tab_index = active_tab_index;
146 | var serialized_str = JSON.stringify(save_data);
147 | return serialized_str;
148 | }
149 |
150 | }
--------------------------------------------------------------------------------
/js/audio/AKWF.js:
--------------------------------------------------------------------------------
1 | class AKWF {
2 |
3 | /* Adventure Kid Waveforms (AKWF) converted for use with Teensy Audio Library
4 | *
5 | * Adventure Kid Waveforms(AKWF) Open waveforms library
6 | * https://www.adventurekid.se/akrt/waveforms/adventure-kid-waveforms/
7 | *
8 | * This code is in the public domain, CC0 1.0 Universal (CC0 1.0)
9 | * https://creativecommons.org/publicdomain/zero/1.0/
10 | *
11 | * Converted by Brad Roy, https://github.com/prosper00
12 | */
13 |
14 | /* AKWF_hvoice_0012 256 samples
15 |
16 | +-----------------------------------------------------------------------------------------------------------------+
17 | | *** ** |
18 | | *** *** |
19 | | *** * ***** ******* |
20 | | ** ** ** ***** *** ** |
21 | | ** * *** **** ** ** |
22 | | ** ** ** ****** * ** |
23 | |** * ** ** ** ** |
24 | | * ** *** ** ** **|
25 | | * * ** ** ** *** |
26 | | ** ** ** * ** ** |
27 | | ** * * ** *** *** |
28 | | * ** *** ** ******** |
29 | | *** ** **** |
30 | | ** ** |
31 | | * *** |
32 | +-----------------------------------------------------------------------------------------------------------------+
33 |
34 |
35 | */
36 |
37 | //suited for lower notes
38 | static hvoice_0012 = [
39 | 33078, 34077, 35044, 36007, 36938, 37805, 38590, 39410, 40186, 40979, 41828, 42751, 43765, 44750, 45625, 46506,
40 | 47377, 48459, 49580, 50412, 51254, 52132, 53054, 53969, 55064, 56224, 57157, 57762, 58474, 59326, 60025, 60421,
41 | 60707, 60933, 61192, 61199, 60011, 57771, 55825, 55237, 55020, 53932, 51409, 48089, 45399, 44422, 44106, 42490,
42 | 39350, 35232, 30729, 27122, 25230, 23798, 21402, 18034, 14910, 12701, 11362, 10543, 9717, 8561, 7008, 4596,
43 | 1765, 131, 201, 1146, 2023, 2735, 3299, 3911, 5049, 7131, 9717, 11930, 13324, 14032, 14722, 16044,
44 | 18235, 21043, 24261, 27245, 29408, 31047, 33083, 35737, 38158, 39841, 40886, 41556, 42213, 43245, 44552, 45917,
45 | 47119, 47870, 48264, 48770, 49539, 49925, 49668, 49229, 48873, 48432, 47913, 47384, 46902, 46605, 46302, 45760,
46 | 45142, 44704, 44318, 43802, 43332, 43042, 42636, 41996, 41337, 40780, 40348, 40072, 39783, 39377, 39035, 38729,
47 | 38329, 37958, 37837, 37831, 37639, 37223, 36573, 35566, 34312, 33093, 32117, 31428, 30844, 30079, 29059, 27979,
48 | 27098, 26276, 25428, 24454, 23049, 21274, 19381, 17785, 16557, 15675, 15000, 14446, 13983, 13647, 13422, 13168,
49 | 12982, 12753, 12429, 12068, 11998, 12294, 12943, 13955, 15251, 16750, 18381, 20097, 21804, 23476, 25102, 26674,
50 | 28171, 29696, 31260, 32930, 34804, 36952, 39192, 41282, 43127, 44699, 46099, 47372, 48514, 49476, 50175, 50618,
51 | 50888, 51099, 51305, 51404, 51383, 51221, 50812, 50133, 49166, 47971, 46660, 45347, 44055, 42645, 40991, 39107,
52 | 37176, 35376, 33764, 32251, 30646, 28956, 27272, 25659, 24192, 22866, 21633, 20368, 19107, 17971, 16966, 16123,
53 | 15408, 14840, 14468, 14309, 14374, 14559, 14792, 14960, 15096, 15298, 15643, 16099, 16589, 17068, 17606, 18341,
54 | 19312, 20471, 21661, 22740, 23624, 24392, 25151, 25945, 26715, 27439, 28051, 28700, 29403, 30342, 31272, 32180,
55 | ];
56 |
57 |
58 | /* AKWF_hvoice_0008 256 samples
59 |
60 | +-----------------------------------------------------------------------------------------------------------------+
61 | | ** ** |
62 | | ** ** |
63 | | ** * |
64 | | ** * ** |
65 | | * ** ******* |
66 | | ** ** ** **** |
67 | | * * *** ** |
68 | |* ** **** ** ** |
69 | |* * ******* *** *** * |
70 | | * *** *** *** ** |
71 | | ** ********** ** ** ** *|
72 | | * *** *** ** ** *|
73 | | ** ** ** ** ** * |
74 | | ** *** *** *** *** ** |
75 | | ** ***** ******* *** ** |
76 | +-----------------------------------------------------------------------------------------------------------------+
77 |
78 |
79 | */
80 |
81 |
82 | //suited for higher notes
83 | static hvoice_0008 = [
84 | 33181, 35486, 37968, 40376, 42771, 45080, 47364, 49564, 51743, 53835, 55881, 57791, 59589, 61183, 62575, 63700,
85 | 64584, 65156, 65464, 65532, 65397, 65033, 64481, 63756, 62857, 61795, 60589, 59244, 57769, 56187, 54493, 52700,
86 | 50804, 48810, 46710, 44526, 42252, 39909, 37506, 35065, 32606, 30170, 27780, 25481, 23312, 21314, 19526, 17987,
87 | 16727, 15759, 15085, 14680, 14507, 14529, 14701, 14981, 15335, 15729, 16131, 16520, 16883, 17220, 17530, 17823,
88 | 18107, 18405, 18742, 19133, 19600, 20152, 20793, 21528, 22346, 23219, 24124, 25037, 25940, 26826, 27679, 28455,
89 | 29125, 29658, 30037, 30271, 30376, 30375, 30291, 30150, 29969, 29778, 29596, 29441, 29340, 29318, 29391, 29570,
90 | 29865, 30266, 30762, 31336, 31959, 32594, 33218, 33807, 34355, 34865, 35333, 35749, 36106, 36392, 36606, 36762,
91 | 36877, 36973, 37079, 37206, 37368, 37560, 37770, 37977, 38155, 38274, 38317, 38275, 38137, 37905, 37573, 37132,
92 | 36575, 35905, 35127, 34265, 33341, 32369, 31357, 30322, 29276, 28227, 27188, 26149, 25114, 24089, 23079, 22088,
93 | 21128, 20203, 19329, 18525, 17801, 17168, 16639, 16221, 15920, 15747, 15693, 15744, 15903, 16162, 16516, 16965,
94 | 17495, 18088, 18756, 19498, 20308, 21208, 22190, 23241, 24387, 25619, 26888, 28221, 29593, 30838, 31899, 32887,
95 | 33833, 34687, 35481, 36256, 37010, 37776, 38593, 39464, 40400, 41413, 42484, 43599, 44747, 45894, 47011, 48063,
96 | 48999, 49776, 50378, 50781, 50983, 50998, 50824, 50475, 49983, 49374, 48691, 47998, 47342, 46758, 46270, 45870,
97 | 45527, 45218, 44900, 44544, 44118, 43587, 42913, 42071, 41043, 39822, 38423, 36863, 35174, 33393, 31568, 29740,
98 | 27974, 26304, 24772, 23384, 22152, 21048, 20083, 19228, 18476, 17792, 17155, 16532, 15920, 15312, 14725, 14184,
99 | 13722, 13384, 13196, 13215, 13436, 13915, 14619, 15604, 16811, 18301, 19969, 21884, 23902, 26136, 28375, 30869,
100 | ];
101 |
102 |
103 |
104 |
105 | /* AKWF_fmsynth_0012 256 samples
106 |
107 | +-----------------------------------------------------------------------------------------------------------------+
108 | | ***** ****** *** ** |
109 | | *** **** *** ** |
110 | | *** *** ** * |
111 | |*** **** *** ** |
112 | |* **** *** * |
113 | | **** *** * **|
114 | | **** *** ** ** |
115 | | ****** ***** * *** |
116 | | *********** * ** |
117 | | ** ** |
118 | | * ** |
119 | | * ** |
120 | | * ** |
121 | | ** ** |
122 | | * *** |
123 | +-----------------------------------------------------------------------------------------------------------------+
124 |
125 |
126 | */
127 |
128 |
129 | static fmsynth_0012 = [
130 | 33063, 33949, 34766, 35596, 36388, 37173, 37929, 38666, 39379, 40067, 40731, 41364, 41976, 42552, 43106, 43625,
131 | 44121, 44580, 45016, 45417, 45794, 46139, 46456, 46745, 47012, 47248, 47464, 47656, 47829, 47980, 48113, 48229,
132 | 48329, 48416, 48490, 48551, 48601, 48643, 48676, 48703, 48723, 48736, 48745, 48748, 48747, 48741, 48728, 48714,
133 | 48693, 48667, 48635, 48595, 48550, 48497, 48436, 48364, 48284, 48193, 48090, 47974, 47846, 47706, 47550, 47380,
134 | 47192, 46991, 46774, 46540, 46289, 46023, 45740, 45441, 45125, 44794, 44446, 44084, 43708, 43316, 42915, 42499,
135 | 42073, 41634, 41187, 40727, 40263, 39791, 39311, 38826, 38337, 37843, 37348, 36850, 36350, 35850, 35350, 34852,
136 | 34356, 33862, 33369, 32882, 32401, 31923, 31451, 30983, 30524, 30072, 29626, 29187, 28759, 28338, 27928, 27527,
137 | 27135, 26754, 26385, 26024, 25678, 25343, 25020, 24708, 24412, 24128, 23858, 23602, 23360, 23134, 22922, 22727,
138 | 22547, 22383, 22235, 22105, 21991, 21894, 21816, 21755, 21711, 21687, 21680, 21693, 21724, 21775, 21845, 21934,
139 | 22044, 22172, 22320, 22487, 22677, 22885, 23113, 23363, 23631, 23921, 24229, 24558, 24908, 25279, 25671, 26081,
140 | 26514, 26965, 27437, 27932, 28443, 28979, 29533, 30108, 30703, 31318, 31954, 32607, 33281, 33975, 34687, 35416,
141 | 36163, 36925, 37703, 38490, 39289, 40096, 40909, 41719, 42527, 43324, 44108, 44867, 45598, 46286, 46925, 47500,
142 | 47996, 48401, 48694, 48858, 48869, 48707, 48340, 47746, 46886, 45734, 44239, 42380, 40091, 37352, 34077, 30264,
143 | 25772, 21005, 16784, 13147, 10049, 7453, 5312, 3592, 2252, 1258, 572, 166, 4, 62, 308, 722,
144 | 1277, 1954, 2735, 3599, 4533, 5522, 6558, 7622, 8712, 9817, 10931, 12049, 13166, 14275, 15381, 16475,
145 | 17558, 18628, 19685, 20730, 21760, 22777, 23780, 24757, 25723, 26697, 27638, 28588, 29498, 30430, 31300, 32236,
146 | ];
147 |
148 | /* Adventure Kid Waveforms (AKWF) converted for use with Teensy Audio Library
149 | *
150 | * Adventure Kid Waveforms(AKWF) Open waveforms library
151 | * https://www.adventurekid.se/akrt/waveforms/adventure-kid-waveforms/
152 | *
153 | * This code is in the public domain, CC0 1.0 Universal (CC0 1.0)
154 | * https://creativecommons.org/publicdomain/zero/1.0/
155 | *
156 | * Converted by Brad Roy, https://github.com/prosper00
157 | */
158 |
159 | /* AKWF_granular_0044 256 samples
160 |
161 | +-----------------------------------------------------------------------------------------------------------------+
162 | | ** ** |
163 | | ** * *** |
164 | | * * ** *** |
165 | | ** * * *** |
166 | | * * ** * |
167 | | *** * * * * |
168 | | *** * * * ** * |
169 | |* * ** * * * ********************************************************|
170 | | * * * * * ** |
171 | | ** * * * * **** |
172 | | **** ** * * ***** |
173 | | **** * ** ** |
174 | | * * |
175 | | * ** |
176 | | ** ** |
177 | +-----------------------------------------------------------------------------------------------------------------+
178 |
179 |
180 | */
181 |
182 |
183 | static granular_0044 = [
184 | 32869, 33554, 33950, 34774, 35134, 36047, 36319, 37297, 37454, 38494, 38511, 39617, 39443, 40664, 40161, 41814,
185 | 38882, 23368, 22518, 21974, 20997, 20545, 19523, 19169, 18210, 17982, 17163, 17128, 16552, 16803, 16594, 17281,
186 | 17607, 18928, 20002, 22162, 24166, 27270, 30129, 33900, 37194, 41086, 44210, 47679, 50274, 53080, 55123, 57263,
187 | 58843, 60380, 61646, 62635, 63750, 64193, 65294, 65000, 65535, 64171, 65114, 62655, 64487, 59956, 65114, 36210,
188 | 212, 3832, 99, 1558, 1, 510, 0, 197, 120, 547, 1094, 1697, 2901, 3914, 5892, 7586,
189 | 10533, 13157, 17216, 20988, 26107, 30361, 35515, 39386, 43777, 46670, 49891, 51621, 53703, 54463, 55644, 55730,
190 | 56241, 55895, 55900, 55320, 54914, 54268, 53486, 52949, 51761, 51561, 49784, 50399, 47306, 51139, 28999, 14730,
191 | 19959, 17550, 20023, 18982, 20690, 20318, 21655, 21737, 22859, 23286, 24286, 24994, 25914, 26840, 27701, 28756,
192 | 29549, 30623, 31268, 32196, 32566, 33184, 33197, 33422, 33100, 32970, 32677, 32843, 32704, 32820, 32726, 32801,
193 | 32745, 32785, 32759, 32771, 32771, 32760, 32779, 32753, 32784, 32751, 32787, 32749, 32786, 32751, 32785, 32753,
194 | 32782, 32756, 32779, 32759, 32775, 32762, 32773, 32765, 32771, 32768, 32768, 32769, 32767, 32769, 32766, 32770,
195 | 32766, 32769, 32767, 32770, 32767, 32769, 32767, 32770, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768,
196 | 32768, 32767, 32767, 32768, 32767, 32768, 32767, 32768, 32769, 32768, 32768, 32768, 32768, 32768, 32768, 32768,
197 | 32767, 32768, 32768, 32768, 32769, 32768, 32768, 32768, 32767, 32768, 32768, 32769, 32768, 32768, 32767, 32769,
198 | 32767, 32769, 32768, 32769, 32767, 32768, 32767, 32768, 32768, 32768, 32768, 32767, 32770, 32767, 32770, 32766,
199 | 32771, 32765, 32772, 32762, 32775, 32757, 32786, 32653, 32303, 32073, 32011, 32104, 32434, 32756, 32768, 32767,
200 | ];
201 |
202 |
203 | }
--------------------------------------------------------------------------------
/js/audio/RealizedSound.js:
--------------------------------------------------------------------------------
1 | class RealizedSound {
2 | static MIN_SAMPLE_RATE = SAMPLE_RATE;
3 |
4 | constructor(length, sample_rate) {
5 | this._buffer = AUDIO_CONTEXT.createBuffer(1, length, sample_rate);
6 | }
7 |
8 |
9 | getBuffer() {
10 | return this._buffer.getChannelData(0);
11 | }
12 |
13 |
14 | source = null;
15 | play() {
16 | ULBS();
17 |
18 | if (this.source!=null){
19 | this.stop();
20 | }
21 | this.source = AUDIO_CONTEXT.createBufferSource();
22 |
23 | this.source.buffer = this._buffer;
24 | this.source.connect(AUDIO_CONTEXT.destination);
25 |
26 | var t = AUDIO_CONTEXT.currentTime;
27 | if (typeof this.source.start != 'undefined') {
28 | this.source.start(t);
29 | } else {
30 | this.source.noteOn(t);
31 | }
32 | this.source.onended = function() {
33 | if (this.source){
34 | this.source.disconnect()
35 | }
36 | }
37 | }
38 |
39 | stop() {
40 | if (this.source==null){
41 | return;
42 | }
43 | this.source.stop();
44 | this.source.disconnect();
45 | this.source = null;
46 | }
47 |
48 | getDataUri() {
49 | const BIT_DEPTH=16;
50 | var raw_buffer = this.getBuffer();
51 | var output_buffer = new Array(raw_buffer.length);
52 | for (var i = 0; i < raw_buffer.length; i++) {
53 | // bit_depth is always 16, rescale [-1.0, 1.0) to [0, 65536)
54 | // Use 32768 (2^15) for 16-bit audio conversion (range: -32768 to 32767)
55 | output_buffer[i] = Math.floor(32768 * Math.max(-1, Math.min(raw_buffer[i], 1)))|0;
56 | }
57 | var wav = MakeRiff(SAMPLE_RATE, BIT_DEPTH, output_buffer);
58 | return wav.dataURI;
59 | }
60 |
61 | static from_buffer(buffer) {
62 | var sound = new RealizedSound(buffer.length, RealizedSound.MIN_SAMPLE_RATE);
63 | sound._buffer.copyToChannel(buffer, 0);
64 | return sound;
65 | };
66 | }
67 |
--------------------------------------------------------------------------------
/js/audio/audio_globals.js:
--------------------------------------------------------------------------------
1 | const SAMPLE_RATE = 44100;
2 | const CONVERSION_FACTOR = (2*Math.PI)/SAMPLE_RATE;
3 |
4 | var AUDIO_CONTEXT;
5 | function checkAudioContextExists() {
6 | try {
7 | if (AUDIO_CONTEXT == null) {
8 | if (typeof AudioContext != 'undefined') {
9 | AUDIO_CONTEXT = new AudioContext();
10 | } else if (typeof webkitAudioContext != 'undefined') {
11 | AUDIO_CONTEXT = new webkitAudioContext();
12 | }
13 | }
14 | } catch (ex) {
15 | window.console.log(ex)
16 | }
17 | }
18 |
19 | checkAudioContextExists();
20 | //unlock bullshit
21 | function ULBS() {
22 | if (AUDIO_CONTEXT.state === 'suspended') {
23 | var unlock = function() {
24 | AUDIO_CONTEXT.resume().then(function() {
25 | document.body.removeEventListener('touchstart', unlock);
26 | document.body.removeEventListener('touchend', unlock);
27 | document.body.removeEventListener('mousedown', unlock);
28 | document.body.removeEventListener('mouseup', unlock);
29 | document.body.removeEventListener('keydown', unlock);
30 | document.body.removeEventListener('keyup', unlock);
31 | });
32 | };
33 |
34 | document.body.addEventListener('touchstart', unlock, false);
35 | document.body.addEventListener('touchend', unlock, false);
36 | document.body.addEventListener('mousedown', unlock, false);
37 | document.body.addEventListener('mouseup', unlock, false);
38 | document.body.addEventListener('keydown', unlock, false);
39 | document.body.addEventListener('keyup', unlock, false);
40 | }
41 | }
--------------------------------------------------------------------------------
/js/audio/puredata.js:
--------------------------------------------------------------------------------
1 | /*
2 | This file implements PureData DSP functions to operate on audio buffers.
3 | */
4 |
5 | const PD_COSTABLESIZE = 2048;
6 | const PD_UNITBIT32 = 1572864; /* 3*2^19; bit 32 has place value 1 */
7 | const PD_HIOFFSET = 1;
8 | const PD_LOWOFFSET = 0;
9 |
10 | var COSTABLENAME;
11 |
12 | function generate_tables(){
13 | COSTABLENAME = new Float32Array(PD_COSTABLESIZE+1);
14 | for (let i = 0; i < PD_COSTABLESIZE; i++) {
15 | COSTABLENAME[i] = Math.cos( 2 * Math.PI * i / PD_COSTABLESIZE);
16 | }
17 | COSTABLENAME[0] = 1;
18 | COSTABLENAME[PD_COSTABLESIZE] = 1;
19 | COSTABLENAME[(PD_COSTABLESIZE/4)|0] = 0;
20 | COSTABLENAME[(3*PD_COSTABLESIZE/4)|0] = 0;
21 | COSTABLENAME[(PD_COSTABLESIZE/2)|0] = -1;
22 | }
23 |
24 | function gt(signal_a,signal_b){
25 | var result = new Float32Array(signal_a.length);
26 | for (let i = 0; i < signal_a.length; i++) {
27 | result[i] = signal_a[i] < signal_b[i] ? 1 : 0;
28 | }
29 | return result;
30 | }
31 |
32 | generate_tables();
33 | var puredata_stream_length = 0;
34 | function pd_set_stream_length_seconds(seconds){
35 | puredata_stream_length = seconds * SAMPLE_RATE;
36 | }
37 |
38 | function pd_fn(fn){
39 | var result = new Float32Array(puredata_stream_length);
40 | for (let i = 0; i < result.length; i++) {
41 | result[i] = fn(i/SAMPLE_RATE);
42 | }
43 | return result;
44 | }
45 |
46 | // white noise signal (in the range from -1 to 1).
47 | // https://pd.iem.sh/objects/noise~/
48 | // https://github.com/pure-data/pure-data/blob/12de13067aee29e332a34eb3539fa3cb967b63a1/src/d_osc.c#L372
49 | function pd_noise(){
50 | var result = new Float32Array(puredata_stream_length);
51 | for (let i = 0; i < result.length; i++) {
52 | result[i] = Math.random() * 2 - 1;
53 | }
54 | return result;
55 | }
56 |
57 | function pd_clip(buffer, min_signal, max_signal){
58 | var result = new Float32Array(buffer.length);
59 | for (let i = 0; i < buffer.length; i++) {
60 | result[i] = Math.max(min_signal[i], Math.min(buffer[i], max_signal[i]));
61 | }
62 | return result;
63 | }
64 |
65 | // cosine wave oscillator
66 | // https://pd.iem.sh/objects/osc~/
67 | // https://github.com/pure-data/pure-data/blob/12de13067aee29e332a34eb3539fa3cb967b63a1/src/d_osc.h#L73C1-L99C6
68 | function pd_osc(freq_signal){
69 | /* original code:
70 | t_osc *x = (t_osc *)(w[1]);
71 | t_sample *in = (t_sample *)(w[2]);
72 | t_sample *out = (t_sample *)(w[3]);
73 | int n = (int)(w[4]);
74 | float *tab = COSTABLENAME, *addr;
75 | t_float f1, f2, frac;
76 | double dphase = x->x_phase + UNITBIT32;
77 | int normhipart;
78 | union tabfudge tf;
79 | float conv = x->x_conv;
80 |
81 | tf.tf_d = UNITBIT32;
82 | normhipart = tf.tf_i[HIOFFSET];
83 | #if 0
84 | while (n--)
85 | {
86 | tf.tf_d = dphase;
87 | dphase += *in++ * conv;
88 | addr = tab + (tf.tf_i[HIOFFSET] & (COSTABLESIZE-1));
89 | tf.tf_i[HIOFFSET] = normhipart;
90 | frac = tf.tf_d - UNITBIT32;
91 | f1 = addr[0];
92 | f2 = addr[1];
93 | *out++ = f1 + frac * (f2 - f1);
94 | }
95 | */
96 | var result = new Float32Array(freq_signal.length);
97 | let dphase = PD_UNITBIT32;
98 | let conv = 2 * Math.PI / SAMPLE_RATE;
99 |
100 | // The bit manipulation in C is not directly translatable to JavaScript
101 | // Instead, we'll use a simpler approach that achieves the same result
102 | let phase = 0;
103 |
104 | for (let i = 0; i < freq_signal.length; i++) {
105 | // Update phase based on frequency
106 | phase += freq_signal[i] * conv;
107 |
108 | // Keep phase in [-π, π] for both positive and negative frequencies
109 | while (phase >= Math.PI) {
110 | phase -= 2 * Math.PI;
111 | }
112 | while (phase < -Math.PI) {
113 | phase += 2 * Math.PI;
114 | }
115 |
116 | // Get table index and fractional part
117 | let index = (((phase + Math.PI) / (2 * Math.PI)) * PD_COSTABLESIZE)|0;
118 | let idx1 = Math.floor(index) % PD_COSTABLESIZE;
119 | let idx2 = (idx1 + 1) % PD_COSTABLESIZE;
120 | let frac = index - idx1;
121 |
122 | // Linear interpolation between adjacent table values
123 | let f1 = COSTABLENAME[idx1];
124 | let f2 = COSTABLENAME[idx2];
125 | result[i] = f1 + frac * (f2 - f1);
126 | }
127 |
128 | return result;
129 | }
130 |
131 | function pd_mul(buffer, multiplier_signal){
132 | var result = new Float32Array(buffer.length);
133 | for (let i = 0; i < buffer.length; i++) {
134 | result[i] = buffer[i] * multiplier_signal[i];
135 | }
136 | return result;
137 | }
138 |
139 | function pd_div(buffer, divisor_signal){
140 | var result = new Float32Array(buffer.length);
141 | for (let i = 0; i < buffer.length; i++) {
142 | result[i] = buffer[i] / divisor_signal[i];
143 | }
144 | return result;
145 | }
146 |
147 | function pd_add(buffer, addend_signal){
148 | var result = new Float32Array(buffer.length);
149 | for (let i = 0; i < buffer.length; i++) {
150 | result[i] = buffer[i] + addend_signal[i];
151 | }
152 | return result;
153 | }
154 |
155 | function pd_polyadd(...buffers){
156 | var result = new Float32Array(buffers[0].length);
157 | for (let i = 0; i < result.length; i++) {
158 | result[i] = buffers[0][i];
159 | for (let j = 1; j < buffers.length; j++) {
160 | result[i] += buffers[j][i];
161 | }
162 | }
163 | return result;
164 | }
165 |
166 | function pd_c(value){
167 | var result = new Float32Array(puredata_stream_length);
168 | result.fill(value);
169 | return result;
170 | }
171 |
172 | function pd_abs(buffer){
173 | var result = new Float32Array(buffer.length);
174 | for (let i = 0; i < buffer.length; i++) {
175 | result[i] = Math.abs(buffer[i]);
176 | }
177 | return result;
178 | }
179 |
180 | function pd_sqrt(buffer){
181 | var result = new Float32Array(buffer.length);
182 | for (let i = 0; i < buffer.length; i++) {
183 | result[i] = Math.sqrt(buffer[i]);
184 | }
185 | return result;
186 | }
187 |
188 | function sigbp_qcos(f)
189 | {
190 | if (f >= -(0.5*Math.PI) && f <= 0.5*Math.PI)
191 | {
192 | var g = f*f;
193 | return (((g*g*g * (-1.0/720.0) + g*g*(1.0/24.0)) - g*0.5) + 1);
194 | }
195 | else {
196 | return 0;
197 | }
198 | }
199 |
200 | /*
201 | // ---------------- bp~ - 2-pole bandpass filter. -----------------
202 |
203 | typedef struct bpctl
204 | {
205 | t_sample c_x1;
206 | t_sample c_x2;
207 | t_sample c_coef1;
208 | t_sample c_coef2;
209 | t_sample c_gain;
210 | } t_bpctl;
211 |
212 | typedef struct sigbp
213 | {
214 | t_object x_obj;
215 | t_float x_sr;
216 | t_float x_freq;
217 | t_float x_q;
218 | t_bpctl x_cspace;
219 | t_float x_f;
220 | } t_sigbp;
221 |
222 | t_class *sigbp_class;
223 |
224 | static void sigbp_docoef(t_sigbp *x, t_floatarg f, t_floatarg q);
225 |
226 | static void *sigbp_new(t_floatarg f, t_floatarg q)
227 | {
228 | t_sigbp *x = (t_sigbp *)pd_new(sigbp_class);
229 | inlet_new(&x->x_obj, &x->x_obj.ob_pd, gensym("float"), gensym("ft1"));
230 | inlet_new(&x->x_obj, &x->x_obj.ob_pd, gensym("float"), gensym("ft2"));
231 | outlet_new(&x->x_obj, &s_signal);
232 | x->x_sr = 44100;
233 | x->x_cspace.c_x1 = 0;
234 | x->x_cspace.c_x2 = 0;
235 | sigbp_docoef(x, f, q);
236 | x->x_f = 0;
237 | return (x);
238 | }
239 |
240 | static t_float sigbp_qcos(t_float f)
241 | {
242 | if (f >= -(0.5f*3.14159f) && f <= 0.5f*3.14159f)
243 | {
244 | t_float g = f*f;
245 | return (((g*g*g * (-1.0f/720.0f) + g*g*(1.0f/24.0f)) - g*0.5) + 1);
246 | }
247 | else return (0);
248 | }
249 |
250 | static void sigbp_docoef(t_sigbp *x, t_floatarg f, t_floatarg q)
251 | {
252 | t_float r, oneminusr, omega;
253 | if (f < 0.001) f = 10;
254 | if (q < 0) q = 0;
255 | x->x_freq = f;
256 | x->x_q = q;
257 | omega = f * (2.0f * 3.14159f) / x->x_sr;
258 | if (q < 0.001) oneminusr = 1.0f;
259 | else oneminusr = omega/q;
260 | if (oneminusr > 1.0f) oneminusr = 1.0f;
261 | r = 1.0f - oneminusr;
262 | x->x_cspace.c_coef1 = 2.0f * sigbp_qcos(omega) * r;
263 | x->x_cspace.c_coef2 = - r * r;
264 | x->x_cspace.c_gain = 2 * oneminusr * (oneminusr + r * omega);
265 | }
266 |
267 | static void sigbp_ft1(t_sigbp *x, t_floatarg f)
268 | {
269 | sigbp_docoef(x, f, x->x_q);
270 | }
271 |
272 | static void sigbp_ft2(t_sigbp *x, t_floatarg q)
273 | {
274 | sigbp_docoef(x, x->x_freq, q);
275 | }
276 |
277 | static void sigbp_clear(t_sigbp *x, t_floatarg q)
278 | {
279 | x->x_cspace.c_x1 = x->x_cspace.c_x2 = 0;
280 | }
281 |
282 | static t_int *sigbp_perform(t_int *w)
283 | {
284 | t_sample *in = (t_sample *)(w[1]);
285 | t_sample *out = (t_sample *)(w[2]);
286 | t_bpctl *c = (t_bpctl *)(w[3]);
287 | int n = (int)w[4];
288 | int i;
289 | t_sample last = c->c_x1;
290 | t_sample prev = c->c_x2;
291 | t_sample coef1 = c->c_coef1;
292 | t_sample coef2 = c->c_coef2;
293 | t_sample gain = c->c_gain;
294 | for (i = 0; i < n; i++)
295 | {
296 | t_sample output = *in++ + coef1 * last + coef2 * prev;
297 | *out++ = gain * output;
298 | prev = last;
299 | last = output;
300 | }
301 | if (PD_BIGORSMALL(last))
302 | last = 0;
303 | if (PD_BIGORSMALL(prev))
304 | prev = 0;
305 | c->c_x1 = last;
306 | c->c_x2 = prev;
307 | return (w+5);
308 | }
309 |
310 | */
311 |
312 | // 2-pole bandpass filter
313 | // https://pd.iem.sh/objects/bp~/
314 | // https://github.com/pure-data/pure-data/blob/12de13067aee29e332a34eb3539fa3cb967b63a1/src/d_filter.c#L325
315 | function pd_bp(buffer, center_freq_signal, q_signal) {
316 | let output_buffer = new Float32Array(buffer.length);
317 | let last = buffer[0];
318 | let prev = buffer[0];
319 |
320 | for (let i = 0; i < buffer.length; i++) {
321 | let f = center_freq_signal[i];
322 | let q = q_signal[i];
323 | if (f < 0.001) f = 10;
324 | if (q < 0) q = 0;
325 |
326 | let omega = f * (2.0 * Math.PI) / SAMPLE_RATE;
327 | let oneminusr;
328 |
329 | if (q < 0.001) {
330 | oneminusr = 1.0;
331 | } else {
332 | oneminusr = omega/q;
333 | }
334 | if (oneminusr > 1.0) oneminusr = 1.0;
335 |
336 | let r = 1.0 - oneminusr;
337 | let coef1 = 2.0 * sigbp_qcos(omega) * r;
338 | let coef2 = -r * r;
339 | let gain = 2 * oneminusr * (oneminusr + r * omega);
340 |
341 | let input = buffer[i];
342 | let output = input + coef1 * last + coef2 * prev;
343 | output_buffer[i] = gain * output;
344 | prev = last;
345 | last = output;
346 | }
347 | return output_buffer;
348 | }
349 |
350 |
351 | // lop one-pole low pass filter.
352 | // https://pd.iem.sh/objects/lop~/
353 | // https://github.com/pure-data/pure-data/blob/12de13067aee29e332a34eb3539fa3cb967b63a1/src/d_filter.c#L139
354 | function pd_lop(buffer, filter_coeff_signal) {
355 | let output_buffer = new Float32Array(buffer.length);
356 | let last = buffer[0];
357 |
358 | for (let i = 0; i < buffer.length; i++) {
359 | let coef = filter_coeff_signal[i]*CONVERSION_FACTOR;
360 | if (coef > 1){
361 | coef = 1;
362 | }
363 | if (coef < 0){
364 | coef = 0;
365 | }
366 | last = coef * buffer[i] + (1-coef) * last;
367 | output_buffer[i] = last;
368 | }
369 |
370 | return output_buffer;
371 | }
372 |
373 | // hip one-pole high pass filter.
374 | // https://pd.iem.sh/objects/hip~/
375 | // https://github.com/pure-data/pure-data/blob/12de13067aee29e332a34eb3539fa3cb967b63a1/src/d_filter.c#L9
376 | function pd_hip(buffer, filter_coeff_signal) {
377 | let output_buffer = new Float32Array(buffer.length);
378 | let last = buffer[0];
379 |
380 | for (let i = 0; i < buffer.length; i++) {
381 | let f = filter_coeff_signal[i];
382 | let coef = 1 - f*CONVERSION_FACTOR;
383 | if (coef< 0){
384 | coef = 0;
385 | }
386 | else if (coef > 1){
387 | coef = 1;
388 | }
389 |
390 | if (coef < 1){
391 | const normal = 0.5*(1+coef);
392 | const cur = buffer[i] + coef*last;
393 | output_buffer[i] = normal*(cur-last);
394 | last = cur;
395 | }
396 | else {
397 | output_buffer[i] = buffer[i];
398 | }
399 | }
400 |
401 | return output_buffer;
402 | }
403 |
404 |
405 | /* voltage-controlled band/low-pass filter
406 | 1st
407 | signal - audio signal to be filtered.
408 | 2nd
409 | signal - resonant frequency in Hz.
410 | 3rd
411 | float - set Q.
412 | */
413 | // https://pd.iem.sh/objects/vcf~/
414 | // https://github.com/pure-data/pure-data/blob/12de13067aee29e332a34eb3539fa3cb967b63a1/src/d_osc.c#L289
415 | // https://github.com/pure-data/pure-data/blob/12de13067aee29e332a34eb3539fa3cb967b63a1/src/d_osc.h#L131
416 | // absolute black magic. Don't understand it - did my best, then let cursor fix it.
417 | function pd_vcf(buffer, res_freq_signal, q_signal) {
418 | let output_buffer = new Float32Array(buffer.length);
419 | let re = 0;
420 | let im = 0;
421 |
422 | // Create a proper tabfudge-like structure to match the original C code
423 | let tf = {
424 | d: 0,
425 | i: new Uint32Array(2)
426 | };
427 |
428 | // Get the normhipart constant similar to the C code
429 | tf.d = PD_UNITBIT32;
430 | const normhipart = tf.i[PD_HIOFFSET];
431 |
432 | for (let i = 0; i < buffer.length; i++) {
433 | let q = q_signal[i];
434 | let qinv = q > 0 ? (1/q) : 0;
435 | let ampcorrect = 2 - 2/(q+2);
436 | let coefr = 0;
437 | let coefi = 0;
438 | let tab = COSTABLENAME;
439 |
440 | // Get the frequency coefficient - use the right conversion factor
441 | let cf = res_freq_signal[i] * CONVERSION_FACTOR;
442 | if (cf < 0) cf = 0;
443 |
444 | // Use the same conversion as in the original C code
445 | let cfindx = cf * (PD_COSTABLESIZE/6.28318);
446 |
447 | // Calculate resonance factor
448 | let r = (qinv > 0) ? (1 - cf * qinv) : 0;
449 | if (r < 0) r = 0;
450 | let oneminusr = 1 - r;
451 |
452 | // Bit-twiddling to get the table index and fraction - similar to original code
453 | let dphase = cfindx + PD_UNITBIT32;
454 | tf.d = dphase;
455 | let tabindex = tf.i[PD_HIOFFSET] & (PD_COSTABLESIZE-1);
456 |
457 | tf.i[PD_HIOFFSET] = normhipart;
458 | let frac = tf.d - PD_UNITBIT32;
459 |
460 | // Get the real coefficient using interpolation
461 | let f1 = tab[tabindex];
462 | let f2 = tab[tabindex+1];
463 | coefr = r * (f1 + frac * (f2 - f1));
464 |
465 | // Get the imaginary coefficient using interpolation
466 | tabindex = ((tabindex - (PD_COSTABLESIZE/4)) & (PD_COSTABLESIZE-1));
467 | f1 = tab[tabindex];
468 | f2 = tab[tabindex+1];
469 | coefi = r * (f1 + frac * (f2 - f1));
470 |
471 | // Apply the filter
472 | let inputSample = buffer[i];
473 | let re2 = re;
474 | re = ampcorrect * oneminusr * inputSample + coefr * re2 - coefi * im;
475 | im = coefi * re2 + coefr * im;
476 |
477 | // Handle numerical instability
478 | if (Math.abs(re) < 1e-10) re = 0;
479 | if (Math.abs(im) < 1e-10) im = 0;
480 |
481 | output_buffer[i] = re;
482 | }
483 |
484 | return output_buffer;
485 | }
486 |
--------------------------------------------------------------------------------
/js/audio/puredata_modules.js:
--------------------------------------------------------------------------------
1 |
2 | const puredata_modules = {"dirt":"#N canvas 283 84 1002 691 12;\r\n#X obj 54 309 *~;\r\n#X obj 72 47 inlet~;\r\n#X obj 76 443 outlet~;\r\n#X obj 138 59 switch~;\r\n#X obj 138 33 inlet;\r\n#X obj 128 221 osc~;\r\n#X obj 127 157 *~;\r\n#X obj 150 82 noise~;\r\n#X obj 150 127 *~ 70;\r\n#X obj 150 104 lop~ 80;\r\n#X obj 100 127 +~ 0.3;\r\n#X obj 128 242 hip~ 200;\r\n#X obj 71 126 *~;\r\n#X obj 70 148 *~;\r\n#X obj 18 250 osc~ 80;\r\n#X obj 18 228 +~ 40;\r\n#X obj 19 202 *~ 500;\r\n#X obj 77 394 +~;\r\n#X obj 127 264 clip~ -1 1;\r\n#X obj 128 179 *~ 70;\r\n#X obj 126 330 *~ 0.04;\r\n#X obj 55 332 *~ 0.5;\r\n#X obj 128 200 +~ 70;\r\n#X connect 0 0 21 0;\r\n#X connect 1 0 10 0;\r\n#X connect 1 0 12 0;\r\n#X connect 1 0 12 1;\r\n#X connect 4 0 3 0;\r\n#X connect 5 0 11 0;\r\n#X connect 6 0 19 0;\r\n#X connect 7 0 9 0;\r\n#X connect 8 0 6 1;\r\n#X connect 9 0 8 0;\r\n#X connect 10 0 6 0;\r\n#X connect 11 0 18 0;\r\n#X connect 12 0 13 0;\r\n#X connect 12 0 13 1;\r\n#X connect 13 0 0 1;\r\n#X connect 13 0 16 0;\r\n#X connect 14 0 0 0;\r\n#X connect 15 0 14 0;\r\n#X connect 16 0 15 0;\r\n#X connect 17 0 2 0;\r\n#X connect 18 0 20 0;\r\n#X connect 19 0 22 0;\r\n#X connect 20 0 17 1;\r\n#X connect 21 0 17 0;\r\n#X connect 22 0 5 0;\r\n","grass":"#N canvas 136 86 1002 691 12;\r\n#X obj 293 399 *~;\r\n#X obj 311 137 inlet~;\r\n#X obj 401 564 outlet~;\r\n#X obj 310 216 *~;\r\n#X obj 309 238 *~;\r\n#X obj 257 340 osc~ 80;\r\n#X obj 401 527 +~;\r\n#X obj 294 422 *~ 0.8;\r\n#X obj 257 318 +~ 30;\r\n#X obj 257 292 *~ 600;\r\n#X obj 257 364 clip~ 0 0.5;\r\n#X obj 591 98 noise~;\r\n#X obj 598 269 lop~ 16;\r\n#X obj 493 231 *~;\r\n#X obj 493 255 *~;\r\n#X obj 599 295 *~ 23800;\r\n#X obj 492 159 /~;\r\n#X obj 521 132 lop~ 2000;\r\n#X obj 459 132 lop~ 300;\r\n#X obj 492 298 clip~ -0.9 0.9;\r\n#X obj 599 317 +~ 3400;\r\n#X obj 492 276 *~ 1e-05;\r\n#X obj 492 196 hip~ 2500;\r\n#X obj 477 472 *~;\r\n#X obj 599 342 clip~ 2000 10000;\r\n#X obj 493 418 hip~ 900;\r\n#X obj 493 450 *~ 0.3;\r\n#X obj 493 388 vcf~ 3333 1;\r\n#X connect 0 0 7 0;\r\n#X connect 1 0 3 0;\r\n#X connect 1 0 3 1;\r\n#X connect 1 0 23 0;\r\n#X connect 3 0 4 0;\r\n#X connect 3 0 4 1;\r\n#X connect 4 0 0 1;\r\n#X connect 4 0 9 0;\r\n#X connect 5 0 10 0;\r\n#X connect 6 0 2 0;\r\n#X connect 7 0 6 0;\r\n#X connect 8 0 5 0;\r\n#X connect 9 0 8 0;\r\n#X connect 10 0 0 0;\r\n#X connect 11 0 12 0;\r\n#X connect 11 0 17 0;\r\n#X connect 11 0 18 0;\r\n#X connect 12 0 15 0;\r\n#X connect 13 0 14 0;\r\n#X connect 13 0 14 1;\r\n#X connect 14 0 21 0;\r\n#X connect 15 0 20 0;\r\n#X connect 16 0 22 0;\r\n#X connect 17 0 16 1;\r\n#X connect 18 0 16 0;\r\n#X connect 19 0 27 0;\r\n#X connect 20 0 24 0;\r\n#X connect 21 0 19 0;\r\n#X connect 22 0 13 0;\r\n#X connect 22 0 13 1;\r\n#X connect 23 0 6 1;\r\n#X connect 24 0 27 1;\r\n#X connect 25 0 26 0;\r\n#X connect 26 0 23 1;\r\n#X connect 27 0 25 0;\r\n","gravel":"#N canvas 772 379 1002 691 12;\n#X obj 123 19 inlet~;\n#X obj 123 499 outlet~;\n#X obj 252 14 noise~;\n#X obj 136 160 *~;\n#X obj 136 107 /~;\n#X obj 166 70 lop~ 2000;\n#X obj 104 70 lop~ 300;\n#X obj 137 241 clip~ -0.9 0.9;\n#X obj 122 475 *~;\n#X obj 156 380 clip~ 500 10000;\n#X obj 154 271 lop~ 50;\n#X obj 137 134 hip~ 400;\n#X obj 137 219 *~ 0.01;\n#X obj 155 297 *~ 50000;\n#X obj 138 430 hip~ 200;\n#X obj 138 453 *~ 2;\n#X obj 156 355 +~;\n#X obj 173 330 *~ 1000;\n#X obj 138 406 vcf~ 200 3;\n#X connect 0 0 8 0;\n#X connect 0 0 17 0;\n#X connect 2 0 5 0;\n#X connect 2 0 6 0;\n#X connect 2 0 10 0;\n#X connect 3 0 12 0;\n#X connect 4 0 11 0;\n#X connect 5 0 4 1;\n#X connect 6 0 4 0;\n#X connect 7 0 18 0;\n#X connect 8 0 1 0;\n#X connect 9 0 18 1;\n#X connect 10 0 13 0;\n#X connect 11 0 3 0;\n#X connect 11 0 3 1;\n#X connect 12 0 7 0;\n#X connect 13 0 16 0;\n#X connect 14 0 15 0;\n#X connect 15 0 8 1;\n#X connect 16 0 9 0;\n#X connect 17 0 16 1;\n#X connect 18 0 14 0;\n","snow":"#N canvas 768 303 857 606 12;\r\n#X obj 161 453 *~;\r\n#X obj 109 225 noise~;\r\n#X obj 102 270 /~;\r\n#X obj 103 343 clip~ -1 1;\r\n#X obj 60 247 lop~ 110;\r\n#X obj 121 246 lop~ 900;\r\n#X obj 166 169 noise~;\r\n#X obj 165 219 /~;\r\n#X obj 123 196 lop~ 50;\r\n#X obj 184 195 lop~ 70;\r\n#X obj 103 290 *~;\r\n#X obj 192 220 lop~ 10;\r\n#X obj 191 241 *~ 17;\r\n#X obj 190 264 *~;\r\n#X obj 103 313 *~;\r\n#X obj 189 286 +~ 0.5;\r\n#X obj 424 130 inlet~;\r\n#X obj 160 507 outlet~;\r\n#X obj 102 365 hip~ 300;\r\n#X obj 185 389 +~ 700;\r\n#X obj 185 365 *~ 9000;\r\n#X obj 162 476 *~ 0.2;\r\n#X obj 146 412 vcf~ 200 0.5;\r\n#X connect 0 0 21 0;\r\n#X connect 1 0 4 0;\r\n#X connect 1 0 5 0;\r\n#X connect 2 0 10 0;\r\n#X connect 3 0 18 0;\r\n#X connect 4 0 2 0;\r\n#X connect 5 0 2 1;\r\n#X connect 6 0 8 0;\r\n#X connect 6 0 9 0;\r\n#X connect 6 0 11 0;\r\n#X connect 7 0 10 1;\r\n#X connect 8 0 7 0;\r\n#X connect 9 0 7 1;\r\n#X connect 10 0 14 0;\r\n#X connect 11 0 12 0;\r\n#X connect 12 0 13 0;\r\n#X connect 12 0 13 1;\r\n#X connect 13 0 15 0;\r\n#X connect 14 0 3 0;\r\n#X connect 15 0 14 1;\r\n#X connect 16 0 0 1;\r\n#X connect 16 0 20 0;\r\n#X connect 18 0 22 0;\r\n#X connect 19 0 22 1;\r\n#X connect 20 0 19 0;\r\n#X connect 21 0 17 0;\r\n#X connect 22 0 0 0;\r\n","wood":"#N canvas 945 155 1270 731 12;\r\n#X obj 470 445 *~;\r\n#X obj 181 142 inlet~;\r\n#X obj 401 564 outlet~;\r\n#X obj 180 221 *~;\r\n#X obj 401 527 +~;\r\n#X obj 466 255 noise~;\r\n#X obj 419 322 *~ 6;\r\n#X obj 801 262 noise~;\r\n#X obj 355 423 *~;\r\n#X obj 737 287 bp~ 123 20;\r\n#X obj 180 251 *~ 2;\r\n#X obj 336 293 bp~ 95 90;\r\n#X obj 411 291 bp~ 134 90;\r\n#X obj 489 290 bp~ 139 90;\r\n#X obj 567 290 bp~ 154 90;\r\n#X obj 815 286 bp~ 156 90;\r\n#X obj 893 286 bp~ 189 90;\r\n#X obj 745 318 *~ 8;\r\n#X obj 662 289 bp~ 201 70;\r\n#X obj 243 258 sqrt~;\r\n#X obj 356 449 *~ 0.5;\r\n#X obj 469 469 *~ 0.6;\r\n#X connect 0 0 21 0;\r\n#X connect 1 0 3 0;\r\n#X connect 1 0 3 1;\r\n#X connect 1 0 19 0;\r\n#X connect 3 0 10 0;\r\n#X connect 4 0 2 0;\r\n#X connect 5 0 11 0;\r\n#X connect 5 0 12 0;\r\n#X connect 5 0 13 0;\r\n#X connect 5 0 14 0;\r\n#X connect 6 0 8 1;\r\n#X connect 7 0 9 0;\r\n#X connect 7 0 15 0;\r\n#X connect 7 0 16 0;\r\n#X connect 7 0 18 0;\r\n#X connect 8 0 20 0;\r\n#X connect 9 0 17 0;\r\n#X connect 10 0 0 0;\r\n#X connect 11 0 6 0;\r\n#X connect 12 0 6 0;\r\n#X connect 13 0 6 0;\r\n#X connect 14 0 6 0;\r\n#X connect 15 0 17 0;\r\n#X connect 16 0 17 0;\r\n#X connect 17 0 0 1;\r\n#X connect 18 0 17 0;\r\n#X connect 19 0 8 0;\r\n#X connect 20 0 4 0;\r\n#X connect 21 0 4 1;\r\n"};
3 |
--------------------------------------------------------------------------------
/js/audio/puredata_parser.js:
--------------------------------------------------------------------------------
1 | var puredata_functions = {};
2 |
3 | //for each source file - object of puredata_modules
4 | var puredata_function_names = Object.keys(puredata_modules);
5 |
6 | function pd_compile(src){
7 | //replace all ";" with ""
8 | src = src.replace(/;/g, "");
9 | src = src.replace(/\r/g, "");
10 | var lines = src.split("\n");
11 |
12 | //split each line at space
13 | lines = lines.map(line => line.split(" "));
14 |
15 | /*
16 | so we need to parse the lines, then build up the function
17 | lines look like
18 | #N canvas 151 188 450 300 12;
19 | (can ignore everything starting with #N)
20 |
21 | #X obj 411 291 bp~ 134 90;
22 | (bp~ 134 90 is the function call with its arguments - important!)
23 |
24 | #X connect 5 0 11 0;
25 | (connect is the connection between the two objects - very important!)
26 | */
27 |
28 | var function_calls = [];
29 | var connections = [];
30 | for (let i = 0; i < lines.length; i++) {
31 | var toks = lines[i];
32 | switch (toks[1]) {
33 | case "obj":
34 | function_calls.push(toks.slice(4));
35 | break;
36 | case "msg":
37 | //basically the same as sig~
38 | function_calls.push(["sig~",toks[4]]);
39 | break;
40 | case "connect":
41 | var connection = {
42 | from_ob: parseInt(toks[2]),
43 | from_slot: parseInt(toks[3]),
44 | to_ob: parseInt(toks[4]),
45 | to_slot: parseInt(toks[5])
46 | };
47 | connections.push(connection);
48 | break;
49 | }
50 | }
51 |
52 | var function_body = "";
53 | var output_node_idx = function_calls.findIndex(call => call[0] === "outlet~");
54 |
55 | var function_tree = build_function_tree(function_calls, connections,output_node_idx);
56 | var function_body = build_function_body(function_tree);
57 | // console.log(function_body);
58 | var fn = new Function("envelope_signal", function_body);
59 | return fn;
60 | }
61 |
62 | var function_info = {
63 | "outlet~":{
64 | input_slots:1,
65 | parameter_indices:[],
66 | js_name:"outlet~"
67 | },
68 | "inlet~":{
69 | input_slots:0,
70 | parameter_indices:[],
71 | js_name:"inlet~"
72 | },
73 | "*~":{
74 | input_slots:2,
75 | parameter_indices:[1],
76 | js_name:"pd_mul"
77 | },
78 | "/~":{
79 | input_slots:2,
80 | parameter_indices:[1],
81 | js_name:"pd_div"
82 | },
83 | "+~":{
84 | input_slots:2,
85 | parameter_indices:[1],
86 | js_name:"pd_add"
87 | },
88 | "sig~":{
89 | input_slots:1,
90 | parameter_indices:[0],
91 | js_name:"pd_c"
92 | },
93 | "vcf~":{
94 | input_slots:3,
95 | parameter_indices:[2],
96 | js_name:"pd_vcf"
97 | },
98 | "hip~":{
99 | input_slots:2,
100 | parameter_indices:[1],
101 | js_name:"pd_hip"
102 | },
103 | "lop~":{
104 | input_slots:2,
105 | parameter_indices:[1],
106 | js_name:"pd_lop"
107 | },
108 | "noise~":{
109 | input_slots:0,
110 | parameter_indices:[],
111 | js_name:"pd_noise"
112 | },
113 | "clip~":{
114 | input_slots:3,
115 | parameter_indices:[1,2],
116 | js_name:"pd_clip"
117 | },
118 | "osc~":{
119 | input_slots:1,
120 | parameter_indices:[0],
121 | js_name:"pd_osc"
122 | },
123 | "bp~":{
124 | input_slots:3,
125 | parameter_indices:[1,2],
126 | js_name:"pd_bp"
127 | },
128 | "sqrt~":{
129 | input_slots:1,
130 | parameter_indices:[0],
131 | js_name:"pd_sqrt"
132 | }
133 |
134 | }
135 |
136 | function build_function_tree(function_calls, connections, cur_node_idx){
137 | var function_call = function_calls[cur_node_idx];
138 | var node_name = function_call[0];
139 | var constructor_arguments = function_call.slice(1);
140 | if (!function_info.hasOwnProperty(node_name)){
141 | console.error("Unknown function: " + node_name);
142 | }
143 | var fn_info = function_info[node_name];
144 | var node_input_slots = fn_info.input_slots;
145 |
146 | // Fix: Create unique arrays for each slot instead of references to the same array
147 | var input_connection_array = Array(node_input_slots).fill().map(() => []);
148 |
149 | //all functions that connect to the current node
150 | var input_connections = connections.filter(connection => connection.to_ob === cur_node_idx);
151 | for (let i = 0; i < input_connections.length; i++) {
152 | var connection = input_connections[i];
153 | // this would be more complicated if any of our functions had multiple outputs,
154 | // but in practice this, happily, never happens!
155 | var connection_data = build_function_tree(function_calls, connections, connection.from_ob);
156 | input_connection_array[connection.to_slot].push(connection_data);
157 | }
158 | var call_data = {
159 | node_name: node_name,
160 | inputs: input_connection_array,
161 | constructor_arguments: constructor_arguments,
162 | node_id: cur_node_idx
163 | }
164 | return call_data;
165 | }
166 |
167 |
168 | function assign_variable_names(syntax_tree_branch,idx,name_dictionary){
169 | if (name_dictionary.hasOwnProperty(syntax_tree_branch.node_id)){
170 | if (name_dictionary[syntax_tree_branch.node_id]
b.node_idx - a.node_idx);
247 | // console.log(flattened_tree);
248 |
249 | var function_body = "";
250 | for (let i = 0; i < flattened_tree.length; i++) {
251 | var node = flattened_tree[i];
252 | //inputs is a array of arrays - we need to have the slots like just s_1 if it's one input, or summing if it's multiple, like
253 | // pd_polyadd(s_1,s_2,s_3)
254 | var args = "";
255 | for (let j = 0; j < node.inputs.length; j++) {
256 | if (j>0){
257 | args += ",";
258 | }
259 | var slot_input = node.inputs[j];
260 | if (slot_input.length === 0){
261 | if (node.constructor_arguments[j]===-1){
262 | args += "NULL";
263 | } else {
264 | args += `pd_c(${node.constructor_arguments[j]})`;
265 | }
266 | } else if (slot_input.length === 1){
267 | args += `s_${slot_input[0]}`;
268 | } else {
269 | args += `pd_polyadd(${slot_input.map(input => `s_${input}`).join(",")})`;
270 | }
271 | }
272 |
273 | // Fix: Convert PureData function names to valid JavaScript function names
274 | var js_funcname = node.node_name;
275 | if (js_funcname==="inlet~"){
276 | function_body += `const s_${node.node_idx} = envelope_signal;\n`;
277 | } else if (js_funcname==="outlet~"){
278 | function_body += `const s_${node.node_idx} = ${args};\n`;
279 | } else {
280 | if (function_info.hasOwnProperty(node.node_name)){
281 | js_funcname = function_info[node.node_name].js_name;
282 | } else {
283 | console.error("Unknown function: " + node.node_name);
284 | }
285 | function_body += `const s_${node.node_idx} = ${js_funcname}(${args});\n`;
286 | }
287 | }
288 | function_body += `return s_0;\n`;
289 | return function_body;
290 | }
291 |
292 | for (let i = 0; i < puredata_function_names.length; i++) {
293 | var function_name = puredata_function_names[i];
294 | var pd_source = puredata_modules[function_name];
295 | // console.log("loading "+function_name);
296 | var pd_compiled = pd_compile(pd_source);
297 | puredata_functions[function_name] = pd_compiled;
298 | }
299 |
--------------------------------------------------------------------------------
/js/audio/riffwave.js:
--------------------------------------------------------------------------------
1 | /*
2 | * RIFFWAVE.js v0.02 - Audio encoder for HTML5