├── .gitignore ├── Gemfile ├── LICENSE ├── README.md ├── _config.yml ├── bower.json ├── docs ├── attributesexamples.md ├── creatingknobimages.md ├── defaultstyles.md ├── detailspecs.md ├── determiningknobsize.md ├── docstyle.css ├── gnavi.html ├── howitworks.md ├── index.md ├── install.md ├── knobgallery.md ├── midisupport.md ├── multitouch.md ├── nonlinear.md ├── resizetest.md ├── tracking.md ├── webaudiocontrolsoptions.md └── workingkeyboard.md ├── img ├── LittlePhatty_sample.png ├── bg.png ├── defknob2.png ├── demo.png ├── hsliderbody.png ├── hsliderknob.png ├── hsw5.png ├── midilearn.png ├── sample3.png ├── switch_toggle.png ├── testknob.png ├── vsliderbody.png └── vsliderknob.png ├── index.md ├── knobs ├── Aqua.knob ├── Aqua.png ├── Carbon.knob ├── Carbon.png ├── JP8000.knob ├── JP8000.png ├── Jambalaya.knob ├── Jambalaya.png ├── LittlePhatty.knob ├── LittlePhatty.png ├── MiniMoog_Main.knob ├── MiniMoog_Main.png ├── SimpleFlat3.knob ├── SimpleFlat3.png ├── Vintage_Knob.knob ├── Vintage_Knob.png ├── Vintage_VUMeter.knob ├── Vintage_VUMeter.png ├── Vintage_VUMeter_2.knob ├── Vintage_VUMeter_2.png ├── WOK_vintage_AbbeyRoad_PAN_Knob.png ├── controls.skin ├── hsliderbody.png ├── hsliderknob.png ├── hsw5.png ├── knob_metal_mesh.png ├── lineshadow.knob ├── lineshadow.png ├── lineshadow2.knob ├── lineshadow2.png ├── m400.knob ├── m400.png ├── nice_lamp_knob.png ├── parambg120x32.png ├── plastic_knob.png ├── redbutton128.png ├── simplegray.knob ├── simplegray.png ├── switch_toggle.knob ├── switch_toggle.png ├── vernier.knob ├── vernier.png ├── vsliderbody.png └── vsliderknob.png ├── package.json ├── webaudio-controls.js └── webcomponents-lite.js /.gitignore: -------------------------------------------------------------------------------- 1 | # jekyll 2 | _site/ 3 | Gemfile.lock 4 | 5 | # bower 6 | bower_components/ 7 | !bower_components/polymer/LICENSE.txt 8 | !bower_components/polymer/polymer.html 9 | !bower_components/polymer/polymer-micro.html 10 | !bower_components/polymer/polymer-mini.html 11 | !bower_components/webcomponentsjs/webcomponents-lite.min.js 12 | !2.0/bower_components/webcomponentsjs/webcomponents-lite.js 13 | 14 | 15 | # Windows image file caches 16 | Thumbs.db 17 | ehthumbs.db 18 | 19 | # Folder config file 20 | Desktop.ini 21 | 22 | # Download Test file 23 | test/ 24 | 25 | # Recycle Bin used on file shares 26 | $RECYCLE.BIN/ 27 | 28 | # Windows Installer files 29 | *.cab 30 | *.msi 31 | *.msm 32 | *.msp 33 | 34 | # ========================= 35 | # Operating System Files 36 | # ========================= 37 | 38 | # OSX 39 | # ========================= 40 | 41 | .DS_Store 42 | .AppleDouble 43 | .LSOverride 44 | 45 | # Icon must end with two \r 46 | Icon 47 | 48 | 49 | # Thumbnails 50 | ._* 51 | 52 | # Files that might appear on external disk 53 | .Spotlight-V100 54 | .Trashes 55 | 56 | # Directories potentially created on remote AFP share 57 | .AppleDB 58 | .AppleDesktop 59 | Network Trash Folder 60 | Temporary Items 61 | .apdisk 62 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | gem 'github-pages', group: :jekyll_plugins 3 | gem 'jekyll-redirect-from' 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, and 10 | distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by the copyright 13 | owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all other entities 16 | that control, are controlled by, or are under common control with that entity. 17 | For the purposes of this definition, "control" means (i) the power, direct or 18 | indirect, to cause the direction or management of such entity, whether by 19 | contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the 20 | outstanding shares, or (iii) beneficial ownership of such entity. 21 | 22 | "You" (or "Your") shall mean an individual or Legal Entity exercising 23 | permissions granted by this License. 24 | 25 | "Source" form shall mean the preferred form for making modifications, including 26 | but not limited to software source code, documentation source, and configuration 27 | files. 28 | 29 | "Object" form shall mean any form resulting from mechanical transformation or 30 | translation of a Source form, including but not limited to compiled object code, 31 | generated documentation, and conversions to other media types. 32 | 33 | "Work" shall mean the work of authorship, whether in Source or Object form, made 34 | available under the License, as indicated by a copyright notice that is included 35 | in or attached to the work (an example is provided in the Appendix below). 36 | 37 | "Derivative Works" shall mean any work, whether in Source or Object form, that 38 | is based on (or derived from) the Work and for which the editorial revisions, 39 | annotations, elaborations, or other modifications represent, as a whole, an 40 | original work of authorship. For the purposes of this License, Derivative Works 41 | shall not include works that remain separable from, or merely link (or bind by 42 | name) to the interfaces of, the Work and Derivative Works thereof. 43 | 44 | "Contribution" shall mean any work of authorship, including the original version 45 | of the Work and any modifications or additions to that Work or Derivative Works 46 | thereof, that is intentionally submitted to Licensor for inclusion in the Work 47 | by the copyright owner or by an individual or Legal Entity authorized to submit 48 | on behalf of the copyright owner. For the purposes of this definition, 49 | "submitted" means any form of electronic, verbal, or written communication sent 50 | to the Licensor or its representatives, including but not limited to 51 | communication on electronic mailing lists, source code control systems, and 52 | issue tracking systems that are managed by, or on behalf of, the Licensor for 53 | the purpose of discussing and improving the Work, but excluding communication 54 | that is conspicuously marked or otherwise designated in writing by the copyright 55 | owner as "Not a Contribution." 56 | 57 | "Contributor" shall mean Licensor and any individual or Legal Entity on behalf 58 | of whom a Contribution has been received by Licensor and subsequently 59 | incorporated within the Work. 60 | 61 | 2. Grant of Copyright License. 62 | 63 | Subject to the terms and conditions of this License, each Contributor hereby 64 | grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, 65 | irrevocable copyright license to reproduce, prepare Derivative Works of, 66 | publicly display, publicly perform, sublicense, and distribute the Work and such 67 | Derivative Works in Source or Object form. 68 | 69 | 3. Grant of Patent License. 70 | 71 | Subject to the terms and conditions of this License, each Contributor hereby 72 | grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, 73 | irrevocable (except as stated in this section) patent license to make, have 74 | made, use, offer to sell, sell, import, and otherwise transfer the Work, where 75 | such license applies only to those patent claims licensable by such Contributor 76 | that are necessarily infringed by their Contribution(s) alone or by combination 77 | of their Contribution(s) with the Work to which such Contribution(s) was 78 | submitted. If You institute patent litigation against any entity (including a 79 | cross-claim or counterclaim in a lawsuit) alleging that the Work or a 80 | Contribution incorporated within the Work constitutes direct or contributory 81 | patent infringement, then any patent licenses granted to You under this License 82 | for that Work shall terminate as of the date such litigation is filed. 83 | 84 | 4. Redistribution. 85 | 86 | You may reproduce and distribute copies of the Work or Derivative Works thereof 87 | in any medium, with or without modifications, and in Source or Object form, 88 | provided that You meet the following conditions: 89 | 90 | You must give any other recipients of the Work or Derivative Works a copy of 91 | this License; and 92 | You must cause any modified files to carry prominent notices stating that You 93 | changed the files; and 94 | You must retain, in the Source form of any Derivative Works that You distribute, 95 | all copyright, patent, trademark, and attribution notices from the Source form 96 | of the Work, excluding those notices that do not pertain to any part of the 97 | Derivative Works; and 98 | If the Work includes a "NOTICE" text file as part of its distribution, then any 99 | Derivative Works that You distribute must include a readable copy of the 100 | attribution notices contained within such NOTICE file, excluding those notices 101 | that do not pertain to any part of the Derivative Works, in at least one of the 102 | following places: within a NOTICE text file distributed as part of the 103 | Derivative Works; within the Source form or documentation, if provided along 104 | with the Derivative Works; or, within a display generated by the Derivative 105 | Works, if and wherever such third-party notices normally appear. The contents of 106 | the NOTICE file are for informational purposes only and do not modify the 107 | License. You may add Your own attribution notices within Derivative Works that 108 | You distribute, alongside or as an addendum to the NOTICE text from the Work, 109 | provided that such additional attribution notices cannot be construed as 110 | modifying the License. 111 | You may add Your own copyright statement to Your modifications and may provide 112 | additional or different license terms and conditions for use, reproduction, or 113 | distribution of Your modifications, or for any such Derivative Works as a whole, 114 | provided Your use, reproduction, and distribution of the Work otherwise complies 115 | with the conditions stated in this License. 116 | 117 | 5. Submission of Contributions. 118 | 119 | Unless You explicitly state otherwise, any Contribution intentionally submitted 120 | for inclusion in the Work by You to the Licensor shall be under the terms and 121 | conditions of this License, without any additional terms or conditions. 122 | Notwithstanding the above, nothing herein shall supersede or modify the terms of 123 | any separate license agreement you may have executed with Licensor regarding 124 | such Contributions. 125 | 126 | 6. Trademarks. 127 | 128 | This License does not grant permission to use the trade names, trademarks, 129 | service marks, or product names of the Licensor, except as required for 130 | reasonable and customary use in describing the origin of the Work and 131 | reproducing the content of the NOTICE file. 132 | 133 | 7. Disclaimer of Warranty. 134 | 135 | Unless required by applicable law or agreed to in writing, Licensor provides the 136 | Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, 137 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, 138 | including, without limitation, any warranties or conditions of TITLE, 139 | NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are 140 | solely responsible for determining the appropriateness of using or 141 | redistributing the Work and assume any risks associated with Your exercise of 142 | permissions under this License. 143 | 144 | 8. Limitation of Liability. 145 | 146 | In no event and under no legal theory, whether in tort (including negligence), 147 | contract, or otherwise, unless required by applicable law (such as deliberate 148 | and grossly negligent acts) or agreed to in writing, shall any Contributor be 149 | liable to You for damages, including any direct, indirect, special, incidental, 150 | or consequential damages of any character arising as a result of this License or 151 | out of the use or inability to use the Work (including but not limited to 152 | damages for loss of goodwill, work stoppage, computer failure or malfunction, or 153 | any and all other commercial damages or losses), even if such Contributor has 154 | been advised of the possibility of such damages. 155 | 156 | 9. Accepting Warranty or Additional Liability. 157 | 158 | While redistributing the Work or Derivative Works thereof, You may choose to 159 | offer, and charge a fee for, acceptance of support, warranty, indemnity, or 160 | other liability obligations and/or rights consistent with this License. However, 161 | in accepting such obligations, You may act only on Your own behalf and on Your 162 | sole responsibility, not on behalf of any other Contributor, and only if You 163 | agree to indemnify, defend, and hold each Contributor harmless for any liability 164 | incurred by, or claims asserted against, such Contributor by reason of your 165 | accepting any such warranty or additional liability. 166 | 167 | END OF TERMS AND CONDITIONS 168 | 169 | APPENDIX: How to apply the Apache License to your work 170 | 171 | To apply the Apache License to your work, attach the following boilerplate 172 | notice, with the fields enclosed by brackets "[]" replaced with your own 173 | identifying information. (Don't include the brackets!) The text should be 174 | enclosed in the appropriate comment syntax for the file format. We also 175 | recommend that a file or class name and description of purpose be included on 176 | the same "printed page" as the copyright notice for easier identification within 177 | third-party archives. 178 | 179 | Copyright [yyyy] [name of copyright owner] 180 | 181 | Licensed under the Apache License, Version 2.0 (the "License"); 182 | you may not use this file except in compliance with the License. 183 | You may obtain a copy of the License at 184 | 185 | http://www.apache.org/licenses/LICENSE-2.0 186 | 187 | Unless required by applicable law or agreed to in writing, software 188 | distributed under the License is distributed on an "AS IS" BASIS, 189 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 190 | See the License for the specific language governing permissions and 191 | limitations under the License. 192 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # webaudio-controls 2 | 3 | **webaudio-controls** is a Javascript library for displaying the GUI parts required to Web Music applications. webaudio-controls consists of knobs, sliders, switches, parameter displays and keyboards. By loading `webaudio-controls.js` to your page, custom tags for component display will be added using WebComponents. You can configure the GUI screen just by writing custom tags in HTML. 4 | 5 | Chrome / Firefox / Edge compatible 6 | iOS and Android touch devices compatible 7 | 8 | ![](img/demo.png) 9 | 10 | ## [Documents and Application Notes](https://g200kg.github.io/webaudio-controls/docs/index.html) 11 | 12 | --- 13 | ## License 14 | WebAudio-Controls is developped based on: 15 | - [WebAudio-Knob](https://github.com/agektmr/webaudio-knob) by [Eiji Kitamura](http://google.com/+agektmr) 16 | - [WebAudio-Slider](https://github.com/ryoyakawai/webaudio-slider) by [Ryoya KAWAI](https://plus.google.com/108242669191458983485/posts) 17 | - [WebAudio-Switch](http://aikelab.net/switch/) by [Keisuke Ai](http://d.hatena.ne.jp/aike/) 18 | Integrated and enhanced by [g200kg](http://www.g200kg.com/) 19 | 20 | Copyright (c) 2013 Eiji Kitamura / Ryoya KAWAI / Keisuke Ai / g200kg / @micbuffa / @CellouBalde 21 | Licensed under the Apache License, Version 2.0 22 | 23 | --- 24 | 25 | Knob/Switch images in samples are from [Knob Gallery](http://www.g200kg.com/en/webknobman/gallery.php) 26 | [switch_toggle.knob](http://www.g200kg.com/en/webknobman/gallery.php?m=p&p=58) by [az](http://bji.yukihotaru.com/) (c) 2011 [CC-BY](http://creativecommons.org/licenses/by/3.0/) 27 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-midnight 2 | plugins: 3 | - jekyll-redirect-from -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "webaudio-controls", 3 | "authors": [ 4 | "Tatsuya Shinyagaito " 5 | ], 6 | "description": "GUI parts library for Web Music Apps", 7 | "main": "webaudio-controls.js", 8 | "keywords": [ 9 | "gui", 10 | "web", 11 | "music", 12 | "knob", 13 | "webcomponents" 14 | ], 15 | "license": "Apache License, Version 2.0", 16 | "homepage": "https://github.com/g200kg/webaudio-controls", 17 | "ignore": [ 18 | "**/.*", 19 | "node_modules", 20 | "bower_components", 21 | "test", 22 | "tests" 23 | ], 24 | "dependencies": { 25 | "webcomponentsjs": "webcomponents/webcomponentsjs#^1.1.0" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /docs/attributesexamples.md: -------------------------------------------------------------------------------- 1 | --- 2 | pageid: AttributesExamples 3 | --- 4 | 5 | 6 | 11 | 12 | 13 | 14 | {% include_relative gnavi.html %} 15 | 16 | --- 17 | 18 |
19 |
20 |


--- Events ---
21 |
22 | 23 |
24 | 25 | 26 | # Examples of Various Attributes 27 | 28 | You can customize the appearance and behavior of `webaudio-controls` by adding attributes to the tags. Here are some examples. 29 | 30 | 31 | 32 | ## webAudio-knob 33 | 34 | By default, the webaudio-knob tag looks like this. 35 | 36 | 37 | 38 | ```html 39 | 40 | ``` 41 | 42 | Examples of color variation by `colors` attribute. 43 | 44 | | | colors attribute 45 | |----|------------------------ 46 | ||`colors="#fc0;#000;#444"` 47 | ||`colors="#fc0;#8ab;#fff"` 48 | ||`colors="#08f;#800;#fff"` 49 | ||`colors="#fc0;#a0b;#fcc"` 50 | ||`colors="#f88;#400;#a00"` 51 | ||`colors="#888;#000;#0f0"` 52 | 53 | An external Knob image-file can be assigned by the `src` attribute. 54 | Also, specify the range of the value in `min`/`max`. The initial value is "60". The value is changed in `step` of "20". 55 | 56 | 58 | 59 | ```html 60 | 64 | 65 | ``` 66 | 67 | The knob image will be resized to specified size (even recommended to prepare required size image for clear display). 68 | 69 |
70 | 71 | ```html 72 | 75 | 76 | ``` 77 | 78 | Non-square image can be used with specifying `width`/`height` instead of diameter. 79 | 80 |
81 | 82 | ```html 83 | 87 | 88 | ``` 89 | 90 | `webaudio-knob` can has tooltip-text and editable parameter display field with `webaudio-param`. 91 | 92 | 93 |
94 | 95 | ```html 96 | 100 | 101 | 102 | ``` 103 | 104 | --- 105 | 106 | ## webAudio-slider 107 | 108 | If no slider image is provided by the `src` attribute, a simple built-in image will be used. 109 | 110 | The direction of the slider is automatically determined by specifying the `width` and `height`, but can also be specified explicitly with the `direction` attribute. 111 | 112 | If you don't specify the `width` and `height`, and you don't specify the `direction`, the default is 128 x 24px. The default is horizontal direction. ( This differs from older versions, but this is for compatibility with `` ). 113 | 114 | 115 | 116 | 117 | ```html 118 | 119 | 120 | 121 | 122 | ``` 123 | 124 | These are examples of color variation by `colors` attribute : 125 | 126 | | | colors attribute 127 | |----|---- 128 | ||`"#0f0;#000;#ff0"` 129 | ||`"#fc0;#8ab;#fff"` 130 | ||`"#08f;#800;#fff"` 131 | ||`"#fc0;#a0b;#fcc"` 132 | ||`"#f88;#400;#fcc"` 133 | ||`"#888;#000;#0f0"` 134 | 135 | If you specify a slider background image and a thumb image, it will look like this. 136 | 137 |
138 | 139 | ```html 140 | 143 | 144 | ``` 145 | 146 | Value step is specified, resized, tooltip-text and webaudio-param are added. 147 | 148 | 149 |
150 | 151 | ```html 152 | 158 | 159 | 160 | ``` 161 | 162 | --- 163 | 164 | ## webAudio-switch 165 | 166 | These are default `webaudio-switch`. If no switch image is specified, simple built-in image resource is used. 167 | Default switch type is "toggle". 168 | 169 | 170 | 171 | ```html 172 | 173 | ``` 174 | 175 | An example of color variation by `colors` attribute. 176 | 177 | | | colors attribute 178 | |----|---- 179 | ||`"#0f0;#000;#ff0"` 180 | ||`"#fc0;#8ab;#fff"` 181 | ||`"#08f;#800;#fff"` 182 | ||`"#fc0;#a0b;#fcc"` 183 | ||`"#f88;#400;#fcc"` 184 | ||`"#888;#000;#0f0"` 185 | 186 | 187 | This is an example when a background image file is specified by `src` attribute. If the size is not specified, the image will be displayed at its original size, but if the `width` and `height` are specified, the image will be resized. 188 | 189 | 190 | 191 | 192 | ```html 193 | 195 | 196 | 198 | 199 | ``` 200 | 201 | This is a 'kick' type switch. This switch is 'on' only while pressing. 202 | 203 | 204 | 205 | ```html 206 | 210 | 211 | ``` 212 | 213 | These are 'radio' type switches. In this mode, only one switch is activated in the group. A group is composed of those that match the `group` attribute. 214 | 215 | 216 | 217 |
218 | 219 | ```html 220 | 224 | 225 | 229 | 230 | 234 | 235 | ``` 236 | 237 | --- 238 | 239 | ## webaudio-keyboard 240 | 241 | This is a default keyboard. 25 keys, 480px X 128px is default. 242 | 243 | 244 | 245 | ```html 246 | 247 | ``` 248 | 249 | Number of keys, width and height settings. Also the lowest key is set to note "A" by the "min" attribute. 250 | 251 | 252 | 253 | ```html 254 | 256 | 257 | ``` 258 | 259 | Color customization by `colors` attribute. 260 | 261 | 262 | 263 | 264 | ```html 265 | 266 | 267 | ``` 268 | 269 | 270 | 271 | 272 | 273 | ```html 274 | 275 | 276 | ``` 277 | 278 | 279 | 280 | 281 | 282 | ```html 283 | 284 | 285 | ``` 286 | 287 | --- 288 | 289 | 352 | -------------------------------------------------------------------------------- /docs/creatingknobimages.md: -------------------------------------------------------------------------------- 1 | --- 2 | pageid: CreatingKnobImages 3 | --- 4 | 5 | 6 | 11 | 12 | 13 | 14 | {% include_relative gnavi.html %} 15 | 16 | --- 17 | 18 | ## Creating knob images 19 | webaudio-knob with sprites attribute is `0` use a single frame knob image that indicate center position. 20 | For example, 21 | 22 | ![](../img/testknob.png) 23 | 24 | This image will be rotated from -135deg to +135deg. This approach will works well if the image is flat designed, but more complex animation (for example, drop-shadowed, highlighted or something elastic) will need pre-rendered frame-by-frame animation as below. 25 | 26 | webaudio-knob (with non zero "sprites") use a vertical 'stitched' multi-frames animation image, and webaudio-switch use a vertical 'stitched' two-frames animation image. 27 | For example, 28 | 29 | ![](../img/LittlePhatty_sample.png) 30 | ![](../img/switch_toggle.png) 31 | 32 | This knob example has only 5 frames but it should has more frames for smooth animation. I recommend to use JKnobMan / WebKnobMan for making these stitched images, 33 | 34 | - [JKnobMan](http://www.g200kg.com/en/software/knobman.html) -- Java based Knob image creation tool. 35 | - [WebKnobMan](http://www.g200kg.com/en/webknobman/) -- WebApp version of the JknobMan 36 | - [KnobGallery](http://www.g200kg.com/en/webknobman/gallery.php) -- knob data sharing space 37 | 38 | --- 39 | 40 | Here is a brief instruction to export knob-image from KnobGallery 41 | 42 | - Go to [KnobGallery](http://www.g200kg.com/en/webknobman/gallery.php) 43 | - Find your favorite knob design and click 'Open with WebKnobMan' 44 | - Click on 'Export' to download `png` file 45 | - Of course, you can create your own! 46 | 47 | **Note: comply with license requirements** 48 | 49 | --- 50 | -------------------------------------------------------------------------------- /docs/defaultstyles.md: -------------------------------------------------------------------------------- 1 | --- 2 | pageid: DefaultStyles 3 | --- 4 | 5 | 6 | 11 | 12 | 13 | 14 | {% include_relative gnavi.html %} 15 | 16 | --- 17 | 18 | # Default Style of Controls 19 | 20 | This is a sample using the default styles of 21 | 'knob', 'param', 'slider', 'switch' and 'keyboard' without external image files. 22 | 23 | ```html 24 | 25 | 26 | 27 | 28 | 29 | ``` 30 | 31 | As each element is manipulated, events such as 'input' or 'change' are issued, 32 | so that the handlers can catch them. 33 | The captured events are displayed on the Events window on the right. 34 | 35 |
36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | <webaudio-knob> 51 | <webaudio-param> 52 | <webaudio-slider> 53 | <webaudio-switch> 54 | <webaudio-keyboard> 55 | toggle(default) 56 | kick 57 | radio 58 |
59 |
60 |
61 |
62 | 63 | --- 64 | 65 | 114 | -------------------------------------------------------------------------------- /docs/detailspecs.md: -------------------------------------------------------------------------------- 1 | --- 2 | pageid: DetailSpecs 3 | --- 4 | 5 | 6 | 11 | 12 | 13 | 14 | {% include_relative gnavi.html %} 15 | 16 | --- 17 | 18 | ## Attributes 19 | 20 | ### webaudio-knob 21 | 22 | 23 | 24 | Attribute | Options| Default| Description 25 | ----------------|--------|--------|------------ 26 | **src** | string | null | url of the knob image, Single frame or vertical stitched. Internal embedded resource is used if not specified. 27 | **value** | float | `0` | The current value. Also used as initial value if specified 28 | **defvalue** | float | Initial 'value' is used if not specified | The default value that will be used when ctrl+click 29 | **min** | float | `0` | Minimum value of the knob 30 | **max** | float | `100` | Maximum value of the knob 31 | **step** | float | `1` | Value step of the control. The 'value' is always rounded to multiple of 'step' 32 | **width** | int | `null` | Knob width in px. diameter value is used if this value is not specified. 33 | **height** | int | `null` | Knob height in px. diameter value is used if this value is not specified. 34 | **diameter** | int | `src` image size or `64` | Knob diameter in px. This attribute can be used instead of width / height if the display image is square. If no `width` `height` `diameter` is specified, the `src` image size is used as is. In addition, 64px square embedded resource will be used if `src` is also not specified. 35 | **sprites** | int | `null` | If `0`, the `src` image should be single frame knob image that indicate middle position. the image will be rotated -135deg to +135deg.
If `sprites` is 1 or more, the `src` image should be vertically stitched multi-framed image. `sprites` specify the max frame number in the stitched knob image. Note that this is (number of frames) - 1.
If `sprites` attribute is not specified (default), assuming one knob image is a square, it will be calculated from the width and height of the image specified in `src`, i.e. height / width - 1 will be used as the `sprites`. 36 | **sensitivity** | float | `1` | Pointing device sensitivity. min-max range correspond to (128 / `sensitivity`) px 37 | **log** | int | `0` | If this value is set to 1, then the value change is logarithmic. For example, if `min="10"` and `max="1000"`, then the center value of the knob will be 100. Here, if `min` is 0 or negative, an error occurs. If `log` is set to 0, then the change in value is linear. 38 | **valuetip** | `0`
`1`| `0` | Enable the overlaid value-tip display. This is equivalent to `tooltip="%s"`. 39 | **tooltip** | string | `null` | Specifies the tooltip text that appears when you hover the mouse cursor for a while. If the text contains "%s", it will be replaced with the current value. Also, if the `conv` attribute is specified, the converted current value, `convValue` is used for display. 40 | **conv** | string | `null` | If this attribute is specified, That string will be evaluated as an expression and stored to `convValue` as a string. This `convValue` will be used for the valuetip, tooltip and the linked webaudio-param display. In this expression, the `x` will represent current `value`. For example, `conv="(Math.pow(10,x)*20).toFixed(0)"` is specified, for range of value between 0 and 3, the range of convValue corresponds to 20 to 20000. As another example, if `conv="['sin','saw','sqr','tri'][x]"` is specified, for values from 0 to 3, each string are assigned respectively. 41 | **enable** | `0`
`1`| `1` | Enable control with the pointing device. 42 | **colors** | string | "#e00;#000;#fff" | Semicolon separated 3 colors for 'indicator', 'body' and 'highlight'. These colors are used in default knob (when `src` is not provided). 43 | **outline** | `0`
`1`
string| `0` | Border style when focused. `0`:no outline. `1`:equivalent to `"1px solid #ccc"`. Any other string will be applied to the style's outline attribute only when it has focus. 44 | **midilearn** | `0`
`1`| `0` | If `1`, MIDI learn function with right-click menu is enabled. 45 | **midicc** | string | null | Assign MIDI control change to this knob, with format `ch.cn`, here the `ch` is channel (1-16, ignore channel if 0) and `cn` is control number (0-119). 46 | 47 | ### webaudio-slider 48 | 49 | 50 | 51 | Attribute | Options| Default | Description 52 | ----------------|--------|---------|------------ 53 | **src** | string | `null` | url of the slider background image. Solid background color is used if not specified. If the empty string "" is specified, the background will not be drawn and will be transparent. 54 | **knobsrc** | string | `null` | url of the slider knob part image. Internal embedded resouce is used if not specified. 55 | **value** | float | `0` | The current value. Also used as initial value if specified. 56 | **defvalue** | float | Initial 'value' is used if not specified | The default value that will be used when ctrl+click. 57 | **min** | float | `0` | Minimum value of the slider. 58 | **max** | float | `100` | Maximum value of the slider. 59 | **step** | float | `1` | Value step of the control. The 'value' is always rounded to multiple of 'step'. 60 | **width** | int | `src` image width or `128` | Slider display width in px. If not specified, the image width specified by the `src` attribute will be used as is. In addition, if no `src` is specified, it will be 128. 61 | **height** | int | `src` image height or `24` | Slider display height in px. If not specified, the image height specified by the `src` attribute will be used as is. In addition, if no `src` is specified, it will be 24. 62 | **knobwidth** | int | same as 'width' if 'direction' is `vert`, or same as 'height' if 'direction' is `horz` | Slider knob part width in px. 63 | **knobheight** | int | same as 'width' if 'direction' is `vert`, or same as 'height' if 'direction' is `horz` | Slider knob part height in px. 64 | **ditchlength** | int | ('height'-'knobheight') or ('width'-'knobwidth') depends on 'direction' | Knob movable length. 65 | **direction** | `"vert"`
`"horz"`| `null` | Slider direction. vertical or horizontal. If not specified, direction is automatically determined to the longer direction by the `width` and `height`. 66 | **tracking** | `"rel"`
`"abs"`| `"rel"` | If 'abs', the slider follows 1:1 to the position of the pointing device. In this mode, sensitivity is ignored. Otherwise, the slider will move by the offset you dragged the pointing device. 67 | **sensitivity** | float | `1` | Pointing device sensitivity. min-max range correspond to (128 / 'sensitivity') px. 68 | **log** | int | `0` | If this value is set to 1, then the value change is logarithmic. For example, if `min="10"` and `max="1000"`, then the center value of the knob will be 100. Here, if `min` is 0 or negative, an error occurs. If `log` is set to 0, then the change in value is linear. 69 | **valuetip** | `0`
`1`| `0` | Enable the overlaid value-tip display. 70 | **tooltip** | string | `null` | Tooltip text that will be shown when mouse hover a while. If the text include a C-printf style value formatter like `%8.2f`, it will be replaced by current value. This formatter should be `%[n][.][m]{d,f,x,X,s}`. Here the 'n' is total columns, 'm' is after the decimal point columns. If the `conv` function is specified, the converted value `convValue` is used for display. 71 | **conv** | string | `null` | If this attribute is specified, That string will be evaluated as an expression and stored as `convValue` as a string. This `convValue` will be used for the valuetip, tooltip and the linked webaudio-param display. In this expression, the `x` will represent current `value`. For example, `conv="(Math.pow(10,x)*20).toFixed(0)"` is specified, for range of value between 0 and 3, the range of convValue corresponds to 20 to 20000. As another example, if `conv="['sin','saw','sqr','tri'][x]"` is specified, for values from 0 to 3, each string are assigned respectively. 72 | **enable** |`0`
`1`| `1` | Enable control with the pointing device. 73 | **colors** | string | "#e00;#000;#fff" | Semicolon separated 3 colors for 'knob', 'background' and 'highlight'. These colors are used in default knob (when `src` or `knobsrc` is not provided). 74 | **outline** | `0`
`1`
string| `0` | Border style when focused. `0`:no outline. `1`:equivalent to `"1px solid #ccc"`. Any other string will be applied to the style's outline attribute only when it has focus. 75 | **midilearn** | `0`
`1`| `0` | If `1`, MIDI learn function with right-click menu is enabled. 76 | **midicc** | string | null | Assign MIDI control change to this slider. with format `ch.cn`, here the `ch` is channel (1-16, ignore channel if 0) and `cn` is control number (0-119). 77 | 78 | 79 | ### webaudio-switch 80 | 81 | 82 | 83 | Attribute | Options | Default | Description 84 | ----------------|-----------|---------|------------ 85 | **src** | string | Internal embedded resource is used if not specified | url of the vertical stitched switch image. 86 | **value** | `0`
`1` | `0` | The current value (`0` or `1`). Also used as initial value of the switch if specified. 87 | **defvalue** | `0`
`1` | Initial 'value' is used if not specified | The default value that will be used when ctrl+click. 88 | **width** | int | `src` image width or `32` | Switch display width in px. If not specified, the image width specified by the `src` attribute will be used as is. In addition, if no `src` is specified, it will be 32. 89 | **height** | int | `src` image height * 0.5 or `32` | Switch display height in px. If not specified, the half the image height specified by the `src` attribute will be used as is. In addition, if no `src` is specified, it will be 32. 90 | **diameter** | int | `null` | Switch diameter in px. This attribute can be used instead of width / height if the display image is square. If no `width` `height` `diameter` is specified, the switch size is calculated form the `src` image size. In addition, 32px square embedded resource will be used if `src` is also not specified. 91 | **type** | `"toggle"`
`"kick"`
`"radio"` | `"toggle"` | Switch type. `"toggle"` switch has so-called 'checkbox' function. `"radio"` switch is a radio-button and the `"kick"` switch is a general command button. 92 | **group** | string | `null` | Group id string used if the 'type' is `"radio"`. Only one switch will be set to `"1"` in same group. 93 | **invert** | `0`
`1` | `0` | exchange on and off image. 94 | **tooltip** | string | `null` | Tooltip text that will be shown when mouse hover a while. 95 | **enable** | `0`
`1` | `1` | Enable control with the pointing device. 96 | **colors** | string | "#e00;#333;#fff" | Semicolon separated 3 colors for 'knob', 'background' and 'highlight'. These colors are used in default switch (when `src` is not provided). 97 | **outline** | `0`
`1`
string| `0` | Border style when focused. `0`:no outline. `1`:equivalent to `"1px solid #ccc"`. Any other string will be applied to the style's outline attribute only when it has focus. 98 | **midilearn** | string | null | If `true`, MIDI learn function with right-click menu is enabled. 99 | **midicc** | string | null | Assign MIDI control change to this switch. with format `ch.cn`, here the `ch` is channel (1-16, ignore channel if 0) and `cn` is control number (0-119). 100 | 101 | ### webaudio-param 102 | 103 | 104 | 105 | Attribute | Options | Default | Description 106 | ---------------|---------|---------|------------ 107 | **src** | string | Black rectangle if not specified | Background image or color. Transparent if set to `""`, or url to background image. 108 | **value** | float | `0` | The current value. Usually same as linked control 109 | **width** | int | `src` image width or `32` | Parameter display width in px. If not specified, the image width specified by the `src` attribute will be used as is. In addition, if no `src` is specified, it will be 32. 110 | **height** | int | `src` image height or `20` | Parameter display height in px. If not specified, the image height specified by the `src` attribute will be used as is. In addition, if no `src` is specified, it will be 20. 111 | **fontsize** | int | `9` | Font-size of the parameter display. 112 | **colors** | string | `"#fff;#000"` | Semicolon separated 2 colors for text and background. background color is used when `src` is not defined. 113 | **outline** | `0`
`1`
string| `0` | Border style when focused. `0`:no outline. `1`:equivalent to `"1px solid #ccc"`. Any other string will be applied to the style's outline attribute only when it has focus. 114 | **link** | string | `null` | Specify the linked webaudio-knob/slider/switch by Id. 115 | **rconv** | string | `null` | Specify the reverse conversion of target's `conv`. It is needed if the target knob/slider use conversion by `conv` attribute and the user edit the `webaudio-param` value directory by keyboard. The reverse converted value will be set to linked target. For example, when the target knob/slider use `conv="Math.pow(10,x)*20"` attribute, this `webaudio-param` should be `rconv="Math.log10(x/20)"`. 116 | 117 | ### webaudio-keyboard 118 | 119 | 120 | 121 | Attribute | Options | Default| Description 122 | -----------|-----------|--------|------------ 123 | **values** | int array | `[]` | The array of current pressed key numbers. "values" may has more than one element in multi-touch environment. 124 | **width** | int | `480` | Keyboard display width in px 125 | **height** | int | `128` | Keyboard display height in px 126 | **min** | int | `0` | Lowest Key number. Each key is numbered incrementally from this number. If the "min" is not `0` and the modulo 12 is not zero, the keyboard is started from corresponding position (not-C). Note that the specified key should be a 'white-key'. 127 | **keys** | int | `25` | Number of keys. `25` means 25 keys keyboard. 128 | **colors** | string | '#222; #eee;#ccc; #333;#000; #e88;#c44; #c33;#800' | semicolon separated 9 keyboard colors. 'border; whitekey-grad-from;whitekey-grad-to; blackkey-grad-from;blackkey-grad-to; active-whitekey-grad-from;active-whitekey-grad-to; active-blackkey-grad-from;active-blackkey-grad-to'. Each key surface can has garadient left to right with 'from' and 'to'. 129 | **outline** | `0`
`1`
string| `0` | Border style when focused. `0`:no outline. `1`:equivalent to `"1px solid #ccc"`. Any other string will be applied to the style's outline attribute only when it has focus. 130 | **enable** | `0`
`1` | `1` | Enable control with the pointing device. 131 | 132 | --- 133 | ## Functions 134 | 135 | ### setValue(value, fire) 136 | `webaudio-knob` | `webaudio-slider` | `webaudio-switch` 137 | **description**: Each control can be setup and redraw by calling this function from JavaScript. 138 | If the `fire` parameter is `undefined` or `false`, this function will not fire `'change'` event. Or the `change` event will be fired. 139 | 140 | 141 | ### setNote(state, note [,audioContext, when]) 142 | `webaudio-keyboard` 143 | **description**: webaudio-keyboard can be setup pressing state with this function from JavaScript. corresponding key specified by the `note` is pressed if the `state` is non-zero otherwise the key is released. This function will NOT fire the 'change' event. If the `audioContext` and `when` arguments are specified, the pressing state will be updated after the specified time. `when` is the time in seconds on the `currentTime` time axis of the `audioContext`. 144 | 145 | --- 146 | ## Events 147 | 148 | ### 'input' 149 | `webaudio-knob` | `webaudio-slider` 150 | **description**: 'input' event is fired when knob / slider value changes while dragging. 151 | 152 | ### 'change' 153 | `webaudio-knob` | `webaudio-slider` | `webaudio-switch` | `webaudio-keyboard` 154 | **description**: `change` event is fired when value changes is decided. It means mouse button release for knobs and sliders, or switch / keyboard state changes. 155 | Also issued when setValue() function call with fire flag is nonzero. 156 | In the event handler of `webaudio-knob`,`webaudio-slider` or `webaudio-switch`, current value can be get with referring `event.target.value`. 157 | 158 | ```js 159 | var knobs = document.getElementsByTagName('webaudio-knob'); 160 | for (var i = 0; i < knobs.length; i++) { 161 | var knob = knobs[i]; 162 | knob.addEventListener('change', function(e) { 163 | console.log(e.target.value); 164 | }); 165 | } 166 | ``` 167 | 168 | For the `webaudio-keyboard`, each 'change' event has the property '.note' that contain a array `[key-state, key-number]`. For example `event.note = [1, 60]` if the key#60 is on, or `event.note = [0, 60]` if the key#60 is off. 169 | 170 | ```js 171 | var keyboard = document.getElementsById('keyboard'); 172 | keyboard.addEventListener('change', function(e) { 173 | if(e.note[0]) 174 | console.log("Note-On:"+e.note[1]); 175 | else 176 | console.log("Note-Off:"+e.note[1]); 177 | }); 178 | ``` 179 | 180 | ### 'click' 181 | `webaudio-switch (kick)` 182 | **description**: 'click' event is emitted if the 'kick' type webaudio-switch has clicked. 183 | 184 | --- 185 | -------------------------------------------------------------------------------- /docs/determiningknobsize.md: -------------------------------------------------------------------------------- 1 | --- 2 | pageid: DeterminingKnobSize 3 | --- 4 | 5 | 6 | 11 | 12 | 13 | 14 | {% include_relative gnavi.html %} 15 | 16 | --- 17 | 18 | # Determining Knob Size 19 | 20 | There are several factors that determine the knob size. The following factors have higher priority from the top and are checked in order. 21 | 22 | Note that the `width` / `height` specification in the style sheet does not affect the size of the knob display, although the outline of the element changes. 23 | 24 | - `width`, `height` attributes of the element 25 | - `diameter` attribute of the element 26 | - `knobWidth`, `knobHeight` specified by `WebAudioControlsOptions` 27 | - `knobDiameter` specified by `WebAudioControlsOptions` 28 | - Image size specified by `src` 29 | - Default size (64px x 64px) 30 | 31 | --- 32 | ## Knob 33 | 34 | 35 |
36 | 37 | The default size (64 x 64px) is used if there is no other size factor. 38 | ```html 39 | 40 | ``` 41 | 42 | --- 43 | 44 | 45 |
46 | 47 | If the `diameter` is specified, the knob will be that size 48 | ```html 49 | 50 | ``` 51 | 52 | --- 53 | 54 | 55 |
56 | 57 | When `width` and `height` attributes are specified individually. 58 | ```html 59 | 60 | ``` 61 | 62 | --- 63 | 64 | 65 | 66 |
67 | 68 | If a `src` image is specified and nothing else is provided, the original size of the image is used. 69 | At this time, `sprites` are automatically calculated assuming that one frame of the knob image is square. 70 | ```html 71 | 72 | ``` 73 | 74 | --- 75 | 76 | 77 |
78 | 79 | If `src` and `diameter` is specified, the original image will be resized. 80 | ```html 81 | 82 | 83 | ``` 84 | 85 | --- 86 | 87 | 90 | 91 |
92 | 93 | When width height is specified individually with src. 94 | ```html 95 | 98 | 99 | ``` 100 | 101 | --- 102 | 103 | 104 |
105 | 106 | If one frame of the knob image is not square, the `sprites` attribute should be set to (number of frames - 1). 107 | ```html 108 | 109 | 110 | ``` 111 | 112 | --- 113 | 114 | ## Slider 115 | 116 | 117 |
118 | 119 | The default size (24 x 128px) is used if there is no other size factor. 120 | ```html 121 | 122 | ``` 123 | 124 | --- 125 | 126 | 128 | 129 |
130 | 131 | If `width` and `height` is specified, it will be the specified size. At this time, the direction of the slider is automatically determined to the longer direction of width or height. 132 | ```html 133 | 134 | 135 | ``` 136 | 137 | --- 138 | 139 | 141 | 142 |
143 | 144 | When the `src` image is specified and `width` `height` is not specified, the size of the original `src` image is used. 145 | ```html 146 | 147 | 148 | ``` 149 | 150 | --- 151 | 152 | 153 |
154 | 155 | When `width` `height` is specified with the `src` image, the `src` image is resized. 156 | ```html 157 | 160 | 161 | ``` 162 | 163 | --- 164 | 165 | 166 |
167 | 168 | `knobwidth` and `knobheight` specify the size of thumb. If these are not specified, a square whose side is the smaller of `width` or `height` will be used. 169 | ```html 170 | 173 | 174 | ``` 175 | 176 | --- 177 | 178 | 179 |
180 | 181 | If the thumb size by `knobwidth` and `knobheight` are not specified but the image is served by `knobsrc`, the original size of the `knobsrc` image will be used. 182 | ```html 183 | 186 | 187 | ``` 188 | 189 | --- 190 | 191 | 192 |
193 | 194 | Specifying `ditchlength` can set the movement range of the thumb. 195 | ```html 196 | 201 | 202 | ``` 203 | 204 | --- 205 | 206 | 207 |
208 | 209 | Setting src to the empty string "" will stop drawing the background and draw only the thumb. 210 | ```html 211 | 217 | 218 | ``` 219 | 220 | --- 221 | 222 | ## Switch 223 | 224 | 225 |
226 | 227 | If no size is specified such as `width`, `height`, it will be displayed in the default size 32 x 32px. 228 | ```html 229 | 230 | ``` 231 | 232 | --- 233 | 234 | 235 |
236 | 237 | If `width` and `height` are specified, it will be displayed in the specified size. 238 | ```html 239 | 240 | 241 | ``` 242 | 243 | --- 244 | 245 | 246 |
247 | 248 | `diameter` attribute specify `width` and `height` together. 249 | ```html 250 | 251 | 252 | ``` 253 | 254 | --- 255 | 256 | 257 | 258 |
259 | 260 | If the `src` attribute specifies an `image`, and `width`, `height`, and `diameter` are not specified, the original image size is used. 261 | ```html 262 | 263 | 264 | ``` 265 | 266 | --- 267 | 268 | 269 |
270 | 271 | If the `src` image and size are specified, the `src` image will be resized to the specified size. 272 | ```html 273 | 274 | 275 | ``` 276 | 277 | --- 278 | 279 | ## Param 280 | 281 | 282 |
283 | 284 | If no size is specified such as `width`, `height`, it will be displayed in the default 32 x 20px. 285 | ```html 286 | 287 | ``` 288 | 289 | --- 290 | 291 | 292 |
293 | 294 | If `width` and `height` are specified, it will be displayed in the specified size. 295 | ```html 296 | 297 | 298 | ``` 299 | 300 | --- 301 | 302 | 303 |
304 | 305 | The font size can be specified with the `fontsize` attribute. 306 | ```html 307 | 310 | 311 | ``` 312 | 313 | --- 314 | 315 | 316 |
317 | 318 | If the `src` image is specified but no `width` and `height` is provided, the original size of the `src` image is used. 319 | ```html 320 | 322 | 323 | ``` 324 | 325 | --- 326 | 327 | 328 |
329 | 330 | If `src` image, `width`, `height` is specified, the `src` image is resized to specified size. 331 | ```html 332 | 336 | 337 | ``` 338 | 339 | --- 340 | 341 | 342 |
343 | 344 | If `src` is empty string `""`, background is transparent. 345 | ```html 346 | 347 | 348 | ``` 349 | 350 | --- 351 | 352 | 353 | 354 | 355 | -------------------------------------------------------------------------------- /docs/docstyle.css: -------------------------------------------------------------------------------- 1 | .gnavi{ 2 | display:flex; 3 | width:100%; 4 | flex-wrap:wrap; 5 | margin:-10px 0px 30px 40px; 6 | padding:0; 7 | } 8 | .gnavi li{ 9 | background:#000; 10 | margin:2px 14px; 11 | padding:1px 10px; 12 | } 13 | .gnavi li.active{ 14 | background:#600; 15 | border:1px solid #966; 16 | } 17 | -------------------------------------------------------------------------------- /docs/gnavi.html: -------------------------------------------------------------------------------- 1 | Basic Usage : 2 | 12 | Advanced Usage and Application Notes : 13 | 23 | 24 | -------------------------------------------------------------------------------- /docs/howitworks.md: -------------------------------------------------------------------------------- 1 | --- 2 | pageid: HowItWorks 3 | --- 4 | 5 | 6 | 7 | 12 | 13 | 14 | 15 | {% include_relative gnavi.html %} 16 | 17 | --- 18 | 19 | # How each Component Works 20 | 21 | ## webAudio-knob 22 | 23 | webaudio-knob is used to create a rotating knob. 24 | Knobs are displayed in the default state by adding tags as follows on HTML. 25 | 26 | 27 | 32 | 33 | 34 | ```html 35 | 36 | 41 | ``` 42 | 43 | 44 | `` and `` are similar in behavior to the `` tag. It has a value in the specified range and can be changed by user operation. 45 | 46 | The events are issued by operating this knob with a mouse or touch device, so you can access it through Javascript by assigning ids like normal DOM elements. 47 | 48 | Also if you assign a external filmstrip-type image file to a `` with the `src` attribute, you can create an frame-by-frame animated knobs that changes not only rotation but changes shapes or colors as the value changes. 49 | 50 |
51 | 52 | 53 | ```html 54 | 55 | ``` 56 | 57 | --- 58 | 59 | ## webaudio-slider 60 | 61 | The `` is similar to the ``, but it is a slider that thumb moves horizontally or vertically, rather than a rotary knob. 62 | 63 | Horizontal or vertical is automatically determined by the `width` and `height` settings. Event handling is the same as for `` 64 | 65 | When specifying the image file, set the background part and the thumb part separately, then the thumb part will move horizontally or vertically on the background part. 66 | 67 |

68 |
69 | 70 | ```html 71 | 72 | 73 | 76 | 77 | ``` 78 | 79 | --- 80 | 81 | ## webaudio-switch 82 | 83 | The `` is a switch with an on/off state and comes in three types: toggle (checkbox), kick (`on` while pressed), and radio button (only one of the button in the group can be `on`). 84 | 85 | Each type is specified by the `type` attribute. In the case of a `radio` button, a group is created using the `group` attribute. 86 | 87 |
88 | 89 | toggle(default)
90 | kick
91 | 92 | 93 | radio
94 | 95 | 106 | 107 | ```html 108 | 109 | toggle(default)
110 | 111 | 112 | kick
113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 132 | ``` 133 | 134 | As for the event, `"change"` is issued when the state changes and `"click"` is issued when the user clicks on it. 135 | 136 | If you want to use an image file, you can set the `src` attribute to an image that is vertically stitched in the off and on states like: 137 | 138 | 139 | 140 |
141 | Then you will see the following:
142 | 143 | 144 | 145 | ```html 146 | 147 | ``` 148 |
149 | 150 | --- 151 | 152 | ## WebAudio-Param 153 | 154 | `` is a small text field used to display the current value of `` or ``. 155 | Additionally, it allows manipulating the knob or slider value by editing this text field. 156 |
157 | The basic behavior of `` does not require any Javascript program; it will work automatically if the `link` attribute is set to the target knob or slider's id. 158 |
159 | 160 | 161 | 162 | 163 | ```html 164 | 165 | 166 | ``` 167 | 168 |
169 | 170 | 171 | 172 | 173 | ```html 174 | 175 | 176 | ``` 177 | 178 |
179 | 180 | --- 181 | 182 | ## webaudio-keyboard 183 | 184 | `webaudio-keyboard` is a tag for creating a music keyboard. It can be played with a mouse or touch device, and also supports multi-touch. 185 | 186 | The default display looks like this: 187 | 188 | 189 | 194 | 195 | ```html 196 | 197 | 198 | 203 | ``` 204 | 205 | About event behavior, a `"change"` event is issued when a key is pressed/released. 206 | This `"change"` event is accompanied by a `note` property, which holds the key on/off state and note number. (Note that the `note` property is attached to the event object, not the webaudio-keyboard element.) 207 | 208 | You can specify the `width`, `height` and number of `keys`. In addition, the note of the lowest `key` can be specified with `min` attribute. 209 | 210 | 211 | 212 | 213 | ```html 214 | 215 | 216 | ``` 217 | 218 | --- 219 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | pageid: Overview 3 | --- 4 | 5 | 6 | 7 | 12 | 13 | 14 | 15 | {% include_relative gnavi.html %} 16 | 17 | --- 18 | 19 | # Overview 20 | 21 | **webaudio-controls** is a Javascript library to display the GUI components used in Web Music Applications. 22 | webaudio-controls consists of knobs, sliders, switches, parameter displays and keyboards. 23 | By loading `webaudio-controls.js` to your page, custom tags for component display will be added using WebComponents. 24 | You can configure the GUI screen just by writing custom tags in HTML. 25 | 26 | **webaudio-controls** is consist of following components. 27 | 28 | Component | Description 29 | ------------------|------------ 30 | webaudio-knob | Rotating or some other frame-by-frame animation knobs. 31 | webaudio-slider | Vertical or Horizontal slidesrs. 32 | webaudio-switch | Toggle / Kick / Radio switches. 33 | webaudio-param | Editable value display field that can auto-link to knobs or sliders. 34 | webaudio-keyboard | Mouse / Touch playable keyboard. multi-touch support. 35 | 36 | Chrome / Firefox / Edge compatible 37 | iOS and Android touch devices compatible 38 | 39 | This is an example of a typical GUI screen created using webaudio-controls. Knobs, sliders, switches etc. can be manipulated with a mouse or touch device. It is also possible to operate from a connected MIDI controller. 40 | 41 | Each component fires an event when manipulated and can be handled by a javascript program. 42 | 43 |
44 |
45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 |
54 |
55 | 56 | ## Source Code is Available At 57 | 58 | GitHub Repository : [https://github.com/g200kg/webaudio-controls](https://github.com/g200kg/webaudio-controls) 59 | 60 |
61 | 62 | ## To Operate 63 | 64 | Following user actions are supported. 65 | 66 | Operation | Component | Description 67 | ---|---|--- 68 | **Click** | Switch
Other | Switch : Toggle / activate the switch.
Other : Focus the component. 69 | **Drag** | Knob
Slider | Up/Right to increase value
Down/Left to decrease value. 70 | **Shift+Drag** | Knob
Slider | Fine control. Increase or decrease by the value specified in the `step`. 71 | **Ctrl+Click
Command+Click(Mac)** | Knob
Slider
Switch | Set to default value. 72 | **Keyboard** | Knob
Slider
Param
Keyboard | To manipulate with the keyboard, it is necessary to get the focus by clicking each component once.

Knob/Slider : ArrowUp/ArrowDown to increase or decrease by the value specified in the `step`.
Param : Edit the param value directly.
Keyboard : [ZSXDCV...for lowest visible 'C' octave] and [Q2W3E... one octave higher] as a music keyboard. 73 | **MouseWheel** | Knob
Slider | Rotate upward to increase value, downward to decrease value. 74 | **Shift+MouseWheel** | Knob
Slider | Fine control. Increase or decrease by the value specified in the `step`. 75 | **Mouse Button Press
Touch** | Keyboard | Play keyboard. multi-touch is supported. 76 | **R-Click** |Knob
Slider
Switch | Open MIDI learn menu. 77 | 78 |
79 | 80 | ## License 81 | webaudio-controls is based on: 82 | - WebAudio-Knob by Eiji Kitamura 83 | - WebAudio-Slider by Ryoya Kawai 84 | - WebAudio-Switch by Keisuke Ai 85 | - Integrated and enhanced by g200kg 86 | 87 | Copyright (c) 2013 Eiji Kitamura / Ryoya KAWAI / Keisuke Ai / g200kg / @micbuffa / @CellouBalde 88 | Licensed under the Apache License, Version 2.0 89 | 90 | --- 91 | 92 | Knob/Switch images are from Knob Gallery
93 | switch_toggle.knob by az Copyright (c) 2011 CC-BY 94 | 95 | --- 96 | 97 | 106 | -------------------------------------------------------------------------------- /docs/install.md: -------------------------------------------------------------------------------- 1 | --- 2 | pageid: Install 3 | --- 4 | 5 | 6 | 11 | 12 | 13 | 14 | {% include_relative gnavi.html %} 15 | 16 | --- 17 | 18 | # Install 19 | 20 | - **webaudio-controls.js** 21 | - Place "webaudio-controls.js" to an appropriate directory.
This is the only file needed. There are no dependencies on other libraries. 22 | 23 | [webaudio-controls.js](https://raw.githubusercontent.com/g200kg/webaudio-controls/master/webaudio-controls.js) 24 | 25 | - **WebComponents polyfill** 26 | - If you want to support legacy browsers that not support WebComponents, the polyfill for WebComponents is needed : 27 | `````` 28 | 29 | - **load webaudio-controls** 30 | - `````` 31 | Or, if you want to load webaudio-controls.js directly from this GitHub page as CDN : 32 | - `````` 33 | 34 | - **insert webaudio-knob / slider / switch / param / keyboard elements.** 35 | - `` 36 | - `` 37 | - `` 38 | - `` 39 | - `` 40 | 41 | --- 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | --- 50 | -------------------------------------------------------------------------------- /docs/knobgallery.md: -------------------------------------------------------------------------------- 1 | --- 2 | pageid: KnobGallery 3 | --- 4 | 5 | 6 | 11 | 12 | 13 | 14 | {% include_relative gnavi.html %} 15 | 16 | --- 17 | 18 | 41 | 42 | # Knob Samples from KnobGallery 43 | 44 | These knobs are randomly picked up from the "knob gallery". 45 | 46 | Knob Gallery 47 | 48 | "Knob Gallery" is a shared space where a lot of knob image data is aggregated. Anyone can upload data with a public domain or Creative Commons license. 49 | 50 | 69 |
70 | 71 | These knobs are published by following people's efforts. 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 |
g200kgAqua / Vernier / lineShadow / lineShadow2
aikem400 / plastic_knob
Sasha RadojevicCarbon / JP8000 / LittlePhatty
Paule AmcaJambalaya
Mike RomansMiniMoog_Main
RedLeadsAudioSimpleFlat3
kuroneko_0622Vintage_Knob / Vintage_VUMeter
AZknob_metal_mesh (CC BY-NC 3.0)
www.WOK.de.toWOK_vintage_AbbeyRoad_PAN_Knob (CC BY-NC 3.0)
tonebytesnice_lamp_knob
85 | 86 | 87 | --- 88 | -------------------------------------------------------------------------------- /docs/midisupport.md: -------------------------------------------------------------------------------- 1 | --- 2 | pageid: MidiSupport 3 | --- 4 | 5 | 6 | 12 | 13 | 14 | 15 | {% include_relative gnavi.html %} 16 | 17 | --- 18 | 19 | # MIDI Support 20 | 21 | **knobs, sliders and switches have midilearn/midicc support built-in** 22 | 23 |
24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 |
34 |
35 | 36 | To enable MIDI related functions, specify `useMidi:1` in `WebAudioControlsOptions` before loading `webaudio-controls.js`. 37 | In addition, define `preserveMidiLearn:1` to retain the result of MIDI learn on next access. If you do not define this, the results of MIDI learn will be discarded when reloaded. 38 | 39 | Technically speaking, the results of MIDI learn are kept in WebAudioControlsMidiLearn in localStorage. If you want to force discard the result of MIDI learn, remove it from your Javascript program. 40 | 41 | ```html 42 | 46 | 47 | ``` 48 | 49 | **Midilearn right click menu** : 50 | Add a `midilearn=1` attribute to the ``, `` and `` elements. Then right click on the element in the GUI, a midi learn menu should appear. Then, operate one of your midi controller and it should start actionning the webaudio-controls widget in the HTML page. You can hot plug/unplug midi devices, they will be detected. 51 | 52 | ![Midi Learn Menu](../img/midilearn.png) 53 | 54 | **Declarative association between a midi controller and a GUI webaudio-controls** : 55 | There is also an HTML `midicc="channel.cc#"` attribute that works like this: 56 | `midicc="3.2"` means "listen to a CC event on channel 3, CC number 2". If you don't know the channel/CC number of your controller: 57 | 1) add a `midilearn=1` attribute so that a right click on the GUI widget will display the midilearn menu, 58 | 2) select "learn" in the menu, 59 | 3) operate your knob/slider/switch, normally the midi controller and the GUI object are in sync. 60 | 4) look at the devtool console, there is a message indicating the channel and CC number, for example "channel 0, cc 28". Then if you add the attribute `midicc="0.28"` to the HTML of your knob/slider/switch, the midi mapping between your GUI webaudio-controls and your midi controller will be automatic. Follow the links at the end of this section and look at the HTML source code to see some examples. 61 | 62 | Example: associate a knob with a controller on channel 7, cc number 7: 63 | 64 | ```html 65 | 66 | ``` 67 | 68 | **External midi event listener (hook)** : 69 | You can also declare in your HTML file your own midi event listener (for example for listening to program changes events): use the `webAudioControlsWidgetManager` object (Formerly known as `webAudioControlsMidiManager`, it is also available under this name), that comes with an `addMidiListener` method. Like that you will benefit from the MIDI code included in the webaudio-controls. Here is an example (also, look at the source code of this page, and open the devtool console to see midi messages received by the hook at the end of the HTML file). 70 | 71 | ```html 72 | 87 | ``` 88 | 89 | Demo at: 90 | [https://wasabi.i3s.unice.fr/AmpSimFA/](https://wasabi.i3s.unice.fr/AmpSimFA/) 91 | and at [https://wasabi.i3s.unice.fr/AmpSimFA/sample1.html](https://wasabi.i3s.unice.fr/AmpSimFA/sample1.html) 92 | 93 | 94 | --- 95 | 96 | -------------------------------------------------------------------------------- /docs/multitouch.md: -------------------------------------------------------------------------------- 1 | --- 2 | pageid: MultiTouch 3 | --- 4 | 5 | 6 | 16 | 17 | 18 | 19 | {% include_relative gnavi.html %} 20 | 21 | --- 22 | 23 | 39 | 40 | 41 | # Multi-Touch Device Support 42 | 43 | **webaudio-controls** supports multi-touch devices. You only need to write multiple `webaudio-controls` elements, 44 | no special action in your code is required. This is a demo to operate multiple controls simultaneously 45 | using multi-touch device. 46 | 47 | Additionally, this demo is set to accept signals from an external MIDI controller 48 | via the Web MIDI API by specifying the "midicc" and "midilearn" attributes. 49 | The MIDI function can be controlled by right-clicking each element to display a menu and assigning MIDI #CC. 50 | 51 |
52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 |
70 |
71 | 72 | 73 | --- 74 | 75 | ```html 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | . 85 | . 86 | . 87 | ``` 88 | --- 89 | -------------------------------------------------------------------------------- /docs/nonlinear.md: -------------------------------------------------------------------------------- 1 | --- 2 | pageid: NonLinear 3 | --- 4 | 5 | 6 | 14 | 15 | 16 | 17 | {% include_relative gnavi.html %} 18 | 19 | --- 20 | 21 | 31 | 32 | # Non-Linear Knobs / Sliders 33 | 34 | In some cases, you may want the knob to vary nonlinear changes with respect to the rotation of the knob. 35 | For example, a knob for setting a frequency in the range of 20Hz to 20,000Hz will make an exponential change with rotation. 36 | There are two approaches to realize a knob that changes nonlinearly. 37 | The 'log' attribute simply makes the number change curve an exponential change, and the 'conv' attribute allows for more complex behavior. 38 | 39 | --- 40 | ## "log" attribute 41 | If you specify log="1" as an attribute, the change in `value` will be exponential. 42 | In this mode, the value increases at the same rate for a fixed rotation angle of the rotating knob. 43 | For example, if min="1" and max="100", it will be "10" in the center of the knob. 44 | value must be able to reach from min value to max value in geometric progression. So be aware that min cannot be 0. 45 | 46 | 47 | 48 | 49 | ```html 50 | 51 | 52 | ``` 53 | 54 | --- 55 | 56 | ## "conv" attribute 57 | 58 | The'conv' attribute can specify a conversion function. 59 | The value of the knob changes linearly, but the value passed through the conversion function is stored as convValue. 60 | This is a example of converting a value from 0 to 100 exponentially from 1 to 10000 by "conv" attribute. 61 | 62 | Here the `conv` attribute is `conv="Math.pow(10000,x/100)"`, 63 | it means the knob's raw value range 0-100 is converted to exponential 1-10000. 64 | 65 | If use with ``, it will show the convValue. 66 | When using with webaudio-param, by adding an inverse conversion expression to the `rconv` of webaudio-param attribute, 67 | directly number-edit in webaudio-param will be possible. 68 | 69 | To get converted value, use `knob.convValue` instead of `knob.value`. 70 | Note that the `convValue` is a string, then you can make more complex display not only number values. 71 | 72 | 73 | 74 | 75 | ```html 76 | 77 | 78 | ``` 79 | 80 |
Raw Value : knob.value = 0
81 |
Converted Value : knob.convValue = 1
82 | 83 | 84 | With this approach, it is possible to realize a knob that has a special action other than exponential change. 85 | The following example changes exponentially from "20" to "20000" for the change of value 0-100, 86 | and if the converted value is 1000 or over, "kHz" is displayed, otherwise "Hz". 87 | 88 | 95 | 96 | 97 |
98 | 99 | ```html 100 | 107 | 108 | 109 | 110 | 111 | ``` 112 | 113 | --- 114 | -------------------------------------------------------------------------------- /docs/resizetest.md: -------------------------------------------------------------------------------- 1 | --- 2 | pageid: Resizing 3 | --- 4 | 5 | 6 | 11 | 12 | 13 | 14 | {% include_relative gnavi.html %} 15 | 16 | --- 17 | 18 | # Resizing After Creation 19 | 20 | This is a resize test after creating the **webaudio-controls** elements. 21 | Attributes such as "diameter", "width", and "height" can be changed later to resize elements. 22 | 23 | 24 | --- 25 | 26 | ## Knob 27 | Size : 28 | 29 | 30 | 31 | 32 | 33 |
34 | 35 | ```html 36 | 37 | 38 | 39 | 40 | ``` 41 | 42 |
43 | 44 | --- 45 | 46 | ## Param 47 | Size : 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 |
57 | 58 | ```html 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | ``` 67 | 68 |
69 | 70 | --- 71 | 72 | ## Slider 73 | Size : 74 | 77 | 80 | 83 | 84 | 85 | 86 |
87 | 88 | ```html 89 | 91 | 93 | 95 | 96 | ``` 97 | 98 |
99 | 100 | --- 101 | 102 | ## Switch 103 | Size : 104 | 105 | 106 | 107 | 108 | 109 |
110 | 111 | ```html 112 | 114 | 116 | 118 | 119 | ``` 120 | 121 |
122 | 123 | --- 124 | 125 | ## Keyboard 126 | Size : 127 | 128 | 129 | 130 | Keys : 131 | 132 | 133 | 134 | 135 | 136 | 137 |
138 | 139 | ```html 140 | Size : 141 | 143 | 145 | 147 | Keys : 148 | 149 | 150 | 151 | 152 | 153 | ``` 154 | 155 | --- 156 | -------------------------------------------------------------------------------- /docs/tracking.md: -------------------------------------------------------------------------------- 1 | --- 2 | pageid: Tracking 3 | --- 4 | 5 | 6 | 11 | 12 | 13 | 14 | {% include_relative gnavi.html %} 15 | 16 | --- 17 | 18 | # Slider tracking "rel" and "abs" 19 | 20 | By default, slider tracking is set to "rel" mode. 21 | 22 | In this mode, when you operate the slider, the drag distance determines the amount of change from the current value. 23 | Pressing and releasing without dragging does not change the current value. 24 | 25 | If you set the "tracking" attribute to "abs", 26 | then simply pressing or dragging anywhere in the slider will change the value to the value corresponding to that position. 27 | Fine control (Shift + drag) is ignored when "abs" mode. 28 | 29 | --- 30 | ## tracking="rel" 31 | 32 | 34 | 35 | 36 | 37 | ```html 38 | 40 | 41 | 42 | ``` 43 | 44 | --- 45 | 46 | ## tracking="abs" 47 | 48 | 50 | 51 | 52 | 53 | ```html 54 | 56 | 57 | 58 | ``` 59 | 60 | Which of "rel" and "abs" is better depends on the case, but "abs" can be used also as changeover switches. 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 |
Triangle
Square
Sawtooth
Sine
69 | 70 | 71 | ```html 72 | 74 | 75 | ``` 76 | 77 | --- 78 | 79 | -------------------------------------------------------------------------------- /docs/webaudiocontrolsoptions.md: -------------------------------------------------------------------------------- 1 | --- 2 | pageid: WebAudioControlsOptions 3 | --- 4 | 5 | 6 | 11 | 12 | 13 | 14 | {% include_relative gnavi.html %} 15 | 16 | --- 17 | 18 | ## WebAudioControlsOptions 19 | 20 | By setting the global object, `WebAudioControlsOptions`, you can specify default values such as the knob size or colors etc when attribute setting on each tag is omitted. 21 | This declaration should be prior to the `webaduio-controls.js` loading. 22 | 23 | ```html 24 | 32 | 33 | ``` 34 | The items that can be set are as follows 35 | 36 | name | default | description 37 | ------------|---------|---------------- 38 | useMidi |0 | If `1`, enable control from midi devices. 39 | midilearn |0 | If `1`, enable midilearn function for each knobs/sliders/switches. 40 | preserveMidiLearn|0 | If `1`, save the MIDI learn result in localStorage and maintain when the next access. 41 | outline |0 | Outline style when focused. Specify detail like `"1px solid #ff8888"` or `"1"` for default `"1px solid #ccc"` style. 42 | valuetip |0 | valuetip display 43 | knobWidth |null | width for knobs 44 | knobHeight |null | height for knobs 45 | knobDiameter|64 | diameter for knobs 46 | knobSrc |null | knob image source 47 | knobSprites |null | knob image number of frames 48 | knobColors |`"#e00;#000;#fff"`| color setting for knobs 49 | sliderWidth |24 | width for sliders 50 | sliderHeight|128 | height for sliders 51 | sliderSrc |null | background image of sliders 52 | sliderKnobWidth|0 | width of the slider thumb 53 | sliderKnobHeight|0 | height of the slider thumb 54 | sliderKnobSrc|null | image of the slider thumb 55 | sliderColors|`"#e00;#333;#fcc"`| color setting for sliders 56 | switchWidth |0 | width for switches 57 | switchHeight|0 | height for switches 58 | switchDiameter|24 | diameter for switches 59 | switchColors|`"#e00;#000;#fcc"`| color setting for switches 60 | paramWidth |32 | width for param 61 | paramHeight |20 | height for param 62 | paramSrc |null | param background image source 63 | paramColors |`"#fff;#000"`| color setting for param 64 | 65 | --- 66 | -------------------------------------------------------------------------------- /docs/workingkeyboard.md: -------------------------------------------------------------------------------- 1 | --- 2 | pageid: WorkingKeyboard 3 | --- 4 | 5 | 6 | 11 | 12 | 13 | 14 | {% include_relative gnavi.html %} 15 | 16 | --- 17 | 18 | 19 | 20 | 68 | 69 | # Working Keyboard Demo 70 | 71 | This demo plays either a software-synth built into a web page 72 | or an external synth connected via the Web MIDI API 73 | from a GUI consisted with **webaduio-controls**. 74 | 75 | The synth built into this page is Web Audio API based 76 | "webaudio-tinysynth". 77 | 78 | **Note : Confirm that the connection is via "HTTPS". Due to privacy policy, this demo can't make any sound over http connection.** 79 | 80 | --- 81 | 82 | 83 | 84 | 85 | MidiOut : 86 | 87 | 88 | Program Change: : 89 | 90 | 91 | Volume : 92 | 93 | 94 | 95 | 96 |
97 | 98 | After clicking keyboard once and `webaudio-keyboard` gets the focus, you can play with `[z][x][c][v]....` on your PC keyboard. 99 | This demo uses the `outline` attribute to indicate that the element has focus. 100 | 101 | --- 102 | -------------------------------------------------------------------------------- /img/LittlePhatty_sample.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/g200kg/webaudio-controls/c4dc6d2b31c196867e57853e7281eb7303d4f6ba/img/LittlePhatty_sample.png -------------------------------------------------------------------------------- /img/bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/g200kg/webaudio-controls/c4dc6d2b31c196867e57853e7281eb7303d4f6ba/img/bg.png -------------------------------------------------------------------------------- /img/defknob2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/g200kg/webaudio-controls/c4dc6d2b31c196867e57853e7281eb7303d4f6ba/img/defknob2.png -------------------------------------------------------------------------------- /img/demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/g200kg/webaudio-controls/c4dc6d2b31c196867e57853e7281eb7303d4f6ba/img/demo.png -------------------------------------------------------------------------------- /img/hsliderbody.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/g200kg/webaudio-controls/c4dc6d2b31c196867e57853e7281eb7303d4f6ba/img/hsliderbody.png -------------------------------------------------------------------------------- /img/hsliderknob.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/g200kg/webaudio-controls/c4dc6d2b31c196867e57853e7281eb7303d4f6ba/img/hsliderknob.png -------------------------------------------------------------------------------- /img/hsw5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/g200kg/webaudio-controls/c4dc6d2b31c196867e57853e7281eb7303d4f6ba/img/hsw5.png -------------------------------------------------------------------------------- /img/midilearn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/g200kg/webaudio-controls/c4dc6d2b31c196867e57853e7281eb7303d4f6ba/img/midilearn.png -------------------------------------------------------------------------------- /img/sample3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/g200kg/webaudio-controls/c4dc6d2b31c196867e57853e7281eb7303d4f6ba/img/sample3.png -------------------------------------------------------------------------------- /img/switch_toggle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/g200kg/webaudio-controls/c4dc6d2b31c196867e57853e7281eb7303d4f6ba/img/switch_toggle.png -------------------------------------------------------------------------------- /img/testknob.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/g200kg/webaudio-controls/c4dc6d2b31c196867e57853e7281eb7303d4f6ba/img/testknob.png -------------------------------------------------------------------------------- /img/vsliderbody.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/g200kg/webaudio-controls/c4dc6d2b31c196867e57853e7281eb7303d4f6ba/img/vsliderbody.png -------------------------------------------------------------------------------- /img/vsliderknob.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/g200kg/webaudio-controls/c4dc6d2b31c196867e57853e7281eb7303d4f6ba/img/vsliderknob.png -------------------------------------------------------------------------------- /index.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # [Overview](./docs/index.html) -------------------------------------------------------------------------------- /knobs/Aqua.knob: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/g200kg/webaudio-controls/c4dc6d2b31c196867e57853e7281eb7303d4f6ba/knobs/Aqua.knob -------------------------------------------------------------------------------- /knobs/Aqua.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/g200kg/webaudio-controls/c4dc6d2b31c196867e57853e7281eb7303d4f6ba/knobs/Aqua.png -------------------------------------------------------------------------------- /knobs/Carbon.knob: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/g200kg/webaudio-controls/c4dc6d2b31c196867e57853e7281eb7303d4f6ba/knobs/Carbon.knob -------------------------------------------------------------------------------- /knobs/Carbon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/g200kg/webaudio-controls/c4dc6d2b31c196867e57853e7281eb7303d4f6ba/knobs/Carbon.png -------------------------------------------------------------------------------- /knobs/JP8000.knob: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/g200kg/webaudio-controls/c4dc6d2b31c196867e57853e7281eb7303d4f6ba/knobs/JP8000.knob -------------------------------------------------------------------------------- /knobs/JP8000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/g200kg/webaudio-controls/c4dc6d2b31c196867e57853e7281eb7303d4f6ba/knobs/JP8000.png -------------------------------------------------------------------------------- /knobs/Jambalaya.knob: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/g200kg/webaudio-controls/c4dc6d2b31c196867e57853e7281eb7303d4f6ba/knobs/Jambalaya.knob -------------------------------------------------------------------------------- /knobs/Jambalaya.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/g200kg/webaudio-controls/c4dc6d2b31c196867e57853e7281eb7303d4f6ba/knobs/Jambalaya.png -------------------------------------------------------------------------------- /knobs/LittlePhatty.knob: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/g200kg/webaudio-controls/c4dc6d2b31c196867e57853e7281eb7303d4f6ba/knobs/LittlePhatty.knob -------------------------------------------------------------------------------- /knobs/LittlePhatty.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/g200kg/webaudio-controls/c4dc6d2b31c196867e57853e7281eb7303d4f6ba/knobs/LittlePhatty.png -------------------------------------------------------------------------------- /knobs/MiniMoog_Main.knob: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/g200kg/webaudio-controls/c4dc6d2b31c196867e57853e7281eb7303d4f6ba/knobs/MiniMoog_Main.knob -------------------------------------------------------------------------------- /knobs/MiniMoog_Main.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/g200kg/webaudio-controls/c4dc6d2b31c196867e57853e7281eb7303d4f6ba/knobs/MiniMoog_Main.png -------------------------------------------------------------------------------- /knobs/SimpleFlat3.knob: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/g200kg/webaudio-controls/c4dc6d2b31c196867e57853e7281eb7303d4f6ba/knobs/SimpleFlat3.knob -------------------------------------------------------------------------------- /knobs/SimpleFlat3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/g200kg/webaudio-controls/c4dc6d2b31c196867e57853e7281eb7303d4f6ba/knobs/SimpleFlat3.png -------------------------------------------------------------------------------- /knobs/Vintage_Knob.knob: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/g200kg/webaudio-controls/c4dc6d2b31c196867e57853e7281eb7303d4f6ba/knobs/Vintage_Knob.knob -------------------------------------------------------------------------------- /knobs/Vintage_Knob.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/g200kg/webaudio-controls/c4dc6d2b31c196867e57853e7281eb7303d4f6ba/knobs/Vintage_Knob.png -------------------------------------------------------------------------------- /knobs/Vintage_VUMeter.knob: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/g200kg/webaudio-controls/c4dc6d2b31c196867e57853e7281eb7303d4f6ba/knobs/Vintage_VUMeter.knob -------------------------------------------------------------------------------- /knobs/Vintage_VUMeter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/g200kg/webaudio-controls/c4dc6d2b31c196867e57853e7281eb7303d4f6ba/knobs/Vintage_VUMeter.png -------------------------------------------------------------------------------- /knobs/Vintage_VUMeter_2.knob: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/g200kg/webaudio-controls/c4dc6d2b31c196867e57853e7281eb7303d4f6ba/knobs/Vintage_VUMeter_2.knob -------------------------------------------------------------------------------- /knobs/Vintage_VUMeter_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/g200kg/webaudio-controls/c4dc6d2b31c196867e57853e7281eb7303d4f6ba/knobs/Vintage_VUMeter_2.png -------------------------------------------------------------------------------- /knobs/WOK_vintage_AbbeyRoad_PAN_Knob.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/g200kg/webaudio-controls/c4dc6d2b31c196867e57853e7281eb7303d4f6ba/knobs/WOK_vintage_AbbeyRoad_PAN_Knob.png -------------------------------------------------------------------------------- /knobs/controls.skin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/g200kg/webaudio-controls/c4dc6d2b31c196867e57853e7281eb7303d4f6ba/knobs/controls.skin -------------------------------------------------------------------------------- /knobs/hsliderbody.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/g200kg/webaudio-controls/c4dc6d2b31c196867e57853e7281eb7303d4f6ba/knobs/hsliderbody.png -------------------------------------------------------------------------------- /knobs/hsliderknob.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/g200kg/webaudio-controls/c4dc6d2b31c196867e57853e7281eb7303d4f6ba/knobs/hsliderknob.png -------------------------------------------------------------------------------- /knobs/hsw5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/g200kg/webaudio-controls/c4dc6d2b31c196867e57853e7281eb7303d4f6ba/knobs/hsw5.png -------------------------------------------------------------------------------- /knobs/knob_metal_mesh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/g200kg/webaudio-controls/c4dc6d2b31c196867e57853e7281eb7303d4f6ba/knobs/knob_metal_mesh.png -------------------------------------------------------------------------------- /knobs/lineshadow.knob: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/g200kg/webaudio-controls/c4dc6d2b31c196867e57853e7281eb7303d4f6ba/knobs/lineshadow.knob -------------------------------------------------------------------------------- /knobs/lineshadow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/g200kg/webaudio-controls/c4dc6d2b31c196867e57853e7281eb7303d4f6ba/knobs/lineshadow.png -------------------------------------------------------------------------------- /knobs/lineshadow2.knob: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/g200kg/webaudio-controls/c4dc6d2b31c196867e57853e7281eb7303d4f6ba/knobs/lineshadow2.knob -------------------------------------------------------------------------------- /knobs/lineshadow2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/g200kg/webaudio-controls/c4dc6d2b31c196867e57853e7281eb7303d4f6ba/knobs/lineshadow2.png -------------------------------------------------------------------------------- /knobs/m400.knob: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/g200kg/webaudio-controls/c4dc6d2b31c196867e57853e7281eb7303d4f6ba/knobs/m400.knob -------------------------------------------------------------------------------- /knobs/m400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/g200kg/webaudio-controls/c4dc6d2b31c196867e57853e7281eb7303d4f6ba/knobs/m400.png -------------------------------------------------------------------------------- /knobs/nice_lamp_knob.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/g200kg/webaudio-controls/c4dc6d2b31c196867e57853e7281eb7303d4f6ba/knobs/nice_lamp_knob.png -------------------------------------------------------------------------------- /knobs/parambg120x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/g200kg/webaudio-controls/c4dc6d2b31c196867e57853e7281eb7303d4f6ba/knobs/parambg120x32.png -------------------------------------------------------------------------------- /knobs/plastic_knob.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/g200kg/webaudio-controls/c4dc6d2b31c196867e57853e7281eb7303d4f6ba/knobs/plastic_knob.png -------------------------------------------------------------------------------- /knobs/redbutton128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/g200kg/webaudio-controls/c4dc6d2b31c196867e57853e7281eb7303d4f6ba/knobs/redbutton128.png -------------------------------------------------------------------------------- /knobs/simplegray.knob: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/g200kg/webaudio-controls/c4dc6d2b31c196867e57853e7281eb7303d4f6ba/knobs/simplegray.knob -------------------------------------------------------------------------------- /knobs/simplegray.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/g200kg/webaudio-controls/c4dc6d2b31c196867e57853e7281eb7303d4f6ba/knobs/simplegray.png -------------------------------------------------------------------------------- /knobs/switch_toggle.knob: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/g200kg/webaudio-controls/c4dc6d2b31c196867e57853e7281eb7303d4f6ba/knobs/switch_toggle.knob -------------------------------------------------------------------------------- /knobs/switch_toggle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/g200kg/webaudio-controls/c4dc6d2b31c196867e57853e7281eb7303d4f6ba/knobs/switch_toggle.png -------------------------------------------------------------------------------- /knobs/vernier.knob: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/g200kg/webaudio-controls/c4dc6d2b31c196867e57853e7281eb7303d4f6ba/knobs/vernier.knob -------------------------------------------------------------------------------- /knobs/vernier.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/g200kg/webaudio-controls/c4dc6d2b31c196867e57853e7281eb7303d4f6ba/knobs/vernier.png -------------------------------------------------------------------------------- /knobs/vsliderbody.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/g200kg/webaudio-controls/c4dc6d2b31c196867e57853e7281eb7303d4f6ba/knobs/vsliderbody.png -------------------------------------------------------------------------------- /knobs/vsliderknob.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/g200kg/webaudio-controls/c4dc6d2b31c196867e57853e7281eb7303d4f6ba/knobs/vsliderknob.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@g200kg/webaudio-controls", 3 | "version": "2.1.2", 4 | "description": "GUI parts library for Web Audio applications", 5 | "main": "webaudio-controls.js", 6 | "publishConfig": { "registry": "https://npm.pkg.github.com/" }, 7 | "scripts": { 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/g200kg/webaudio-controls.git" 13 | }, 14 | "keywords": [ 15 | "gui", 16 | "webaudio", 17 | "knob" 18 | ], 19 | "author": "g200kg", 20 | "license": "Apache-2.0", 21 | "bugs": { "url": "https://github.com/g200kg/webaudio-controls/issues" }, 22 | "homepage": "https://github.com/g200kg/webaudio-controls#readme" 23 | } 24 | -------------------------------------------------------------------------------- /webaudio-controls.js: -------------------------------------------------------------------------------- 1 | /* * 2 | * 3 | * WebAudio-Controls is based on 4 | * webaudio-knob by Eiji Kitamura http://google.com/+agektmr 5 | * webaudio-slider by RYoya Kawai https://plus.google.com/108242669191458983485/posts 6 | * webaudio-switch by Keisuke Ai http://d.hatena.ne.jp/aike/ 7 | * Integrated and enhanced by g200kg http://www.g200kg.com/ 8 | * 9 | * Copyright 2013 Eiji Kitamura / Ryoya KAWAI / Keisuke Ai / g200kg(Tatsuya Shinyagaito) 10 | * 11 | * Licensed under the Apache License, Version 2.0 (the "License"); 12 | * you may not use this file except in compliance with the License. 13 | * You may obtain a copy of the License at 14 | * 15 | * http://www.apache.org/licenses/LICENSE-2.0 16 | * 17 | * Unless required by applicable law or agreed to in writing, software 18 | * distributed under the License is distributed on an "AS IS" BASIS, 19 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 20 | * See the License for the specific language governing permissions and 21 | * limitations under the License. 22 | * 23 | * */ 24 | if(window.customElements){ 25 | let styles=document.createElement("style"); 26 | styles.innerHTML= 27 | `#webaudioctrl-context-menu { 28 | display: none; 29 | position: absolute; 30 | z-index: 10; 31 | padding: 0; 32 | width: 100px; 33 | color:#eee; 34 | background-color: #268; 35 | border: solid 1px #888; 36 | box-shadow: 1px 1px 2px #888; 37 | font-family: sans-serif; 38 | font-size: 11px; 39 | line-height:1.7em; 40 | text-align:center; 41 | cursor:pointer; 42 | color:#fff; 43 | list-style: none; 44 | } 45 | #webaudioctrl-context-menu.active { 46 | display: block; 47 | } 48 | .webaudioctrl-context-menu__item { 49 | display: block; 50 | margin: 0; 51 | padding: 0; 52 | color: #000; 53 | background-color:#eee; 54 | text-decoration: none; 55 | } 56 | .webaudioctrl-context-menu__title{ 57 | font-weight:bold; 58 | } 59 | .webaudioctrl-context-menu__item:last-child { 60 | margin-bottom: 0; 61 | } 62 | .webaudioctrl-context-menu__item:hover { 63 | background-color: #b8b8b8; 64 | } 65 | `; 66 | document.head.appendChild(styles); 67 | let midimenu=document.createElement("ul"); 68 | midimenu.id="webaudioctrl-context-menu"; 69 | midimenu.innerHTML= 70 | `
  • MIDI Learn
  • 71 |
  • Learn
  • 72 |
  • Clear
  • 73 |
  • Close
  • 74 | `; 75 | let opt={ 76 | useMidi:0, 77 | preserveMidiLearn:0, 78 | preserveValue:0, 79 | midilearn:0, 80 | mididump:0, 81 | outline:null, 82 | knobSrc:null, 83 | knobSprites:null, 84 | knobWidth:null, 85 | knobHeight:null, 86 | knobDiameter:null, 87 | knobColors:"#e00;#000;#fff", 88 | sliderSrc:null, 89 | sliderWidth:null, 90 | sliderHeight:null, 91 | sliderKnobSrc:null, 92 | sliderKnobWidth:null, 93 | sliderKnobHeight:null, 94 | sliderDitchlength:null, 95 | sliderColors:"#e00;#333;#fcc", 96 | switchWidth:null, 97 | switchHeight:null, 98 | switchDiameter:null, 99 | switchColors:"#e00;#000;#fcc", 100 | paramWidth:null, 101 | paramHeight:null, 102 | paramFontSize:9, 103 | paramColors:"#fff;#000", 104 | valuetip:0, 105 | xypadColors:"#e00;#000;#fcc", 106 | }; 107 | if(window.WebAudioControlsOptions) 108 | Object.assign(opt,window.WebAudioControlsOptions); 109 | class WebAudioControlsWidget extends HTMLElement{ 110 | constructor(){ 111 | super(); 112 | this.addEventListener("keydown",this.keydown); 113 | this.addEventListener("mousedown",this.pointerdown,{passive:false}); 114 | this.addEventListener("touchstart",this.pointerdown,{passive:false}); 115 | this.addEventListener("wheel",this.wheel,{passive:false}); 116 | this.addEventListener("mouseover",this.pointerover); 117 | this.addEventListener("mouseout",this.pointerout); 118 | this.addEventListener("contextmenu",this.contextMenu); 119 | this.hover=this.drag=0; 120 | document.body.appendChild(midimenu); 121 | this.basestyle=` 122 | .webaudioctrl-tooltip{ 123 | display:inline-block; 124 | position:absolute; 125 | margin:0 -1000px; 126 | z-index: 999; 127 | background:#eee; 128 | color:#000; 129 | border:1px solid #666; 130 | border-radius:4px; 131 | padding:5px 10px; 132 | text-align:center; 133 | left:0; top:0; 134 | font-size:11px; 135 | opacity:0; 136 | visibility:hidden; 137 | } 138 | .webaudioctrl-tooltip:before{ 139 | content: ""; 140 | position: absolute; 141 | top: 100%; 142 | left: 50%; 143 | margin-left: -8px; 144 | border: 8px solid transparent; 145 | border-top: 8px solid #666; 146 | } 147 | .webaudioctrl-tooltip:after{ 148 | content: ""; 149 | position: absolute; 150 | top: 100%; 151 | left: 50%; 152 | margin-left: -6px; 153 | border: 6px solid transparent; 154 | border-top: 6px solid #eee; 155 | } 156 | `; 157 | this.onblur=()=>{ 158 | this.elem.style.outline="none"; 159 | } 160 | this.onfocus=()=>{ 161 | switch(+this.outline){ 162 | case null: 163 | case 0: 164 | this.elem.style.outline="none"; 165 | break; 166 | case 1: 167 | this.elem.style.outline="1px solid #444"; 168 | break; 169 | default: 170 | this.elem.style.outline=this.outline; 171 | } 172 | } 173 | } 174 | sendEvent(ev){ 175 | let event; 176 | event=document.createEvent("HTMLEvents"); 177 | event.initEvent(ev,false,true); 178 | this.dispatchEvent(event); 179 | } 180 | getAttr(n,def){ 181 | let v=this.getAttribute(n); 182 | if(v==null) return def; 183 | switch(typeof(def)){ 184 | case "number": 185 | if(v=="true") return 1; 186 | v=+v; 187 | if(isNaN(v)) return 0; 188 | return v; 189 | } 190 | return v; 191 | } 192 | showtip(d){ 193 | function valstr(x,c,type){ 194 | switch(type){ 195 | case "x": return (x|0).toString(16); 196 | case "X": return (x|0).toString(16).toUpperCase(); 197 | case "d": return (x|0).toString(); 198 | case "f": return parseFloat(x).toFixed(c); 199 | case "s": return x.toString(); 200 | } 201 | return ""; 202 | } 203 | function numformat(s,x){ 204 | let i=s.indexOf("%"); 205 | let j=i+1; 206 | if(i<0) 207 | j=s.length; 208 | let c=[0,0],type=0,m=0,r=""; 209 | if(s.indexOf("%s")>=0){ 210 | return s.replace("%s",x); 211 | } 212 | for(;j=0){ 214 | type=s[j]; 215 | break; 216 | } 217 | if(s[j]==".") 218 | m=1; 219 | else 220 | c[m]=c[m]*10+parseInt(s[j]); 221 | } 222 | r=valstr(x,c[1],type); 223 | if(c[0]>0) 224 | r=(" "+r).slice(-c[0]); 225 | r=s.replace(/%.*[xXdfs]/,r); 226 | return r; 227 | } 228 | let s=this.tooltip; 229 | if(this.drag||this.hover){ 230 | if(this.valuetip){ 231 | if(s==null) 232 | s=`%s`; 233 | else if(s.indexOf("%")<0) 234 | s+=` : %s`; 235 | } 236 | if(s){ 237 | this.ttframe.innerHTML=numformat(s,this.convValue); 238 | this.ttframe.style.display="inline-block"; 239 | this.ttframe.style.width="auto"; 240 | this.ttframe.style.height="auto"; 241 | this.ttframe.style.transition="opacity 0.5s "+d+"s,visibility 0.5s "+d+"s"; 242 | this.ttframe.style.opacity=0.9; 243 | this.ttframe.style.visibility="visible"; 244 | let rc=this.getBoundingClientRect(),rc2=this.ttframe.getBoundingClientRect(),rc3=document.documentElement.getBoundingClientRect(); 245 | this.ttframe.style.left=((rc.width-rc2.width)*0.5+1000)+"px"; 246 | this.ttframe.style.top=(-rc2.height-8)+"px"; 247 | return; 248 | } 249 | } 250 | this.ttframe.style.transition="opacity 0.1s "+d+"s,visibility 0.1s "+d+"s"; 251 | this.ttframe.style.opacity=0; 252 | this.ttframe.style.visibility="hidden"; 253 | } 254 | setupLabel(){ 255 | this.labelpos=this.getAttr("labelpos", "bottom 0px"); 256 | const lpos=this.labelpos.split(" "); 257 | let offs=""; 258 | if(lpos.length==3) 259 | offs=`translate(${lpos[1]},${lpos[2]})`; 260 | this.label.style.position="absolute"; 261 | switch(lpos[0]){ 262 | case "center": 263 | this.label.style.top="50%"; 264 | this.label.style.left="50%"; 265 | this.label.style.transform=`translate(-50%,-50%) ${offs}`; 266 | break; 267 | case "right": 268 | this.label.style.top="50%"; 269 | this.label.style.left="100%"; 270 | this.label.style.transform=`translateY(-50%) ${offs}`; 271 | break; 272 | case "left": 273 | this.label.style.top="50%"; 274 | this.label.style.left="0%"; 275 | this.label.style.transform=`translate(-100%,-50%) ${offs}`; 276 | break; 277 | case "bottom": 278 | this.label.style.top="100%"; 279 | this.label.style.left="50%"; 280 | this.label.style.transform=`translateX(-50%) ${offs}`; 281 | break; 282 | case "top": 283 | this.label.style.top="0%"; 284 | this.label.style.left="50%"; 285 | this.label.style.transform=`translate(-50%,-100%) ${offs}`; 286 | break; 287 | } 288 | } 289 | pointerover(e) { 290 | this.hover=1; 291 | this.showtip(0.6); 292 | } 293 | pointerout(e) { 294 | this.hover=0; 295 | this.showtip(0); 296 | } 297 | contextMenu(e){ 298 | if(window.webAudioControlsWidgetManager && this.midilearn) 299 | webAudioControlsWidgetManager.contextMenuOpen(e,this); 300 | e.preventDefault(); 301 | e.stopPropagation(); 302 | } 303 | setMidiController(channel, cc) { 304 | if (this.listeningToThisMidiController(channel, cc)) return; 305 | this.midiController={ 'channel': channel, 'cc': cc}; 306 | console.log("Added mapping for channel=" + channel + " cc=" + cc + " tooltip=" + this.tooltip); 307 | } 308 | listeningToThisMidiController(channel, cc) { 309 | const c = this.midiController; 310 | if((c.channel === channel || c.channel < 0) && c.cc === cc) 311 | return true; 312 | return false; 313 | } 314 | processMidiEvent(event){ 315 | const channel = event.data[0] & 0xf; 316 | const controlNumber = event.data[1]; 317 | if(this.midiMode == 'learn') { 318 | this.setMidiController(channel, controlNumber); 319 | webAudioControlsWidgetManager.contextMenuClose(); 320 | this.midiMode = 'normal'; 321 | webAudioControlsWidgetManager.preserveMidiLearn(); 322 | } 323 | if(this.listeningToThisMidiController(channel, controlNumber)) { 324 | if(this.tagName=="WEBAUDIO-SWITCH"){ 325 | switch(this.type){ 326 | case "toggle": 327 | if(event.data[2]>=64) 328 | this.setValue(1-this.value,true); 329 | break; 330 | case "kick": 331 | this.setValue(event.data[2]>=64?1:0); 332 | break; 333 | case "radio": 334 | let els=document.querySelectorAll("webaudio-switch[type='radio'][group='"+this.group+"']"); 335 | for(let i=0;i 365 | ${this.basestyle} 366 | :host{ 367 | display:inline-block; 368 | margin:0; 369 | padding:0; 370 | cursor:pointer; 371 | font-family: sans-serif; 372 | font-size: 11px; 373 | } 374 | .webaudio-knob-body{ 375 | display:inline-block; 376 | position:relative; 377 | margin:0; 378 | padding:0; 379 | vertical-align:bottom; 380 | white-space:pre; 381 | } 382 | 383 |
    384 | `; 385 | this.elem=root.childNodes[2]; 386 | this.ttframe=this.elem.firstChild; 387 | this.label=this.ttframe.nextSibling; 388 | this.enable=this.getAttr("enable",1); 389 | this._src=this.getAttr("src",opt.knobSrc); if (!this.hasOwnProperty("src")) Object.defineProperty(this,"src",{get:()=>{return this._src},set:(v)=>{this._src=v;this.setupImage()}}); 390 | this._value=this.getAttr("value",0); if (!this.hasOwnProperty("value")) Object.defineProperty(this,"value",{get:()=>{return this._value},set:(v)=>{this._value=v;this.redraw()}}); 391 | this.defvalue=this.getAttr("defvalue",this._value); 392 | this._min=this.getAttr("min",0); if (!this.hasOwnProperty("min")) Object.defineProperty(this,"min",{get:()=>{return this._min},set:(v)=>{this._min=+v;this.redraw()}}); 393 | this._max=this.getAttr("max",100); if (!this.hasOwnProperty("max")) Object.defineProperty(this,"max",{get:()=>{return this._max},set:(v)=>{this._max=+v;this.redraw()}}); 394 | this._step=this.getAttr("step",1); if (!this.hasOwnProperty("step")) Object.defineProperty(this,"step",{get:()=>{return this._step},set:(v)=>{this._step=+v;this.redraw()}}); 395 | this._sprites=this.getAttr("sprites",opt.knobSprites); if (!this.hasOwnProperty("sprites")) Object.defineProperty(this,"sprites",{get:()=>{return this._sprites},set:(v)=>{this._sprites=v;this.setupImage()}}); 396 | this._width=this.getAttr("width", null); if (!this.hasOwnProperty("width")) Object.defineProperty(this,"width",{get:()=>{return this._width},set:(v)=>{this._width=v;this.setupImage()}}); 397 | this._height=this.getAttr("height", null); if (!this.hasOwnProperty("height")) Object.defineProperty(this,"height",{get:()=>{return this._height},set:(v)=>{this._height=v;this.setupImage()}}); 398 | this._diameter=this.getAttr("diameter", null); if (!this.hasOwnProperty("diameter")) Object.defineProperty(this,"diameter",{get:()=>{return this._diameter},set:(v)=>{this._diameter=v;this.setupImage()}}); 399 | this._colors=this.getAttr("colors",opt.knobColors); if (!this.hasOwnProperty("colors")) Object.defineProperty(this,"colors",{get:()=>{return this._colors},set:(v)=>{this._colors=v;this.setupImage()}}); 400 | this.outline=this.getAttr("outline",opt.outline); 401 | this.setupLabel(); 402 | this.log=this.getAttr("log",0); 403 | this.sensitivity=this.getAttr("sensitivity",1); 404 | this.valuetip=this.getAttr("valuetip",opt.valuetip); 405 | this.tooltip=this.getAttr("tooltip",null); 406 | this.conv=this.getAttr("conv",null); 407 | if(this.conv){ 408 | const x=this._value; 409 | this.convValue=eval(this.conv); 410 | if(typeof(this.convValue)=="function") 411 | this.convValue=this.convValue(x); 412 | } 413 | else 414 | this.convValue=this._value; 415 | this.midilearn=this.getAttr("midilearn",opt.midilearn); 416 | this.midicc=this.getAttr("midicc",null); 417 | this.midiController={}; 418 | this.midiMode="normal"; 419 | if(this.midicc) { 420 | let ch = parseInt(this.midicc.substring(0, this.midicc.lastIndexOf("."))) - 1; 421 | let cc = parseInt(this.midicc.substring(this.midicc.lastIndexOf(".") + 1)); 422 | this.setMidiController(ch, cc); 423 | } 424 | if(this.midilearn && this.id){ 425 | if(webAudioControlsWidgetManager && webAudioControlsWidgetManager.midiLearnTable){ 426 | const ml=webAudioControlsWidgetManager.midiLearnTable; 427 | for(let i=0; i < ml.length; ++i){ 428 | if(ml[i].id==this.id){ 429 | this.setMidiController(ml[i].cc.channel, ml[i].cc.cc); 430 | break; 431 | } 432 | } 433 | } 434 | } 435 | this.setupImage(); 436 | this.digits=0; 437 | if(this.step && this.step < 1) { 438 | for(let n = this.step ; n < 1; n *= 10) 439 | ++this.digits; 440 | } 441 | this._setValue(this._value); 442 | this.coltab=["#e00","#000","#000"]; 443 | if(window.webAudioControlsWidgetManager) 444 | window.webAudioControlsWidgetManager.addWidget(this); 445 | } 446 | disconnectedCallback(){} 447 | setupImage(){ 448 | this.kw=this._width||this._diameter||opt.knobWidth||opt.knobDiameter; 449 | this.kh=this._height||this._diameter||opt.knobHeight||opt.knobDiameter; 450 | if(!this.src){ 451 | if(this.colors) 452 | this.coltab = this.colors.split(";"); 453 | if(!this.coltab) 454 | this.coltab=["#e00","#000","#000"]; 455 | let svg= 456 | ` 457 | 458 | 459 | 460 | 461 | 462 | 463 | 464 | 465 | 466 | 467 | 468 | 469 | 470 | 471 | 472 | 473 | 474 | 475 | 476 | 477 | `; 478 | for(let i=0;i<101;++i){ 479 | svg += ``; 480 | } 481 | svg += ""; 482 | this.elem.style.backgroundImage = "url(data:image/svg+xml;base64,"+btoa(svg)+")"; 483 | if(this.kw==null) this.kw=64; 484 | if(this.kh==null) this.kh=64; 485 | this.elem.style.backgroundSize = `${this.kw}px ${this.kh*101}px`; 486 | this.elem.style.width=this.kw+"px"; 487 | this.elem.style.height=this.kh+"px"; 488 | this.style.height=this.kh+"px"; 489 | this.fireflag=true; 490 | this.redraw(); 491 | return; 492 | } 493 | else{ 494 | this.img=new Image(); 495 | this.img.onload=()=>{ 496 | this.elem.style.backgroundImage = "url("+(this.src)+")"; 497 | if(this._sprites==null) 498 | this._sprites=this.img.height/this.img.width - 1; 499 | else 500 | this._sprites=+this._sprites; 501 | if(this.kw==null) this.kw=this.img.width; 502 | if(this.kh==null) this.kh=this.img.height/(this.sprites+1); 503 | if(!this.sprites) 504 | this.elem.style.backgroundSize = "100% 100%"; 505 | else 506 | this.elem.style.backgroundSize = `${this.kw}px ${this.kh*(this.sprites+1)}px`; 507 | this.elem.style.width=this.kw+"px"; 508 | this.elem.style.height=this.kh+"px"; 509 | this.style.height=this.kh+"px"; 510 | this.redraw(); 511 | }; 512 | this.img.src=this.src; 513 | } 514 | } 515 | redraw() { 516 | let ratio; 517 | this.digits=0; 518 | if(this.step && this.step < 1) { 519 | for(let n = this.step ; n < 1; n *= 10) 520 | ++this.digits; 521 | } 522 | if(this.valuethis.max){ 526 | this.value=this.max; 527 | } 528 | if(this.log) 529 | ratio = Math.log(this.value/this.min) / Math.log(this.max/this.min); 530 | else 531 | ratio = (this.value - this.min) / (this.max - this.min); 532 | let style = this.elem.style; 533 | let sp = this.src?this.sprites:100; 534 | if(sp>=1){ 535 | let offset = (sp * ratio) | 0; 536 | style.backgroundPosition = "0px " + (-offset*this.kh) + "px"; 537 | style.transform = 'rotate(0deg)'; 538 | } else { 539 | let deg = 270 * (ratio - 0.5); 540 | style.backgroundPosition="0px 0px"; 541 | style.transform = 'rotate(' + deg + 'deg)'; 542 | } 543 | } 544 | _setValue(v){ 545 | if(this.step) 546 | v=(Math.round((v-this.min)/this.step))*this.step+this.min; 547 | this._value=Math.min(this.max,Math.max(this.min,v)); 548 | if(this._value!=this.oldvalue){ 549 | this.fireflag=true; 550 | this.oldvalue=this._value; 551 | if(this.conv){ 552 | const x=this._value; 553 | this.convValue=eval(this.conv); 554 | if(typeof(this.convValue)=="function") 555 | this.convValue=this.convValue(x); 556 | } 557 | else 558 | this.convValue=this._value; 559 | if(typeof(this.convValue)=="number"){ 560 | this.convValue=this.convValue.toFixed(this.digits); 561 | } 562 | this.redraw(); 563 | this.showtip(0); 564 | return 1; 565 | } 566 | return 0; 567 | } 568 | setValue(v,f){ 569 | if(this._setValue(v) && f) 570 | this.sendEvent("input"),this.sendEvent("change"); 571 | } 572 | keydown(e){ 573 | const delta = this.step; 574 | if(delta==0) 575 | delta=1; 576 | switch(e.key){ 577 | case "ArrowUp": 578 | this.setValue(this.value+delta,true); 579 | break; 580 | case "ArrowDown": 581 | this.setValue(this.value-delta,true); 582 | break; 583 | default: 584 | return; 585 | } 586 | e.preventDefault(); 587 | e.stopPropagation(); 588 | } 589 | wheel(e) { 590 | if (!this.enable) 591 | return; 592 | if(this.log){ 593 | let r=Math.log(this.value/this.min)/Math.log(this.max/this.min); 594 | let d = (e.deltaY>0?-0.01:0.01); 595 | if(!e.shiftKey) 596 | d*=5; 597 | r += d; 598 | this.setValue(this.min*Math.pow(this.max/this.min,r),true); 599 | } 600 | else{ 601 | let delta=Math.max(this.step, (this.max-this.min)*0.05); 602 | if(e.shiftKey) 603 | delta=this.step?this.step:1; 604 | delta=e.deltaY>0?-delta:delta; 605 | this.setValue(+this.value+delta,true); 606 | } 607 | e.preventDefault(); 608 | e.stopPropagation(); 609 | } 610 | pointerdown(ev){ 611 | if(!this.enable) 612 | return; 613 | let e=ev; 614 | if(ev.touches){ 615 | e = ev.changedTouches[0]; 616 | this.identifier=e.identifier; 617 | } 618 | else { 619 | if(e.buttons!=1 && e.button!=0) 620 | return; 621 | } 622 | this.elem.focus(); 623 | this.drag=1; 624 | this.showtip(0); 625 | this.oldvalue=this._value; 626 | let pointermove=(ev)=>{ 627 | let e=ev; 628 | if(ev.touches){ 629 | for(let i=0;i1) r=1; 648 | this._setValue(this.min * Math.pow(this.max/this.min, r)); 649 | } 650 | else{ 651 | this._setValue(this.min + ((((this.startVal + (this.max - this.min) * offset / ((e.shiftKey ? 4 : 1) * 128)) - this.min) / this.step) | 0) * this.step); 652 | } 653 | if(this.fireflag){ 654 | this.sendEvent("input"); 655 | this.fireflag=false; 656 | } 657 | if(e.preventDefault) 658 | e.preventDefault(); 659 | if(e.stopPropagation) 660 | e.stopPropagation(); 661 | return false; 662 | } 663 | let pointerup=(ev)=>{ 664 | let e=ev; 665 | if(ev.touches){ 666 | for(let i=0;;){ 667 | if(ev.changedTouches[i].identifier==this.identifier){ 668 | break; 669 | } 670 | if(++i>=ev.changedTouches.length) 671 | return; 672 | } 673 | } 674 | this.drag=0; 675 | this.showtip(0); 676 | this.startPosX = this.startPosY = null; 677 | window.removeEventListener('mousemove', pointermove); 678 | window.removeEventListener('touchmove', pointermove, {passive:false}); 679 | window.removeEventListener('mouseup', pointerup); 680 | window.removeEventListener('touchend', pointerup); 681 | window.removeEventListener('touchcancel', pointerup); 682 | document.body.removeEventListener('touchstart', preventScroll,{passive:false}); 683 | this.sendEvent("change"); 684 | } 685 | let preventScroll=(e)=>{ 686 | e.preventDefault(); 687 | } 688 | if(e.ctrlKey || e.metaKey) 689 | this.setValue(this.defvalue,true); 690 | else { 691 | this.startPosX = e.pageX; 692 | this.startPosY = e.pageY; 693 | this.startVal = this.value; 694 | window.addEventListener('mousemove', pointermove); 695 | window.addEventListener('touchmove', pointermove, {passive:false}); 696 | } 697 | window.addEventListener('mouseup', pointerup); 698 | window.addEventListener('touchend', pointerup); 699 | window.addEventListener('touchcancel', pointerup); 700 | document.body.addEventListener('touchstart', preventScroll,{passive:false}); 701 | ev.preventDefault(); 702 | ev.stopPropagation(); 703 | return false; 704 | } 705 | }); 706 | } catch(error){ 707 | console.log("webaudio-knob already defined"); 708 | } 709 | 710 | try{ 711 | customElements.define("webaudio-slider", class WebAudioSlider extends WebAudioControlsWidget { 712 | constructor(){ 713 | super(); 714 | } 715 | connectedCallback(){ 716 | let root; 717 | if(this.attachShadow) 718 | root=this.attachShadow({mode: 'open'}); 719 | else 720 | root=this; 721 | root.innerHTML= 722 | ` 748 |
    749 | `; 750 | this.elem=root.childNodes[2]; 751 | this.knob=this.elem.firstChild; 752 | this.ttframe=this.knob.nextSibling; 753 | this.label=this.ttframe.nextSibling; 754 | this.enable=this.getAttr("enable",1); 755 | this.tracking=this.getAttr("tracking","rel"); 756 | this._src=this.getAttr("src",opt.sliderSrc); if (!this.hasOwnProperty("src")) Object.defineProperty(this,"src",{get:()=>{return this._src},set:(v)=>{this._src=v;this.setupImage()}}); 757 | this._knobsrc=this.getAttr("knobsrc",opt.sliderKnobSrc); if (!this.hasOwnProperty("knobsrc")) Object.defineProperty(this,"knobsrc",{get:()=>{return this._knobsrc},set:(v)=>{this._knobsrc=v;this.setupImage()}}); 758 | this._value=this.getAttr("value",0); if (!this.hasOwnProperty("value")) Object.defineProperty(this,"value",{get:()=>{return this._value},set:(v)=>{this._value=v;this.redraw()}}); 759 | this.defvalue=this.getAttr("defvalue",this._value); 760 | this._min=this.getAttr("min",0); if (!this.hasOwnProperty("min")) Object.defineProperty(this,"min",{get:()=>{return this._min},set:(v)=>{this._min=v;this.redraw()}}); 761 | this._max=this.getAttr("max",100); if (!this.hasOwnProperty("max")) Object.defineProperty(this,"max",{get:()=>{return this._max},set:(v)=>{this._max=v;this.redraw()}}); 762 | this._step=this.getAttr("step",1); if (!this.hasOwnProperty("step")) Object.defineProperty(this,"step",{get:()=>{return this._step},set:(v)=>{this._step=v;this.redraw()}}); 763 | this._sprites=this.getAttr("sprites",0); if (!this.hasOwnProperty("sprites")) Object.defineProperty(this,"sprites",{get:()=>{return this._sprites},set:(v)=>{this._sprites=v;this.setupImage()}}); 764 | this._direction=this.getAttr("direction",null); if (!this.hasOwnProperty("direction")) Object.defineProperty(this,"direction",{get:()=>{return this._direction},set:(v)=>{this._direction=v;this.setupImage()}}); 765 | this.log=this.getAttr("log",0); 766 | this._width=this.getAttr("width",opt.sliderWidth); if (!this.hasOwnProperty("width")) Object.defineProperty(this,"width",{get:()=>{return this._width},set:(v)=>{this._width=v;this.setupImage()}}); 767 | this._height=this.getAttr("height",opt.sliderHeight); if (!this.hasOwnProperty("height")) Object.defineProperty(this,"height",{get:()=>{return this._height},set:(v)=>{this._height=v;this.setupImage()}}); 768 | this._knobwidth=this.getAttr("knobwidth",opt.sliderKnobWidth); if (!this.hasOwnProperty("knobwidth")) Object.defineProperty(this,"knobwidth",{get:()=>{return this._knobwidth},set:(v)=>{this._knobwidth=v;this.setupImage()}}); 769 | this._knobheight=this.getAttr("knobheight",opt.sliderKnobHeight); if (!this.hasOwnProperty("knobheight")) Object.defineProperty(this,"knobheight",{get:()=>{return this._knobheight},set:(v)=>{this._knobheight=v;this.setupImage()}}); 770 | this._ditchlength=this.getAttr("ditchlength",opt.sliderDitchlength); if (!this.hasOwnProperty("ditchlength")) Object.defineProperty(this,"ditchlength",{get:()=>{return this._ditchlength},set:(v)=>{this._ditchlength=v;this.setupImage()}}); 771 | this._colors=this.getAttr("colors",opt.sliderColors); if (!this.hasOwnProperty("colors")) Object.defineProperty(this,"colors",{get:()=>{return this._colors},set:(v)=>{this._colors=v;this.setupImage()}}); 772 | this.outline=this.getAttr("outline",opt.outline); 773 | this.setupLabel(); 774 | this.sensitivity=this.getAttr("sensitivity",1); 775 | this.valuetip=this.getAttr("valuetip",opt.valuetip); 776 | this.tooltip=this.getAttr("tooltip",null); 777 | this.conv=this.getAttr("conv",null); 778 | if(this.conv){ 779 | const x=this._value; 780 | this.convValue=eval(this.conv); 781 | if(typeof(this.convValue)=="function") 782 | this.convValue=this.convValue(x); 783 | } 784 | else 785 | this.convValue=this._value; 786 | this.midilearn=this.getAttr("midilearn",opt.midilearn); 787 | this.midicc=this.getAttr("midicc",null); 788 | this.midiController={}; 789 | this.midiMode="normal"; 790 | if(this.midicc) { 791 | let ch = parseInt(this.midicc.substring(0, this.midicc.lastIndexOf("."))) - 1; 792 | let cc = parseInt(this.midicc.substring(this.midicc.lastIndexOf(".") + 1)); 793 | this.setMidiController(ch, cc); 794 | } 795 | if(this.midilearn && this.id){ 796 | if(webAudioControlsWidgetManager && webAudioControlsWidgetManager.midiLearnTable){ 797 | const ml=webAudioControlsWidgetManager.midiLearnTable; 798 | for(let i=0; i < ml.length; ++i){ 799 | if(ml[i].id==this.id){ 800 | this.setMidiController(ml[i].cc.channel, ml[i].cc.cc); 801 | break; 802 | } 803 | } 804 | } 805 | } 806 | this.setupImage(); 807 | this.digits=0; 808 | if(this.step && this.step < 1) { 809 | for(let n = this.step ; n < 1; n *= 10) 810 | ++this.digits; 811 | } 812 | this.fireflag=true; 813 | if(window.webAudioControlsWidgetManager) 814 | // window.webAudioControlsWidgetManager.updateWidgets(); 815 | window.webAudioControlsWidgetManager.addWidget(this); 816 | this.elem.onclick=(e)=>{e.stopPropagation()}; 817 | } 818 | disconnectedCallback(){} 819 | setupImage(){ 820 | this.coltab = this.colors.split(";"); 821 | this.bodyimg=new Image(); 822 | this.knobimg=new Image(); 823 | this.srcurl=null; 824 | if(this.src==null||this.src==""){ 825 | this.sw=+this._width; 826 | this.sh=+this.height; 827 | if(this._direction=="horz"){ 828 | if(this._width==null) this.sw=128; 829 | if(this._height==null) this.sh=24; 830 | } 831 | else if(this._direction=="vert"){ 832 | if(this._width==null) this.sw=24; 833 | if(this._height==null) this.sh=128; 834 | } 835 | else{ 836 | if(this._width==null) this.sw=128; 837 | if(this._height==null) this.sh=24; 838 | } 839 | const r=Math.min(this.sw,this.sh)*0.5; 840 | const svgbody= 841 | ` 842 | 843 | 844 | 845 | 846 | this.sh)?'x2="0%" y2="100%"':'x2="100%" y2="0%"'}> 847 | 848 | 849 | 850 | 851 | 852 | 853 | 854 | `; 855 | this.srcurl = "data:image/svg+xml;base64,"+btoa(svgbody); 856 | } 857 | else{ 858 | this.srcurl = this.src; 859 | } 860 | this.bodyimg.onload=()=>{ 861 | if(this.src!="") 862 | this.elem.style.backgroundImage = "url("+this.srcurl+")"; 863 | this.sw=+this._width; 864 | this.sh=+this._height; 865 | if(this._width==null) this.sw=this.bodyimg.width; 866 | if(this._height==null) this.sh=this.bodyimg.height; 867 | if(this.dr==null){ 868 | if(this.sw>this.sh) 869 | this.dr="horz"; 870 | else 871 | this.dr="vert"; 872 | } 873 | this.kw=+this._knobwidth; 874 | this.kh=+this._knobheight; 875 | if(this._knobsrc==null){ 876 | if(this._knobwidth==null) this.kw=Math.min(this.sw,this.sh); 877 | if(this._knobheight==null) this.kh=Math.min(this.sw,this.sh); 878 | const mm=Math.min(this.kw,this.kh)*0.5; 879 | const kw2=Math.max(1,this.kw-12); 880 | const kh2=Math.max(1,this.kh-12); 881 | const svgknob= 882 | ` 883 | 884 | 885 | 886 | 887 | 888 | 889 | 890 | 891 | 892 | 893 | 894 | 895 | 896 | 897 | 898 | 899 | 900 | 901 | 902 | 903 | 904 | 905 | `; 906 | this.knobsrcurl = "data:image/svg+xml;base64,"+btoa(svgknob); 907 | } 908 | else{ 909 | this.knobsrcurl = this.knobsrc; 910 | } 911 | this.knobimg.onload=()=>{ 912 | this.knob.style.backgroundImage = "url("+this.knobsrcurl+")"; 913 | if(this._knobwidth==null) this.kw=this.knobimg.width; 914 | if(this._knobheight==null) this.kh=this.knobimg.height; 915 | this.dlen=this.ditchlength; 916 | if(this.dlen==null){ 917 | if(this.dr=="horz") 918 | this.dlen=this.sw-this.kw; 919 | else 920 | this.dlen=this.sh-this.kh; 921 | } 922 | this.knob.style.backgroundSize = "100% 100%"; 923 | this.knob.style.width = this.kw+"px"; 924 | this.knob.style.height = this.kh+"px"; 925 | this.elem.style.backgroundSize = "100% 100%"; 926 | this.elem.style.width=this.sw+"px"; 927 | this.elem.style.height=this.sh+"px"; 928 | this.redraw(); 929 | }; 930 | this.knobimg.src=this.knobsrcurl; 931 | }; 932 | this.bodyimg.src=this.srcurl; 933 | } 934 | redraw() { 935 | let ratio; 936 | this.digits=0; 937 | if(this.step && this.step < 1) { 938 | for(let n = this.step ; n < 1; n *= 10) 939 | ++this.digits; 940 | } 941 | if(this.valuethis.max){ 945 | this.value=this.max; 946 | } 947 | if(this.log) 948 | ratio = Math.log(this.value/this.min) / Math.log(this.max/this.min); 949 | else 950 | ratio = (this.value - this.min) / (this.max - this.min); 951 | let style = this.knob.style; 952 | if(this.dr=="horz"){ 953 | style.top=(this.sh-this.kh)*0.5+"px"; 954 | style.left=((this.sw-this.kw-this.dlen)*0.5+ratio*this.dlen)+"px"; 955 | this.sensex=1; this.sensey=0; 956 | } 957 | else{ 958 | style.left=(this.sw-this.kw)*0.5+"px"; 959 | style.top=((this.sh-this.kh-this.dlen)*0.5+(1-ratio)*this.dlen)+"px"; 960 | this.sensex=0; this.sensey=1; 961 | } 962 | } 963 | _setValue(v){ 964 | v=(Math.round((v-this.min)/this.step))*this.step+this.min; 965 | this._value=Math.min(this.max,Math.max(this.min,v)); 966 | if(this._value!=this.oldvalue){ 967 | this.oldvalue=this._value; 968 | this.fireflag=true; 969 | if(this.conv){ 970 | const x=this._value; 971 | this.convValue=eval(this.conv); 972 | if(typeof(this.convValue)=="function") 973 | this.convValue=this.convValue(x); 974 | } 975 | else 976 | this.convValue=this._value; 977 | if(typeof(this.convValue)=="number"){ 978 | this.convValue=this.convValue.toFixed(this.digits); 979 | } 980 | this.redraw(); 981 | this.showtip(0); 982 | return 1; 983 | } 984 | return 0; 985 | } 986 | setValue(v,f){ 987 | if(this._setValue(v)&&f) 988 | this.sendEvent("input"),this.sendEvent("change"); 989 | } 990 | keydown(e){ 991 | const delta = this.step; 992 | if(delta==0) 993 | delta=1; 994 | switch(e.key){ 995 | case "ArrowUp": 996 | this.setValue(this.value+delta,true); 997 | break; 998 | case "ArrowDown": 999 | this.setValue(this.value-delta,true); 1000 | break; 1001 | default: 1002 | return; 1003 | } 1004 | e.preventDefault(); 1005 | e.stopPropagation(); 1006 | } 1007 | wheel(e) { 1008 | if (!this.enable) 1009 | return; 1010 | if(this.log){ 1011 | let r=Math.log(this.value/this.min)/Math.log(this.max/this.min); 1012 | let d = (e.deltaY>0?-0.01:0.01); 1013 | if(!e.shiftKey) 1014 | d*=5; 1015 | r += d; 1016 | this.setValue(this.min*Math.pow(this.max/this.min,r),true); 1017 | } 1018 | else{ 1019 | let delta=Math.max(this.step, (this.max-this.min)*0.05); 1020 | if(e.shiftKey) 1021 | delta=this.step?this.step:1; 1022 | delta=e.deltaY>0?-delta:delta; 1023 | this.setValue(+this.value+delta,true); 1024 | } 1025 | e.preventDefault(); 1026 | e.stopPropagation(); 1027 | } 1028 | pointerdown(ev){ 1029 | if(!this.enable) 1030 | return; 1031 | let e=ev; 1032 | if(ev.touches){ 1033 | e = ev.changedTouches[0]; 1034 | this.identifier=e.identifier; 1035 | } 1036 | else { 1037 | if(e.buttons!=1 && e.button!=0) 1038 | return; 1039 | } 1040 | this.elem.focus(); 1041 | this.drag=1; 1042 | this.showtip(0); 1043 | let pointermove=(ev)=>{ 1044 | let e=ev; 1045 | if(ev.touches){ 1046 | for(let i=0;i1) r=1; 1079 | this._setValue(this.min * Math.pow(this.max/this.min, r)); 1080 | } 1081 | else{ 1082 | this._setValue(this.min + ((((this.startVal + (this.max - this.min) * offset / ((e.shiftKey ? 4 : 1) * this.dlen)) - this.min) / this.step) | 0) * this.step); 1083 | } 1084 | } 1085 | if(this.fireflag){ 1086 | this.sendEvent("input"); 1087 | this.fireflag=false; 1088 | } 1089 | if(e.preventDefault) 1090 | e.preventDefault(); 1091 | if(e.stopPropagation) 1092 | e.stopPropagation(); 1093 | return false; 1094 | } 1095 | let pointerup=(ev)=>{ 1096 | let e=ev; 1097 | if(ev.touches){ 1098 | for(let i=0;;){ 1099 | if(ev.changedTouches[i].identifier==this.identifier){ 1100 | break; 1101 | } 1102 | if(++i>=ev.changedTouches.length) 1103 | return; 1104 | } 1105 | } 1106 | this.drag=0; 1107 | this.showtip(0); 1108 | this.startPosX = this.startPosY = null; 1109 | window.removeEventListener('mousemove', pointermove); 1110 | window.removeEventListener('touchmove', pointermove, {passive:false}); 1111 | window.removeEventListener('mouseup', pointerup); 1112 | window.removeEventListener('touchend', pointerup); 1113 | window.removeEventListener('touchcancel', pointerup); 1114 | document.body.removeEventListener('touchstart', preventScroll,{passive:false}); 1115 | this.sendEvent("change"); 1116 | } 1117 | let preventScroll=(e)=>{ 1118 | e.preventDefault(); 1119 | } 1120 | if(e.touches) 1121 | e = e.touches[0]; 1122 | if(e.ctrlKey || e.metaKey) 1123 | this.setValue(this.defvalue,true); 1124 | else { 1125 | this.startPosX = e.pageX; 1126 | this.startPosY = e.pageY; 1127 | this.startVal = this.value; 1128 | window.addEventListener('mousemove', pointermove); 1129 | window.addEventListener('touchmove', pointermove, {passive:false}); 1130 | pointermove(ev); 1131 | } 1132 | window.addEventListener('mouseup', pointerup); 1133 | window.addEventListener('touchend', pointerup); 1134 | window.addEventListener('touchcancel', pointerup); 1135 | document.body.addEventListener('touchstart', preventScroll,{passive:false}); 1136 | e.preventDefault(); 1137 | e.stopPropagation(); 1138 | return false; 1139 | } 1140 | }); 1141 | } catch(error){ 1142 | console.log("webaudio-slider already defined"); 1143 | } 1144 | 1145 | try{ 1146 | customElements.define("webaudio-switch", class WebAudioSwitch extends WebAudioControlsWidget { 1147 | constructor(){ 1148 | super(); 1149 | } 1150 | connectedCallback(){ 1151 | let root; 1152 | if(this.attachShadow) 1153 | root=this.attachShadow({mode: 'open'}); 1154 | else 1155 | root=this; 1156 | root.innerHTML= 1157 | ` 1182 |
    1183 | `; 1184 | this.elem=root.childNodes[2]; 1185 | this.ttframe=this.elem.firstChild; 1186 | this.label=this.ttframe.nextSibling; 1187 | this.enable=this.getAttr("enable",1); 1188 | this._src=this.getAttr("src",null); if (!this.hasOwnProperty("src")) Object.defineProperty(this,"src",{get:()=>{return this._src},set:(v)=>{this._src=v;this.setupImage()}}); 1189 | this._value=this.getAttr("value",0); if (!this.hasOwnProperty("value")) Object.defineProperty(this,"value",{get:()=>{return this._value},set:(v)=>{this._value=v;this.redraw()}}); 1190 | this.defvalue=this.getAttr("defvalue",this._value); 1191 | this.type=this.getAttr("type","toggle"); 1192 | this.group=this.getAttr("group",""); 1193 | this._width=this.getAttr("width",null); if (!this.hasOwnProperty("width")) Object.defineProperty(this,"width",{get:()=>{return this._width},set:(v)=>{this._width=v;this.setupImage()}}); 1194 | this._height=this.getAttr("height",null); if (!this.hasOwnProperty("height")) Object.defineProperty(this,"height",{get:()=>{return this._height},set:(v)=>{this._height=v;this.setupImage()}}); 1195 | this._diameter=this.getAttr("diameter",null); if (!this.hasOwnProperty("diameter")) Object.defineProperty(this,"diameter",{get:()=>{return this._diameter},set:(v)=>{this._diameter=v;this.setupImage()}}); 1196 | this.invert=this.getAttr("invert",0); 1197 | this._colors=this.getAttr("colors",opt.switchColors); if (!this.hasOwnProperty("colors")) Object.defineProperty(this,"colors",{get:()=>{return this._colors},set:(v)=>{this._colors=v;this.setupImage()}}); 1198 | this.outline=this.getAttr("outline",opt.outline); 1199 | this.setupLabel(); 1200 | this.valuetip=0; 1201 | this.tooltip=this.getAttr("tooltip",null); 1202 | this.midilearn=this.getAttr("midilearn",opt.midilearn); 1203 | this.midicc=this.getAttr("midicc",null); 1204 | this.midiController={}; 1205 | this.midiMode="normal"; 1206 | if(this.midicc) { 1207 | let ch = parseInt(this.midicc.substring(0, this.midicc.lastIndexOf("."))) - 1; 1208 | let cc = parseInt(this.midicc.substring(this.midicc.lastIndexOf(".") + 1)); 1209 | this.setMidiController(ch, cc); 1210 | } 1211 | if(this.midilearn && this.id){ 1212 | if(webAudioControlsWidgetManager && webAudioControlsWidgetManager.midiLearnTable){ 1213 | const ml=webAudioControlsWidgetManager.midiLearnTable; 1214 | for(let i=0; i < ml.length; ++i){ 1215 | if(ml[i].id==this.id){ 1216 | this.setMidiController(ml[i].cc.channel, ml[i].cc.cc); 1217 | break; 1218 | } 1219 | } 1220 | } 1221 | } 1222 | this.setupImage(); 1223 | this.digits=0; 1224 | if(this.step && this.step < 1) { 1225 | for(let n = this.step ; n < 1; n *= 10) 1226 | ++this.digits; 1227 | } 1228 | if(window.webAudioControlsWidgetManager) 1229 | // window.webAudioControlsWidgetManager.updateWidgets(); 1230 | window.webAudioControlsWidgetManager.addWidget(this); 1231 | this.elem.onclick=(e)=>{e.stopPropagation()}; 1232 | } 1233 | disconnectedCallback(){} 1234 | setupImage(){ 1235 | this.coltab = this.colors.split(";"); 1236 | this.kw=this._width||this._diameter||opt.switchWidth||opt.switchDiameter; 1237 | this.kh=this._height||this._diameter||opt.switchHeight||opt.switchDiameter; 1238 | this.img=new Image(); 1239 | this.srcurl=null; 1240 | if(this.src==null||this.src==""){ 1241 | if(this.kw==null) this.kw=32; 1242 | if(this.kh==null) this.kh=32; 1243 | const mm=Math.min(this.kw,this.kh); 1244 | const kw=this.kw,kh=this.kh; 1245 | const svg= 1246 | ` 1247 | 1248 | 1249 | 1250 | 1251 | 1252 | 1253 | 1254 | 1255 | 1256 | 1257 | 1258 | 1259 | 1260 | 1261 | 1262 | 1263 | 1264 | 1265 | 1266 | 1267 | 1268 | 1269 | 1270 | 1271 | 1272 | 1273 | `; 1274 | this.srcurl="data:image/svg+xml;base64,"+btoa(svg); 1275 | } 1276 | else 1277 | this.srcurl=this.src; 1278 | this.img.onload=()=>{ 1279 | if(this.kw==null) this.kw=this.img.width; 1280 | if(this.kh==null) this.kh=this.img.height*0.5; 1281 | this.elem.style.backgroundImage = "url("+this.srcurl+")"; 1282 | this.elem.style.backgroundSize = "100% 200%"; 1283 | this.elem.style.width=this.kw+"px"; 1284 | this.elem.style.height=this.kh+"px"; 1285 | this.redraw(); 1286 | } 1287 | this.img.src=this.srcurl; 1288 | } 1289 | redraw() { 1290 | let style = this.elem.style; 1291 | if(this.value^this.invert) 1292 | style.backgroundPosition = "0px -100%"; 1293 | else 1294 | style.backgroundPosition = "0px 0px"; 1295 | } 1296 | setValue(v,f){ 1297 | this.value=v; 1298 | this.checked=(!!v); 1299 | if(this.value!=this.oldvalue){ 1300 | this.redraw(); 1301 | this.showtip(0); 1302 | if(f){ 1303 | this.sendEvent("input"); 1304 | this.sendEvent("change"); 1305 | } 1306 | this.oldvalue=this.value; 1307 | } 1308 | } 1309 | pointerdown(ev){ 1310 | if(!this.enable) 1311 | return; 1312 | let e=ev; 1313 | if(ev.touches){ 1314 | e = ev.changedTouches[0]; 1315 | this.identifier=e.identifier; 1316 | } 1317 | else { 1318 | if(e.buttons!=1 && e.button!=0) 1319 | return; 1320 | } 1321 | this.elem.focus(); 1322 | this.drag=1; 1323 | this.showtip(0); 1324 | let pointermove=(e)=>{ 1325 | e.preventDefault(); 1326 | e.stopPropagation(); 1327 | return false; 1328 | } 1329 | let pointerup=(e)=>{ 1330 | this.drag=0; 1331 | this.showtip(0); 1332 | window.removeEventListener('mousemove', pointermove); 1333 | window.removeEventListener('touchmove', pointermove, {passive:false}); 1334 | window.removeEventListener('mouseup', pointerup); 1335 | window.removeEventListener('touchend', pointerup); 1336 | window.removeEventListener('touchcancel', pointerup); 1337 | document.body.removeEventListener('touchstart', preventScroll,{passive:false}); 1338 | if(this.type=="kick"){ 1339 | this.value=0; 1340 | this.checked=false; 1341 | this.redraw(); 1342 | this.sendEvent("change"); 1343 | } 1344 | this.sendEvent("click"); 1345 | e.preventDefault(); 1346 | e.stopPropagation(); 1347 | } 1348 | let preventScroll=(e)=>{ 1349 | e.preventDefault(); 1350 | } 1351 | switch(this.type){ 1352 | case "kick": 1353 | this.setValue(1); 1354 | this.sendEvent("change"); 1355 | break; 1356 | case "toggle": 1357 | if(e.ctrlKey || e.metaKey) 1358 | this.value=defvalue; 1359 | else 1360 | this.value=1-this.value; 1361 | this.checked=!!this.value; 1362 | this.sendEvent("change"); 1363 | break; 1364 | case "radio": 1365 | let els=document.querySelectorAll("webaudio-switch[type='radio'][group='"+this.group+"']"); 1366 | for(let i=0;i 1410 | ${this.basestyle} 1411 | :host{ 1412 | display:inline-block; 1413 | user-select:none; 1414 | margin:0; 1415 | padding:0; 1416 | font-family: sans-serif; 1417 | font-size: 8px; 1418 | cursor:pointer; 1419 | position:relative; 1420 | vertical-align:baseline; 1421 | } 1422 | .webaudio-param-body{ 1423 | display:inline-block; 1424 | position:relative; 1425 | text-align:center; 1426 | background:none; 1427 | margin:0; 1428 | padding:0; 1429 | font-family:sans-serif; 1430 | font-size:11px; 1431 | vertical-align:bottom; 1432 | border:none; 1433 | } 1434 | 1435 |
    1436 | `; 1437 | this.elem=root.childNodes[2]; 1438 | this.ttframe=root.childNodes[3]; 1439 | this.enable=this.getAttr("enable",1); 1440 | this._value=this.getAttr("value",0); if (!this.hasOwnProperty("value")) Object.defineProperty(this,"value",{get:()=>{return this._value},set:(v)=>{this._value=v;this.redraw()}}); 1441 | this.defvalue=this.getAttr("defvalue",0); 1442 | this._fontsize=this.getAttr("fontsize",9); if (!this.hasOwnProperty("fontsize")) Object.defineProperty(this,"fontsize",{get:()=>{return this._fontsize},set:(v)=>{this._fontsize=v;this.setupImage()}}); 1443 | this._src=this.getAttr("src",opt.paramSrc); if (!this.hasOwnProperty("src")) Object.defineProperty(this,"src",{get:()=>{return this._src},set:(v)=>{this._src=v;this.setupImage()}}); 1444 | this.link=this.getAttr("link",""); 1445 | this._width=this.getAttr("width",opt.paramWidth); if (!this.hasOwnProperty("width")) Object.defineProperty(this,"width",{get:()=>{return this._width},set:(v)=>{this._width=v;this.setupImage()}}); 1446 | this._height=this.getAttr("height",opt.paramHeight); if (!this.hasOwnProperty("height")) Object.defineProperty(this,"height",{get:()=>{return this._height},set:(v)=>{this._height=v;this.setupImage()}}); 1447 | this._colors=this.getAttr("colors",opt.paramColors); if (!this.hasOwnProperty("colors")) Object.defineProperty(this,"colors",{get:()=>{return this._colors},set:(v)=>{this._colors=v;this.setupImage()}}); 1448 | this.outline=this.getAttr("outline",opt.outline); 1449 | this.rconv=this.getAttr("rconv",null); 1450 | this.midiController={}; 1451 | this.midiMode="normal"; 1452 | this.currentLink=null; 1453 | if(this.midicc) { 1454 | let ch = parseInt(this.midicc.substring(0, this.midicc.lastIndexOf("."))) - 1; 1455 | let cc = parseInt(this.midicc.substring(this.midicc.lastIndexOf(".") + 1)); 1456 | this.setMidiController(ch, cc); 1457 | } 1458 | this.setupImage(); 1459 | if(window.webAudioControlsWidgetManager) 1460 | // window.webAudioControlsWidgetManager.updateWidgets(); 1461 | window.webAudioControlsWidgetManager.addWidget(this); 1462 | this.fromLink=((e)=>{ 1463 | this.setValue(e.target.convValue.toFixed(e.target.digits)); 1464 | }).bind(this); 1465 | this.elem.onchange=()=>{ 1466 | if(!this.currentLink.target.conv || (this.currentLink.target.conv&&this.rconv)){ 1467 | let val = this.value=this.elem.value; 1468 | if(this.rconv){ 1469 | let x=+this.elem.value; 1470 | val=eval(this.rconv); 1471 | } 1472 | if(this.currentLink){ 1473 | this.currentLink.target.setValue(val, true); 1474 | } 1475 | } 1476 | } 1477 | } 1478 | disconnectedCallback(){} 1479 | setupImage(){ 1480 | this.imgloaded=()=>{ 1481 | if(this.src!=""&&this.src!=null){ 1482 | this.elem.style.backgroundImage = "url("+this.src+")"; 1483 | this.elem.style.backgroundSize = "100% 100%"; 1484 | if(this._width==null) this._width=this.img.width; 1485 | if(this._height==null) this._height=this.img.height; 1486 | } 1487 | else{ 1488 | if(this._width==null) this._width=32; 1489 | if(this._height==null) this._height=20; 1490 | } 1491 | this.elem.style.width=this._width+"px"; 1492 | this.elem.style.height=this._height+"px"; 1493 | this.elem.style.fontSize=this.fontsize+"px"; 1494 | let l=document.getElementById(this.link); 1495 | if(l&&typeof(l.value)!="undefined"){ 1496 | if(typeof(l.convValue)=="number") 1497 | this.setValue(l.convValue.toFixed(l.digits)); 1498 | else 1499 | this.setValue(l.convValue); 1500 | if(this.currentLink) 1501 | this.currentLink.removeEventListener("input",this.currentLink.func); 1502 | this.currentLink={target:l, func:(e)=>{ 1503 | if(typeof(l.convValue)=="number") 1504 | this.setValue(l.convValue.toFixed(l.digits)); 1505 | else 1506 | this.setValue(l.convValue); 1507 | }}; 1508 | this.currentLink.target.addEventListener("input",this.currentLink.func); 1509 | // l.addEventListener("input",(e)=>{this.setValue(l.convValue.toFixed(l.digits))}); 1510 | } 1511 | this.redraw(); 1512 | }; 1513 | this.coltab = this.colors.split(";"); 1514 | this.elem.style.color=this.coltab[0]; 1515 | this.img=new Image(); 1516 | this.img.onload=this.imgloaded.bind(); 1517 | if(this.src==null){ 1518 | this.elem.style.backgroundColor=this.coltab[1]; 1519 | this.imgloaded(); 1520 | } 1521 | else if(this.src==""){ 1522 | this.elem.style.background="none"; 1523 | this.imgloaded(); 1524 | } 1525 | else{ 1526 | this.img.src=this.src; 1527 | } 1528 | } 1529 | redraw() { 1530 | this.elem.value=this.value; 1531 | } 1532 | setValue(v,f){ 1533 | this.value=v; 1534 | if(this.value!=this.oldvalue){ 1535 | this.redraw(); 1536 | this.showtip(0); 1537 | if(f){ 1538 | let event=document.createEvent("HTMLEvents"); 1539 | event.initEvent("change",false,true); 1540 | this.dispatchEvent(event); 1541 | } 1542 | this.oldvalue=this.value; 1543 | } 1544 | } 1545 | pointerdown(ev){ 1546 | if(!this.enable) 1547 | return; 1548 | let e=ev; 1549 | if(ev.touches) 1550 | e = ev.touches[0]; 1551 | else { 1552 | if(e.buttons!=1 && e.button!=0) 1553 | return; 1554 | } 1555 | this.elem.focus(); 1556 | this.redraw(); 1557 | } 1558 | }); 1559 | } catch(error){ 1560 | console.log("webaudio-param already defined"); 1561 | } 1562 | 1563 | try{ 1564 | customElements.define("webaudio-keyboard", class WebAudioKeyboard extends WebAudioControlsWidget { 1565 | constructor(){ 1566 | super(); 1567 | } 1568 | connectedCallback(){ 1569 | let root; 1570 | if(this.attachShadow) 1571 | root=this.attachShadow({mode: 'open'}); 1572 | else 1573 | root=this; 1574 | root.innerHTML= 1575 | ` 1592 |
    1593 | `; 1594 | this.elem=this.cv=root.childNodes[2]; 1595 | this.ttframe=root.childNodes[3]; 1596 | this.ctx=this.cv.getContext("2d"); 1597 | this._values=[]; 1598 | this.enable=this.getAttr("enable",1); 1599 | this._width=this.getAttr("width",480); if (!this.hasOwnProperty("width")) Object.defineProperty(this,"width",{get:()=>{return this._width},set:(v)=>{this._width=v;this.setupImage()}}); 1600 | this._height=this.getAttr("height",128); if (!this.hasOwnProperty("height")) Object.defineProperty(this,"height",{get:()=>{return this._height},set:(v)=>{this._height=v;this.setupImage()}}); 1601 | this._min=this.getAttr("min",0); if (!this.hasOwnProperty("min")) Object.defineProperty(this,"min",{get:()=>{return this._min},set:(v)=>{this._min=+v;this.redraw()}}); 1602 | this._keys=this.getAttr("keys",25); if (!this.hasOwnProperty("keys")) Object.defineProperty(this,"keys",{get:()=>{return this._keys},set:(v)=>{this._keys=+v;this.setupImage()}}); 1603 | this._colors=this.getAttr("colors","#222;#eee;#ccc;#333;#000;#e88;#c44;#c33;#800"); if (!this.hasOwnProperty("colors")) Object.defineProperty(this,"colors",{get:()=>{return this._colors},set:(v)=>{this._colors=v;this.setupImage()}}); 1604 | this.outline=this.getAttr("outline",opt.outline); 1605 | this.midilearn=this.getAttr("midilearn",0); 1606 | this.midicc=this.getAttr("midicc",null); 1607 | this.press=0; 1608 | this.keycodes1=[90,83,88,68,67,86,71,66,72,78,74,77,188,76,190,187,191,226]; 1609 | this.keycodes2=[81,50,87,51,69,82,53,84,54,89,55,85,73,57,79,48,80,192,222,219]; 1610 | this.addEventListener("keyup",this.keyup); 1611 | this.midiController={}; 1612 | this.midiMode="normal"; 1613 | if(this.midicc) { 1614 | let ch = parseInt(this.midicc.substring(0, this.midicc.lastIndexOf("."))) - 1; 1615 | let cc = parseInt(this.midicc.substring(this.midicc.lastIndexOf(".") + 1)); 1616 | this.setMidiController(ch, cc); 1617 | } 1618 | this.setupImage(); 1619 | this.digits=0; 1620 | if(this.step && this.step < 1) { 1621 | for(let n = this.step ; n < 1; n *= 10) 1622 | ++this.digits; 1623 | } 1624 | if(window.webAudioControlsWidgetManager) 1625 | window.webAudioControlsWidgetManager.addWidget(this); 1626 | } 1627 | disconnectedCallback(){} 1628 | setupImage(){ 1629 | this.cv.style.width=this.width+"px"; 1630 | this.cv.style.height=this.height+"px"; 1631 | this.bheight = this.height * 0.55; 1632 | this.kp=[0,7/12,1,3*7/12,2,3,6*7/12,4,8*7/12,5,10*7/12,6]; 1633 | this.kf=[0,1,0,1,0,0,1,0,1,0,1,0]; 1634 | this.ko=[0,0,(7*2)/12-1,0,(7*4)/12-2,(7*5)/12-3,0,(7*7)/12-4,0,(7*9)/12-5,0,(7*11)/12-6]; 1635 | this.kn=[0,2,4,5,7,9,11]; 1636 | this.coltab=this.colors.split(";"); 1637 | this.cv.width = this.width; 1638 | this.cv.height = this.height; 1639 | this.cv.style.width = this.width+'px'; 1640 | this.cv.style.height = this.height+'px'; 1641 | this.style.height = this.height+'px'; 1642 | this.cv.style.outline=this.outline?"":"none"; 1643 | this.bheight = this.height * 0.55; 1644 | this.max=this.min+this.keys-1; 1645 | this.dispvalues=[]; 1646 | this.valuesold=[]; 1647 | if(this.kf[this.min%12]) 1648 | --this.min; 1649 | if(this.kf[this.max%12]) 1650 | ++this.max; 1651 | this.redraw(); 1652 | } 1653 | redraw(){ 1654 | function rrect(ctx, x, y, w, h, r, c1, c2) { 1655 | if(c2) { 1656 | let g=ctx.createLinearGradient(x,y,x+w,y); 1657 | g.addColorStop(0,c1); 1658 | g.addColorStop(1,c2); 1659 | ctx.fillStyle=g; 1660 | } 1661 | else 1662 | ctx.fillStyle=c1; 1663 | ctx.beginPath(); 1664 | ctx.moveTo(x, y); 1665 | ctx.lineTo(x+w, y); 1666 | ctx.lineTo(x+w, y+h-r); 1667 | ctx.quadraticCurveTo(x+w, y+h, x+w-r, y+h); 1668 | ctx.lineTo(x+r, y+h); 1669 | ctx.quadraticCurveTo(x, y+h, x, y+h-r); 1670 | ctx.lineTo(x, y); 1671 | ctx.fill(); 1672 | } 1673 | this.ctx.fillStyle = this.coltab[0]; 1674 | this.ctx.fillRect(0,0,this.width,this.height); 1675 | let x0=7*((this.min/12)|0)+this.kp[this.min%12]; 1676 | let x1=7*((this.max/12)|0)+this.kp[this.max%12]; 1677 | let n=x1-x0; 1678 | this.wwidth=(this.width-1)/(n+1); 1679 | this.bwidth=this.wwidth*7/12; 1680 | let h2=this.bheight; 1681 | let r=Math.min(8,this.wwidth*0.2); 1682 | for(let i=this.min,j=0;i<=this.max;++i) { 1683 | if(this.kf[i%12]==0) { 1684 | let x=this.wwidth*(j++)+1; 1685 | if(this.dispvalues.indexOf(i)>=0) 1686 | rrect(this.ctx,x,1,this.wwidth-1,this.height-2,r,this.coltab[5],this.coltab[6]); 1687 | else 1688 | rrect(this.ctx,x,1,this.wwidth-1,this.height-2,r,this.coltab[1],this.coltab[2]); 1689 | } 1690 | } 1691 | r=Math.min(8,this.bwidth*0.3); 1692 | for(let i=this.min;i=0) 1696 | rrect(this.ctx,x,1,this.bwidth,h2,r,this.coltab[7],this.coltab[8]); 1697 | else 1698 | rrect(this.ctx,x,1,this.bwidth,h2,r,this.coltab[3],this.coltab[4]); 1699 | this.ctx.strokeStyle=this.coltab[0]; 1700 | this.ctx.stroke(); 1701 | } 1702 | } 1703 | } 1704 | _setValue(v){ 1705 | if(this.step) 1706 | v=(Math.round((v-this.min)/this.step))*this.step+this.min; 1707 | this._value=Math.min(this.max,Math.max(this.min,v)); 1708 | if(this._value!=this.oldvalue){ 1709 | this.oldvalue=this._value; 1710 | this.redraw(); 1711 | this.showtip(0); 1712 | return 1; 1713 | } 1714 | return 0; 1715 | } 1716 | setValue(v,f){ 1717 | if(this._setValue(v) && f) 1718 | this.sendEvent("input"),this.sendEvent("change"); 1719 | } 1720 | wheel(e){} 1721 | keydown(e){ 1722 | let m=Math.floor((this.min+11)/12)*12; 1723 | let k=this.keycodes1.indexOf(e.keyCode); 1724 | if(k<0) { 1725 | k=this.keycodes2.indexOf(e.keyCode); 1726 | if(k>=0) k+=12; 1727 | } 1728 | if(k>=0){ 1729 | k+=m; 1730 | if(this.currentKey!=k){ 1731 | this.currentKey=k; 1732 | this.sendEventFromKey(1,k); 1733 | this.setNote(1,k); 1734 | } 1735 | } 1736 | } 1737 | keyup(e){ 1738 | let m=Math.floor((this.min+11)/12)*12; 1739 | let k=this.keycodes1.indexOf(e.keyCode); 1740 | if(k<0) { 1741 | k=this.keycodes2.indexOf(e.keyCode); 1742 | if(k>=0) k+=12; 1743 | } 1744 | if(k>=0){ 1745 | k+=m; 1746 | this.currentKey=-1; 1747 | this.sendEventFromKey(0,k); 1748 | this.setNote(0,k); 1749 | } 1750 | } 1751 | pointerdown(ev){ 1752 | this.cv.focus(); 1753 | if(this.enable) { 1754 | ++this.press; 1755 | } 1756 | let pointermove=(ev)=>{ 1757 | if(!this.enable) 1758 | return; 1759 | let r=this.getBoundingClientRect(); 1760 | let v=[],p; 1761 | if(ev.touches) 1762 | p=ev.targetTouches; 1763 | else if(this.press) 1764 | p=[ev]; 1765 | else 1766 | p=[]; 1767 | if(p.length>0) 1768 | this.drag=1; 1769 | for(let i=0;i=0&&py=this.min&&k<=this.max) 1785 | v.push(k); 1786 | } 1787 | } 1788 | v.sort(); 1789 | this.values=v; 1790 | this.sendevent(); 1791 | this.redraw(); 1792 | } 1793 | 1794 | let pointerup=(ev)=>{ 1795 | if(this.enable) { 1796 | if(ev.touches) 1797 | this.press=ev.touches.length; 1798 | else 1799 | this.press=0; 1800 | pointermove(ev); 1801 | this.sendevent(); 1802 | if(this.press==0){ 1803 | window.removeEventListener('mousemove', pointermove); 1804 | window.removeEventListener('touchmove', pointermove, {passive:false}); 1805 | window.removeEventListener('mouseup', pointerup); 1806 | window.removeEventListener('touchend', pointerup); 1807 | window.removeEventListener('touchcancel', pointerup); 1808 | document.body.removeEventListener('touchstart', preventScroll,{passive:false}); 1809 | } 1810 | this.redraw(); 1811 | } 1812 | this.drag=0; 1813 | ev.preventDefault(); 1814 | } 1815 | let preventScroll=(ev)=>{ 1816 | ev.preventDefault(); 1817 | } 1818 | window.addEventListener('mousemove', pointermove); 1819 | window.addEventListener('touchmove', pointermove, {passive:false}); 1820 | window.addEventListener('mouseup', pointerup); 1821 | window.addEventListener('touchend', pointerup); 1822 | window.addEventListener('touchcancel', pointerup); 1823 | document.body.addEventListener('touchstart', preventScroll,{passive:false}); 1824 | pointermove(ev); 1825 | ev.preventDefault(); 1826 | ev.stopPropagation(); 1827 | } 1828 | sendEventFromKey(s,k){ 1829 | let ev=document.createEvent('HTMLEvents'); 1830 | ev.initEvent('change',true,true); 1831 | ev.note=[s,k]; 1832 | this.dispatchEvent(ev); 1833 | } 1834 | sendevent(){ 1835 | let notes=[]; 1836 | for(let i=0,j=this.valuesold.length;i=0) this.dispvalues.splice(n,1); 1862 | } 1863 | } 1864 | setNote(state,note,actx,when) { 1865 | const t=(actx&&when-actx.currentTime); 1866 | if(t>0){ 1867 | setTimeout(()=>{this.setNote(state,note)},t*1000); 1868 | } 1869 | else{ 1870 | this.setdispvalues(state,note); 1871 | this.redraw(); 1872 | } 1873 | } }); 1874 | } catch(error){ 1875 | console.log("webaudio-keyboard already defined"); 1876 | } 1877 | 1878 | class WebAudioControlsWidgetManager { 1879 | constructor(){ 1880 | this.midiAccess = null; 1881 | this.listOfWidgets = []; 1882 | this.listOfExternalMidiListeners = []; 1883 | this.updateWidgets(); 1884 | if(opt.preserveMidiLearn) 1885 | this.midiLearnTable=JSON.parse(localStorage.getItem("WebAudioControlsMidiLearn")); 1886 | else 1887 | this.midiLearnTable=null; 1888 | this.initWebAudioControls(); 1889 | } 1890 | addWidget(w){ 1891 | this.listOfWidgets.push(w); 1892 | } 1893 | updateWidgets(){ 1894 | // this.listOfWidgets = document.querySelectorAll("webaudio-knob,webaudio-slider,webaudio-switch"); 1895 | } 1896 | initWebAudioControls() { 1897 | if(navigator.requestMIDIAccess) { 1898 | navigator.requestMIDIAccess().then( 1899 | (ma)=>{this.midiAccess = ma,this.enableInputs()}, 1900 | (err)=>{ console.log("MIDI not initialized - error encountered:" + err.code)} 1901 | ); 1902 | } 1903 | } 1904 | enableInputs() { 1905 | let inputs = this.midiAccess.inputs.values(); 1906 | console.log("Found " + this.midiAccess.inputs.size + " MIDI input(s)"); 1907 | for(let input = inputs.next(); input && !input.done; input = inputs.next()) { 1908 | console.log("Connected input: " + input.value.name); 1909 | input.value.onmidimessage = this.handleMIDIMessage.bind(this); 1910 | } 1911 | } 1912 | midiConnectionStateChange(e) { 1913 | console.log("connection: " + e.port.name + " " + e.port.connection + " " + e.port.state); 1914 | enableInputs(); 1915 | } 1916 | 1917 | onMIDIStarted(midi) { 1918 | this.midiAccess = midi; 1919 | midi.onstatechange = this.midiConnectionStateChange; 1920 | enableInputs(midi); 1921 | } 1922 | // Add hooks for external midi listeners support 1923 | addMidiListener(callback) { 1924 | this.listOfExternalMidiListeners.push(callback); 1925 | } 1926 | getCurrentConfigAsJSON() { 1927 | return currentConfig.stringify(); 1928 | } 1929 | handleMIDIMessage(event) { 1930 | this.listOfExternalMidiListeners.forEach(function (externalListener) { 1931 | externalListener(event); 1932 | }); 1933 | if(((event.data[0] & 0xf0) == 0xf0) || ((event.data[0] & 0xf0) == 0xb0 && event.data[1] >= 120)) 1934 | return; 1935 | for(let w of this.listOfWidgets) { 1936 | if(w.processMidiEvent) 1937 | w.processMidiEvent(event); 1938 | } 1939 | if(opt.mididump) 1940 | console.log(event.data); 1941 | } 1942 | contextMenuOpen(e,knob){ 1943 | if(!this.midiAccess) 1944 | return; 1945 | let menu=document.getElementById("webaudioctrl-context-menu"); 1946 | menu.style.left=e.pageX+"px"; 1947 | menu.style.top=e.pageY+"px"; 1948 | menu.knob=knob; 1949 | menu.classList.add("active"); 1950 | menu.knob.focus(); 1951 | menu.knob.addEventListener("keydown",this.contextMenuCloseByKey.bind(this)); 1952 | } 1953 | contextMenuCloseByKey(e){ 1954 | if(e.keyCode==27) 1955 | this.contextMenuClose(); 1956 | } 1957 | contextMenuClose(){ 1958 | let menu=document.getElementById("webaudioctrl-context-menu"); 1959 | menu.knob.removeEventListener("keydown",this.contextMenuCloseByKey); 1960 | menu.classList.remove("active"); 1961 | let menuItemLearn=document.getElementById("webaudioctrl-context-menu-learn"); 1962 | menuItemLearn.innerHTML = 'Learn'; 1963 | menu.knob.midiMode = 'normal'; 1964 | } 1965 | contextMenuLearn(){ 1966 | let menu=document.getElementById("webaudioctrl-context-menu"); 1967 | let menuItemLearn=document.getElementById("webaudioctrl-context-menu-learn"); 1968 | menuItemLearn.innerHTML = 'Listening...'; 1969 | menu.knob.midiMode = 'learn'; 1970 | } 1971 | contextMenuClear(e){ 1972 | let menu=document.getElementById("webaudioctrl-context-menu"); 1973 | menu.knob.midiController={}; 1974 | this.contextMenuClose(); 1975 | } 1976 | preserveMidiLearn(){ 1977 | if(!opt.preserveMidiLearn) 1978 | return; 1979 | const v=[]; 1980 | for(let w of this.listOfWidgets) { 1981 | if(w.id) 1982 | v.push({"id":w.id, "cc":w.midiController}); 1983 | } 1984 | const s=JSON.stringify(v); 1985 | localStorage.setItem("WebAudioControlsMidiLearn",s); 1986 | } 1987 | } 1988 | if(window.UseWebAudioControlsMidi||opt.useMidi) 1989 | window.webAudioControlsWidgetManager = window.webAudioControlsMidiManager = new WebAudioControlsWidgetManager(); 1990 | } 1991 | --------------------------------------------------------------------------------