├── .github └── workflows │ └── release.yml ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── assets ├── fonts │ ├── LICENSE.md │ └── iosevka-Iosevka-medium.ttf ├── images │ ├── bg.png │ ├── bg.xcf │ ├── pointer.png │ ├── pointer.xcf │ └── readme_screenshot.png └── shaders │ ├── shader.frag │ └── shader.vert ├── build.rs ├── bundle_macos.sh └── src ├── dsp ├── mod.rs └── smoothed.rs ├── editor ├── interface │ ├── graphics.rs │ ├── mod.rs │ └── state.rs └── mod.rs ├── lib.rs └── plugin_state.rs /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | tags: 7 | - "v*" 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | 12 | jobs: 13 | create_release: 14 | name: Create release 15 | runs-on: macos-latest 16 | outputs: 17 | id: ${{ steps.draft_release.outputs.id }} 18 | html_url: ${{ steps.draft_release.outputs.html_url }} 19 | upload_url: ${{ steps.draft_release.outputs.upload_url }} 20 | steps: 21 | - name: Draft release 22 | id: draft_release 23 | uses: actions/create-release@v1 24 | env: 25 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 26 | with: 27 | tag_name: ${{ github.ref }} 28 | release_name: Release ${{ github.ref }} 29 | draft: true 30 | 31 | build_release: 32 | name: Build release 33 | needs: create_release 34 | runs-on: ${{ matrix.os }} 35 | strategy: 36 | matrix: 37 | os: [ubuntu-latest, macos-latest, windows-latest] 38 | include: 39 | - os: ubuntu-latest 40 | name: linux 41 | arch: x86-64 42 | asset_path: ./target/release/libampli_fe.so 43 | plugin_ext: .so 44 | - os: macos-latest 45 | name: macos 46 | arch: x86-64 47 | asset_path: ./ampli-Fe.vst 48 | plugin_ext: .vst 49 | - os: windows-latest 50 | name: windows 51 | arch: x86-64 52 | asset_path: ./target/release/ampli_fe.dll 53 | plugin_ext: .dll 54 | steps: 55 | - name: Checkout code 56 | uses: actions/checkout@v2 57 | - name: Install zip on Windows 58 | if: matrix.os == 'windows-latest' 59 | shell: bash 60 | run: choco install zip 61 | - uses: actions-rs/toolchain@v1 62 | with: 63 | toolchain: stable 64 | - uses: actions-rs/cargo@v1 65 | with: 66 | command: build 67 | args: --release 68 | - name: Bundle macOS plugin 69 | if: matrix.os == 'macos-latest' 70 | shell: bash 71 | run: ./bundle_macos.sh 72 | - name: Move Linux and Windows plugins 73 | if: matrix.os == 'ubuntu-latest' || matrix.os == 'windows-latest' 74 | shell: bash 75 | run: mv ${{ matrix.asset_path }} ./ampli-Fe${{ matrix.plugin_ext }} 76 | - name: Create plugin zip archive 77 | shell: bash 78 | run: | 79 | zip -r ampli-Fe.zip ./ampli-Fe${{ matrix.plugin_ext }} 80 | - name: Upload plugin archive 81 | uses: actions/upload-release-asset@v1 82 | env: 83 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 84 | with: 85 | upload_url: ${{ needs.create_release.outputs.upload_url }} 86 | asset_path: ./ampli-Fe.zip 87 | asset_name: ampli-Fe_${{ matrix.name }}-${{ matrix.arch }}.zip 88 | asset_content_type: application/octet-stream 89 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /assets/generated 3 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "ab_glyph" 7 | version = "0.2.5" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "5b50c188ff14b5a6efeb38eee8ccbc505cdf61e347a3d5eb04dc55d74ae4f20e" 10 | dependencies = [ 11 | "ab_glyph_rasterizer", 12 | "owned_ttf_parser", 13 | ] 14 | 15 | [[package]] 16 | name = "ab_glyph_rasterizer" 17 | version = "0.1.2" 18 | source = "registry+https://github.com/rust-lang/crates.io-index" 19 | checksum = "2b7e4e8cf778db814365e46839949ca74df4efb10e87ba4913e6ec5967ef0285" 20 | 21 | [[package]] 22 | name = "adler32" 23 | version = "1.2.0" 24 | source = "registry+https://github.com/rust-lang/crates.io-index" 25 | checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" 26 | 27 | [[package]] 28 | name = "ahash" 29 | version = "0.7.6" 30 | source = "registry+https://github.com/rust-lang/crates.io-index" 31 | checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" 32 | dependencies = [ 33 | "getrandom 0.2.4", 34 | "once_cell", 35 | "version_check", 36 | ] 37 | 38 | [[package]] 39 | name = "ampli-fe" 40 | version = "0.1.1" 41 | dependencies = [ 42 | "cgmath", 43 | "futures", 44 | "glsl-to-spirv", 45 | "once_cell", 46 | "png", 47 | "raw-window-handle", 48 | "vst", 49 | "vst_window", 50 | "wgpu", 51 | "wgpu_glyph", 52 | "zerocopy", 53 | ] 54 | 55 | [[package]] 56 | name = "approx" 57 | version = "0.3.2" 58 | source = "registry+https://github.com/rust-lang/crates.io-index" 59 | checksum = "f0e60b75072ecd4168020818c0107f2857bb6c4e64252d8d3983f6263b40a5c3" 60 | dependencies = [ 61 | "num-traits", 62 | ] 63 | 64 | [[package]] 65 | name = "arrayref" 66 | version = "0.3.6" 67 | source = "registry+https://github.com/rust-lang/crates.io-index" 68 | checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" 69 | 70 | [[package]] 71 | name = "arrayvec" 72 | version = "0.7.2" 73 | source = "registry+https://github.com/rust-lang/crates.io-index" 74 | checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" 75 | 76 | [[package]] 77 | name = "ash" 78 | version = "0.34.0+1.2.203" 79 | source = "registry+https://github.com/rust-lang/crates.io-index" 80 | checksum = "b0f780da53d0063880d45554306489f09dd8d1bda47688b4a57bc579119356df" 81 | dependencies = [ 82 | "libloading 0.7.3", 83 | ] 84 | 85 | [[package]] 86 | name = "autocfg" 87 | version = "0.1.7" 88 | source = "registry+https://github.com/rust-lang/crates.io-index" 89 | checksum = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2" 90 | 91 | [[package]] 92 | name = "autocfg" 93 | version = "1.0.0" 94 | source = "registry+https://github.com/rust-lang/crates.io-index" 95 | checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" 96 | 97 | [[package]] 98 | name = "bit-set" 99 | version = "0.5.2" 100 | source = "registry+https://github.com/rust-lang/crates.io-index" 101 | checksum = "6e11e16035ea35e4e5997b393eacbf6f63983188f7a2ad25bfb13465f5ad59de" 102 | dependencies = [ 103 | "bit-vec", 104 | ] 105 | 106 | [[package]] 107 | name = "bit-vec" 108 | version = "0.6.3" 109 | source = "registry+https://github.com/rust-lang/crates.io-index" 110 | checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" 111 | 112 | [[package]] 113 | name = "bitflags" 114 | version = "1.2.1" 115 | source = "registry+https://github.com/rust-lang/crates.io-index" 116 | checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" 117 | 118 | [[package]] 119 | name = "block" 120 | version = "0.1.6" 121 | source = "registry+https://github.com/rust-lang/crates.io-index" 122 | checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" 123 | 124 | [[package]] 125 | name = "block-buffer" 126 | version = "0.3.3" 127 | source = "registry+https://github.com/rust-lang/crates.io-index" 128 | checksum = "a076c298b9ecdb530ed9d967e74a6027d6a7478924520acddcddc24c1c8ab3ab" 129 | dependencies = [ 130 | "arrayref", 131 | "byte-tools", 132 | ] 133 | 134 | [[package]] 135 | name = "bumpalo" 136 | version = "3.4.0" 137 | source = "registry+https://github.com/rust-lang/crates.io-index" 138 | checksum = "2e8c087f005730276d1096a652e92a8bacee2e2472bcc9715a74d2bec38b5820" 139 | 140 | [[package]] 141 | name = "byte-tools" 142 | version = "0.2.0" 143 | source = "registry+https://github.com/rust-lang/crates.io-index" 144 | checksum = "560c32574a12a89ecd91f5e742165893f86e3ab98d21f8ea548658eb9eef5f40" 145 | 146 | [[package]] 147 | name = "bytemuck" 148 | version = "1.7.3" 149 | source = "registry+https://github.com/rust-lang/crates.io-index" 150 | checksum = "439989e6b8c38d1b6570a384ef1e49c8848128f5a97f3914baef02920842712f" 151 | dependencies = [ 152 | "bytemuck_derive", 153 | ] 154 | 155 | [[package]] 156 | name = "bytemuck_derive" 157 | version = "1.0.1" 158 | source = "registry+https://github.com/rust-lang/crates.io-index" 159 | checksum = "8e215f8c2f9f79cb53c8335e687ffd07d5bfcb6fe5fc80723762d0be46e7cc54" 160 | dependencies = [ 161 | "proc-macro2", 162 | "quote", 163 | "syn", 164 | ] 165 | 166 | [[package]] 167 | name = "byteorder" 168 | version = "1.3.4" 169 | source = "registry+https://github.com/rust-lang/crates.io-index" 170 | checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" 171 | 172 | [[package]] 173 | name = "cc" 174 | version = "1.0.58" 175 | source = "registry+https://github.com/rust-lang/crates.io-index" 176 | checksum = "f9a06fb2e53271d7c279ec1efea6ab691c35a2ae67ec0d91d7acec0caf13b518" 177 | 178 | [[package]] 179 | name = "cfg-if" 180 | version = "0.1.10" 181 | source = "registry+https://github.com/rust-lang/crates.io-index" 182 | checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" 183 | 184 | [[package]] 185 | name = "cfg-if" 186 | version = "1.0.0" 187 | source = "registry+https://github.com/rust-lang/crates.io-index" 188 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 189 | 190 | [[package]] 191 | name = "cfg_aliases" 192 | version = "0.1.1" 193 | source = "registry+https://github.com/rust-lang/crates.io-index" 194 | checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" 195 | 196 | [[package]] 197 | name = "cgmath" 198 | version = "0.17.0" 199 | source = "registry+https://github.com/rust-lang/crates.io-index" 200 | checksum = "283944cdecc44bf0b8dd010ec9af888d3b4f142844fdbe026c20ef68148d6fe7" 201 | dependencies = [ 202 | "approx", 203 | "num-traits", 204 | "rand 0.6.5", 205 | ] 206 | 207 | [[package]] 208 | name = "cloudabi" 209 | version = "0.0.3" 210 | source = "registry+https://github.com/rust-lang/crates.io-index" 211 | checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" 212 | dependencies = [ 213 | "bitflags", 214 | ] 215 | 216 | [[package]] 217 | name = "cloudabi" 218 | version = "0.1.0" 219 | source = "registry+https://github.com/rust-lang/crates.io-index" 220 | checksum = "4344512281c643ae7638bbabc3af17a11307803ec8f0fcad9fae512a8bf36467" 221 | dependencies = [ 222 | "bitflags", 223 | ] 224 | 225 | [[package]] 226 | name = "cmake" 227 | version = "0.1.44" 228 | source = "registry+https://github.com/rust-lang/crates.io-index" 229 | checksum = "0e56268c17a6248366d66d4a47a3381369d068cce8409bb1716ed77ea32163bb" 230 | dependencies = [ 231 | "cc", 232 | ] 233 | 234 | [[package]] 235 | name = "cocoa" 236 | version = "0.22.0" 237 | source = "registry+https://github.com/rust-lang/crates.io-index" 238 | checksum = "667fdc068627a2816b9ff831201dd9864249d6ee8d190b9532357f1fc0f61ea7" 239 | dependencies = [ 240 | "bitflags", 241 | "block", 242 | "core-foundation", 243 | "core-graphics", 244 | "foreign-types", 245 | "libc", 246 | "objc", 247 | ] 248 | 249 | [[package]] 250 | name = "codespan-reporting" 251 | version = "0.11.1" 252 | source = "registry+https://github.com/rust-lang/crates.io-index" 253 | checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" 254 | dependencies = [ 255 | "termcolor", 256 | "unicode-width", 257 | ] 258 | 259 | [[package]] 260 | name = "copyless" 261 | version = "0.1.5" 262 | source = "registry+https://github.com/rust-lang/crates.io-index" 263 | checksum = "a2df960f5d869b2dd8532793fde43eb5427cceb126c929747a26823ab0eeb536" 264 | 265 | [[package]] 266 | name = "core-foundation" 267 | version = "0.9.0" 268 | source = "registry+https://github.com/rust-lang/crates.io-index" 269 | checksum = "3b5ed8e7e76c45974e15e41bfa8d5b0483cd90191639e01d8f5f1e606299d3fb" 270 | dependencies = [ 271 | "core-foundation-sys", 272 | "libc", 273 | ] 274 | 275 | [[package]] 276 | name = "core-foundation-sys" 277 | version = "0.8.0" 278 | source = "registry+https://github.com/rust-lang/crates.io-index" 279 | checksum = "9a21fa21941700a3cd8fcb4091f361a6a712fac632f85d9f487cc892045d55c6" 280 | 281 | [[package]] 282 | name = "core-graphics" 283 | version = "0.21.0" 284 | source = "registry+https://github.com/rust-lang/crates.io-index" 285 | checksum = "52a67c4378cf203eace8fb6567847eb641fd6ff933c1145a115c6ee820ebb978" 286 | dependencies = [ 287 | "bitflags", 288 | "core-foundation", 289 | "foreign-types", 290 | "libc", 291 | ] 292 | 293 | [[package]] 294 | name = "core-graphics-types" 295 | version = "0.1.1" 296 | source = "registry+https://github.com/rust-lang/crates.io-index" 297 | checksum = "3a68b68b3446082644c91ac778bf50cd4104bfb002b5a6a7c44cca5a2c70788b" 298 | dependencies = [ 299 | "bitflags", 300 | "core-foundation", 301 | "foreign-types", 302 | "libc", 303 | ] 304 | 305 | [[package]] 306 | name = "crc32fast" 307 | version = "1.2.0" 308 | source = "registry+https://github.com/rust-lang/crates.io-index" 309 | checksum = "ba125de2af0df55319f41944744ad91c71113bf74a4646efff39afe1f6842db1" 310 | dependencies = [ 311 | "cfg-if 0.1.10", 312 | ] 313 | 314 | [[package]] 315 | name = "crossbeam-channel" 316 | version = "0.4.3" 317 | source = "registry+https://github.com/rust-lang/crates.io-index" 318 | checksum = "09ee0cc8804d5393478d743b035099520087a5186f3b93fa58cec08fa62407b6" 319 | dependencies = [ 320 | "cfg-if 0.1.10", 321 | "crossbeam-utils", 322 | ] 323 | 324 | [[package]] 325 | name = "crossbeam-deque" 326 | version = "0.7.3" 327 | source = "registry+https://github.com/rust-lang/crates.io-index" 328 | checksum = "9f02af974daeee82218205558e51ec8768b48cf524bd01d550abe5573a608285" 329 | dependencies = [ 330 | "crossbeam-epoch", 331 | "crossbeam-utils", 332 | "maybe-uninit", 333 | ] 334 | 335 | [[package]] 336 | name = "crossbeam-epoch" 337 | version = "0.8.2" 338 | source = "registry+https://github.com/rust-lang/crates.io-index" 339 | checksum = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace" 340 | dependencies = [ 341 | "autocfg 1.0.0", 342 | "cfg-if 0.1.10", 343 | "crossbeam-utils", 344 | "lazy_static", 345 | "maybe-uninit", 346 | "memoffset", 347 | "scopeguard", 348 | ] 349 | 350 | [[package]] 351 | name = "crossbeam-queue" 352 | version = "0.2.3" 353 | source = "registry+https://github.com/rust-lang/crates.io-index" 354 | checksum = "774ba60a54c213d409d5353bda12d49cd68d14e45036a285234c8d6f91f92570" 355 | dependencies = [ 356 | "cfg-if 0.1.10", 357 | "crossbeam-utils", 358 | "maybe-uninit", 359 | ] 360 | 361 | [[package]] 362 | name = "crossbeam-utils" 363 | version = "0.7.2" 364 | source = "registry+https://github.com/rust-lang/crates.io-index" 365 | checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8" 366 | dependencies = [ 367 | "autocfg 1.0.0", 368 | "cfg-if 0.1.10", 369 | "lazy_static", 370 | ] 371 | 372 | [[package]] 373 | name = "cty" 374 | version = "0.2.2" 375 | source = "registry+https://github.com/rust-lang/crates.io-index" 376 | checksum = "b365fabc795046672053e29c954733ec3b05e4be654ab130fe8f1f94d7051f35" 377 | 378 | [[package]] 379 | name = "d3d12" 380 | version = "0.4.1" 381 | source = "registry+https://github.com/rust-lang/crates.io-index" 382 | checksum = "2daefd788d1e96e0a9d66dee4b828b883509bc3ea9ce30665f04c3246372690c" 383 | dependencies = [ 384 | "bitflags", 385 | "libloading 0.7.3", 386 | "winapi", 387 | ] 388 | 389 | [[package]] 390 | name = "deflate" 391 | version = "0.8.6" 392 | source = "registry+https://github.com/rust-lang/crates.io-index" 393 | checksum = "73770f8e1fe7d64df17ca66ad28994a0a623ea497fa69486e14984e715c5d174" 394 | dependencies = [ 395 | "adler32", 396 | "byteorder", 397 | ] 398 | 399 | [[package]] 400 | name = "digest" 401 | version = "0.7.6" 402 | source = "registry+https://github.com/rust-lang/crates.io-index" 403 | checksum = "03b072242a8cbaf9c145665af9d250c59af3b958f83ed6824e13533cf76d5b90" 404 | dependencies = [ 405 | "generic-array", 406 | ] 407 | 408 | [[package]] 409 | name = "either" 410 | version = "1.5.3" 411 | source = "registry+https://github.com/rust-lang/crates.io-index" 412 | checksum = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3" 413 | 414 | [[package]] 415 | name = "fake-simd" 416 | version = "0.1.2" 417 | source = "registry+https://github.com/rust-lang/crates.io-index" 418 | checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" 419 | 420 | [[package]] 421 | name = "fixedbitset" 422 | version = "0.4.1" 423 | source = "registry+https://github.com/rust-lang/crates.io-index" 424 | checksum = "279fb028e20b3c4c320317955b77c5e0c9701f05a1d309905d6fc702cdc5053e" 425 | 426 | [[package]] 427 | name = "foreign-types" 428 | version = "0.3.2" 429 | source = "registry+https://github.com/rust-lang/crates.io-index" 430 | checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" 431 | dependencies = [ 432 | "foreign-types-shared", 433 | ] 434 | 435 | [[package]] 436 | name = "foreign-types-shared" 437 | version = "0.1.1" 438 | source = "registry+https://github.com/rust-lang/crates.io-index" 439 | checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" 440 | 441 | [[package]] 442 | name = "fuchsia-cprng" 443 | version = "0.1.1" 444 | source = "registry+https://github.com/rust-lang/crates.io-index" 445 | checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" 446 | 447 | [[package]] 448 | name = "futures" 449 | version = "0.3.5" 450 | source = "registry+https://github.com/rust-lang/crates.io-index" 451 | checksum = "1e05b85ec287aac0dc34db7d4a569323df697f9c55b99b15d6b4ef8cde49f613" 452 | dependencies = [ 453 | "futures-channel", 454 | "futures-core", 455 | "futures-executor", 456 | "futures-io", 457 | "futures-sink", 458 | "futures-task", 459 | "futures-util", 460 | ] 461 | 462 | [[package]] 463 | name = "futures-channel" 464 | version = "0.3.5" 465 | source = "registry+https://github.com/rust-lang/crates.io-index" 466 | checksum = "f366ad74c28cca6ba456d95e6422883cfb4b252a83bed929c83abfdbbf2967d5" 467 | dependencies = [ 468 | "futures-core", 469 | "futures-sink", 470 | ] 471 | 472 | [[package]] 473 | name = "futures-core" 474 | version = "0.3.5" 475 | source = "registry+https://github.com/rust-lang/crates.io-index" 476 | checksum = "59f5fff90fd5d971f936ad674802482ba441b6f09ba5e15fd8b39145582ca399" 477 | 478 | [[package]] 479 | name = "futures-executor" 480 | version = "0.3.5" 481 | source = "registry+https://github.com/rust-lang/crates.io-index" 482 | checksum = "10d6bb888be1153d3abeb9006b11b02cf5e9b209fda28693c31ae1e4e012e314" 483 | dependencies = [ 484 | "futures-core", 485 | "futures-task", 486 | "futures-util", 487 | ] 488 | 489 | [[package]] 490 | name = "futures-io" 491 | version = "0.3.5" 492 | source = "registry+https://github.com/rust-lang/crates.io-index" 493 | checksum = "de27142b013a8e869c14957e6d2edeef89e97c289e69d042ee3a49acd8b51789" 494 | 495 | [[package]] 496 | name = "futures-macro" 497 | version = "0.3.5" 498 | source = "registry+https://github.com/rust-lang/crates.io-index" 499 | checksum = "d0b5a30a4328ab5473878237c447333c093297bded83a4983d10f4deea240d39" 500 | dependencies = [ 501 | "proc-macro-hack", 502 | "proc-macro2", 503 | "quote", 504 | "syn", 505 | ] 506 | 507 | [[package]] 508 | name = "futures-sink" 509 | version = "0.3.5" 510 | source = "registry+https://github.com/rust-lang/crates.io-index" 511 | checksum = "3f2032893cb734c7a05d85ce0cc8b8c4075278e93b24b66f9de99d6eb0fa8acc" 512 | 513 | [[package]] 514 | name = "futures-task" 515 | version = "0.3.5" 516 | source = "registry+https://github.com/rust-lang/crates.io-index" 517 | checksum = "bdb66b5f09e22019b1ab0830f7785bcea8e7a42148683f99214f73f8ec21a626" 518 | dependencies = [ 519 | "once_cell", 520 | ] 521 | 522 | [[package]] 523 | name = "futures-util" 524 | version = "0.3.5" 525 | source = "registry+https://github.com/rust-lang/crates.io-index" 526 | checksum = "8764574ff08b701a084482c3c7031349104b07ac897393010494beaa18ce32c6" 527 | dependencies = [ 528 | "futures-channel", 529 | "futures-core", 530 | "futures-io", 531 | "futures-macro", 532 | "futures-sink", 533 | "futures-task", 534 | "memchr", 535 | "pin-project", 536 | "pin-utils", 537 | "proc-macro-hack", 538 | "proc-macro-nested", 539 | "slab", 540 | ] 541 | 542 | [[package]] 543 | name = "fxhash" 544 | version = "0.2.1" 545 | source = "registry+https://github.com/rust-lang/crates.io-index" 546 | checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" 547 | dependencies = [ 548 | "byteorder", 549 | ] 550 | 551 | [[package]] 552 | name = "generic-array" 553 | version = "0.9.0" 554 | source = "registry+https://github.com/rust-lang/crates.io-index" 555 | checksum = "ef25c5683767570c2bbd7deba372926a55eaae9982d7726ee2a1050239d45b9d" 556 | dependencies = [ 557 | "typenum", 558 | ] 559 | 560 | [[package]] 561 | name = "getrandom" 562 | version = "0.1.14" 563 | source = "registry+https://github.com/rust-lang/crates.io-index" 564 | checksum = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb" 565 | dependencies = [ 566 | "cfg-if 0.1.10", 567 | "libc", 568 | "wasi 0.9.0+wasi-snapshot-preview1", 569 | ] 570 | 571 | [[package]] 572 | name = "getrandom" 573 | version = "0.2.4" 574 | source = "registry+https://github.com/rust-lang/crates.io-index" 575 | checksum = "418d37c8b1d42553c93648be529cb70f920d3baf8ef469b74b9638df426e0b4c" 576 | dependencies = [ 577 | "cfg-if 1.0.0", 578 | "libc", 579 | "wasi 0.10.2+wasi-snapshot-preview1", 580 | ] 581 | 582 | [[package]] 583 | name = "glow" 584 | version = "0.11.2" 585 | source = "registry+https://github.com/rust-lang/crates.io-index" 586 | checksum = "d8bd5877156a19b8ac83a29b2306fe20537429d318f3ff0a1a2119f8d9c61919" 587 | dependencies = [ 588 | "js-sys", 589 | "slotmap", 590 | "wasm-bindgen", 591 | "web-sys", 592 | ] 593 | 594 | [[package]] 595 | name = "glsl-to-spirv" 596 | version = "0.1.7" 597 | source = "registry+https://github.com/rust-lang/crates.io-index" 598 | checksum = "28caebc98746d507603a2d3df66dcbe04e41d4febad0320f3eec1ef72b6bbef1" 599 | dependencies = [ 600 | "cmake", 601 | "sha2", 602 | "tempfile", 603 | ] 604 | 605 | [[package]] 606 | name = "glyph_brush" 607 | version = "0.7.0" 608 | source = "registry+https://github.com/rust-lang/crates.io-index" 609 | checksum = "afd3e2cfd503a5218dd56172a8bf7c8655a4a7cf745737c606a6edfeea1b343f" 610 | dependencies = [ 611 | "glyph_brush_draw_cache", 612 | "glyph_brush_layout", 613 | "log", 614 | "ordered-float", 615 | "rustc-hash", 616 | "twox-hash", 617 | ] 618 | 619 | [[package]] 620 | name = "glyph_brush_draw_cache" 621 | version = "0.1.1" 622 | source = "registry+https://github.com/rust-lang/crates.io-index" 623 | checksum = "e5f15abf9569e0b4440797efc0d565c8f30c4a1ca8b0b0c10c0fb7fb2a343c82" 624 | dependencies = [ 625 | "ab_glyph", 626 | "crossbeam-channel", 627 | "crossbeam-deque", 628 | "linked-hash-map", 629 | "rayon", 630 | "rustc-hash", 631 | ] 632 | 633 | [[package]] 634 | name = "glyph_brush_layout" 635 | version = "0.2.0" 636 | source = "registry+https://github.com/rust-lang/crates.io-index" 637 | checksum = "9aa49abf7dcf7bfe68f42c1c8ab7473505aaba14de84afb8899a0109b6c61717" 638 | dependencies = [ 639 | "ab_glyph", 640 | "approx", 641 | "xi-unicode", 642 | ] 643 | 644 | [[package]] 645 | name = "gpu-alloc" 646 | version = "0.5.3" 647 | source = "registry+https://github.com/rust-lang/crates.io-index" 648 | checksum = "7fc59e5f710e310e76e6707f86c561dd646f69a8876da9131703b2f717de818d" 649 | dependencies = [ 650 | "bitflags", 651 | "gpu-alloc-types", 652 | ] 653 | 654 | [[package]] 655 | name = "gpu-alloc-types" 656 | version = "0.2.0" 657 | source = "registry+https://github.com/rust-lang/crates.io-index" 658 | checksum = "54804d0d6bc9d7f26db4eaec1ad10def69b599315f487d32c334a80d1efe67a5" 659 | dependencies = [ 660 | "bitflags", 661 | ] 662 | 663 | [[package]] 664 | name = "gpu-descriptor" 665 | version = "0.2.2" 666 | source = "registry+https://github.com/rust-lang/crates.io-index" 667 | checksum = "a538f217be4d405ff4719a283ca68323cc2384003eca5baaa87501e821c81dda" 668 | dependencies = [ 669 | "bitflags", 670 | "gpu-descriptor-types", 671 | "hashbrown 0.11.2", 672 | ] 673 | 674 | [[package]] 675 | name = "gpu-descriptor-types" 676 | version = "0.1.1" 677 | source = "registry+https://github.com/rust-lang/crates.io-index" 678 | checksum = "363e3677e55ad168fef68cf9de3a4a310b53124c5e784c53a1d70e92d23f2126" 679 | dependencies = [ 680 | "bitflags", 681 | ] 682 | 683 | [[package]] 684 | name = "hashbrown" 685 | version = "0.9.1" 686 | source = "registry+https://github.com/rust-lang/crates.io-index" 687 | checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04" 688 | 689 | [[package]] 690 | name = "hashbrown" 691 | version = "0.11.2" 692 | source = "registry+https://github.com/rust-lang/crates.io-index" 693 | checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" 694 | dependencies = [ 695 | "ahash", 696 | ] 697 | 698 | [[package]] 699 | name = "hermit-abi" 700 | version = "0.1.15" 701 | source = "registry+https://github.com/rust-lang/crates.io-index" 702 | checksum = "3deed196b6e7f9e44a2ae8d94225d80302d81208b1bb673fd21fe634645c85a9" 703 | dependencies = [ 704 | "libc", 705 | ] 706 | 707 | [[package]] 708 | name = "hexf-parse" 709 | version = "0.2.1" 710 | source = "registry+https://github.com/rust-lang/crates.io-index" 711 | checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df" 712 | 713 | [[package]] 714 | name = "indexmap" 715 | version = "1.6.2" 716 | source = "registry+https://github.com/rust-lang/crates.io-index" 717 | checksum = "824845a0bf897a9042383849b02c1bc219c2383772efcd5c6f9766fa4b81aef3" 718 | dependencies = [ 719 | "autocfg 1.0.0", 720 | "hashbrown 0.9.1", 721 | ] 722 | 723 | [[package]] 724 | name = "inplace_it" 725 | version = "0.3.3" 726 | source = "registry+https://github.com/rust-lang/crates.io-index" 727 | checksum = "90953f308a79fe6d62a4643e51f848fbfddcd05975a38e69fdf4ab86a7baf7ca" 728 | 729 | [[package]] 730 | name = "instant" 731 | version = "0.1.6" 732 | source = "registry+https://github.com/rust-lang/crates.io-index" 733 | checksum = "5b141fdc7836c525d4d594027d318c84161ca17aaf8113ab1f81ab93ae897485" 734 | dependencies = [ 735 | "js-sys", 736 | "wasm-bindgen", 737 | "web-sys", 738 | ] 739 | 740 | [[package]] 741 | name = "js-sys" 742 | version = "0.3.56" 743 | source = "registry+https://github.com/rust-lang/crates.io-index" 744 | checksum = "a38fc24e30fd564ce974c02bf1d337caddff65be6cc4735a1f7eab22a7440f04" 745 | dependencies = [ 746 | "wasm-bindgen", 747 | ] 748 | 749 | [[package]] 750 | name = "khronos-egl" 751 | version = "4.1.0" 752 | source = "registry+https://github.com/rust-lang/crates.io-index" 753 | checksum = "8c2352bd1d0bceb871cb9d40f24360c8133c11d7486b68b5381c1dd1a32015e3" 754 | dependencies = [ 755 | "libc", 756 | "libloading 0.7.3", 757 | ] 758 | 759 | [[package]] 760 | name = "lazy_static" 761 | version = "1.4.0" 762 | source = "registry+https://github.com/rust-lang/crates.io-index" 763 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 764 | 765 | [[package]] 766 | name = "libc" 767 | version = "0.2.74" 768 | source = "registry+https://github.com/rust-lang/crates.io-index" 769 | checksum = "a2f02823cf78b754822df5f7f268fb59822e7296276d3e069d8e8cb26a14bd10" 770 | 771 | [[package]] 772 | name = "libloading" 773 | version = "0.5.2" 774 | source = "registry+https://github.com/rust-lang/crates.io-index" 775 | checksum = "f2b111a074963af1d37a139918ac6d49ad1d0d5e47f72fd55388619691a7d753" 776 | dependencies = [ 777 | "cc", 778 | "winapi", 779 | ] 780 | 781 | [[package]] 782 | name = "libloading" 783 | version = "0.7.3" 784 | source = "registry+https://github.com/rust-lang/crates.io-index" 785 | checksum = "efbc0f03f9a775e9f6aed295c6a1ba2253c5757a9e03d55c6caa46a681abcddd" 786 | dependencies = [ 787 | "cfg-if 1.0.0", 788 | "winapi", 789 | ] 790 | 791 | [[package]] 792 | name = "linked-hash-map" 793 | version = "0.5.3" 794 | source = "registry+https://github.com/rust-lang/crates.io-index" 795 | checksum = "8dd5a6d5999d9907cda8ed67bbd137d3af8085216c2ac62de5be860bd41f304a" 796 | 797 | [[package]] 798 | name = "lock_api" 799 | version = "0.4.1" 800 | source = "registry+https://github.com/rust-lang/crates.io-index" 801 | checksum = "28247cc5a5be2f05fbcd76dd0cf2c7d3b5400cb978a28042abcd4fa0b3f8261c" 802 | dependencies = [ 803 | "scopeguard", 804 | ] 805 | 806 | [[package]] 807 | name = "log" 808 | version = "0.4.11" 809 | source = "registry+https://github.com/rust-lang/crates.io-index" 810 | checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b" 811 | dependencies = [ 812 | "cfg-if 0.1.10", 813 | ] 814 | 815 | [[package]] 816 | name = "malloc_buf" 817 | version = "0.0.6" 818 | source = "registry+https://github.com/rust-lang/crates.io-index" 819 | checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" 820 | dependencies = [ 821 | "libc", 822 | ] 823 | 824 | [[package]] 825 | name = "maybe-uninit" 826 | version = "2.0.0" 827 | source = "registry+https://github.com/rust-lang/crates.io-index" 828 | checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" 829 | 830 | [[package]] 831 | name = "memchr" 832 | version = "2.3.3" 833 | source = "registry+https://github.com/rust-lang/crates.io-index" 834 | checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400" 835 | 836 | [[package]] 837 | name = "memoffset" 838 | version = "0.5.5" 839 | source = "registry+https://github.com/rust-lang/crates.io-index" 840 | checksum = "c198b026e1bbf08a937e94c6c60f9ec4a2267f5b0d2eec9c1b21b061ce2be55f" 841 | dependencies = [ 842 | "autocfg 1.0.0", 843 | ] 844 | 845 | [[package]] 846 | name = "metal" 847 | version = "0.23.1" 848 | source = "registry+https://github.com/rust-lang/crates.io-index" 849 | checksum = "e0514f491f4cc03632ab399ee01e2c1c1b12d3e1cf2d667c1ff5f87d6dcd2084" 850 | dependencies = [ 851 | "bitflags", 852 | "block", 853 | "core-graphics-types", 854 | "foreign-types", 855 | "log", 856 | "objc", 857 | ] 858 | 859 | [[package]] 860 | name = "miniz_oxide" 861 | version = "0.3.7" 862 | source = "registry+https://github.com/rust-lang/crates.io-index" 863 | checksum = "791daaae1ed6889560f8c4359194f56648355540573244a5448a83ba1ecc7435" 864 | dependencies = [ 865 | "adler32", 866 | ] 867 | 868 | [[package]] 869 | name = "naga" 870 | version = "0.8.2" 871 | source = "registry+https://github.com/rust-lang/crates.io-index" 872 | checksum = "1dfa3912b150e6bfb38a7e94d3f53b950a456a905bb8858590af02006e2e78be" 873 | dependencies = [ 874 | "bit-set", 875 | "bitflags", 876 | "codespan-reporting", 877 | "hexf-parse", 878 | "indexmap", 879 | "log", 880 | "num-traits", 881 | "petgraph", 882 | "rustc-hash", 883 | "spirv", 884 | "thiserror", 885 | ] 886 | 887 | [[package]] 888 | name = "num-traits" 889 | version = "0.2.12" 890 | source = "registry+https://github.com/rust-lang/crates.io-index" 891 | checksum = "ac267bcc07f48ee5f8935ab0d24f316fb722d7a1292e2913f0cc196b29ffd611" 892 | dependencies = [ 893 | "autocfg 1.0.0", 894 | ] 895 | 896 | [[package]] 897 | name = "num_cpus" 898 | version = "1.13.0" 899 | source = "registry+https://github.com/rust-lang/crates.io-index" 900 | checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" 901 | dependencies = [ 902 | "hermit-abi", 903 | "libc", 904 | ] 905 | 906 | [[package]] 907 | name = "objc" 908 | version = "0.2.7" 909 | source = "registry+https://github.com/rust-lang/crates.io-index" 910 | checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" 911 | dependencies = [ 912 | "malloc_buf", 913 | "objc_exception", 914 | ] 915 | 916 | [[package]] 917 | name = "objc_exception" 918 | version = "0.1.2" 919 | source = "registry+https://github.com/rust-lang/crates.io-index" 920 | checksum = "ad970fb455818ad6cba4c122ad012fae53ae8b4795f86378bce65e4f6bab2ca4" 921 | dependencies = [ 922 | "cc", 923 | ] 924 | 925 | [[package]] 926 | name = "once_cell" 927 | version = "1.9.0" 928 | source = "registry+https://github.com/rust-lang/crates.io-index" 929 | checksum = "da32515d9f6e6e489d7bc9d84c71b060db7247dc035bbe44eac88cf87486d8d5" 930 | 931 | [[package]] 932 | name = "ordered-float" 933 | version = "1.1.0" 934 | source = "registry+https://github.com/rust-lang/crates.io-index" 935 | checksum = "3741934be594d77de1c8461ebcbbe866f585ea616a9753aa78f2bdc69f0e4579" 936 | dependencies = [ 937 | "num-traits", 938 | ] 939 | 940 | [[package]] 941 | name = "owned_ttf_parser" 942 | version = "0.8.0" 943 | source = "registry+https://github.com/rust-lang/crates.io-index" 944 | checksum = "fb477c7fd2a3a6e04e1dc6ca2e4e9b04f2df702021dc5a5d1cf078c587dc59f7" 945 | dependencies = [ 946 | "ttf-parser", 947 | ] 948 | 949 | [[package]] 950 | name = "parking_lot" 951 | version = "0.11.0" 952 | source = "registry+https://github.com/rust-lang/crates.io-index" 953 | checksum = "a4893845fa2ca272e647da5d0e46660a314ead9c2fdd9a883aabc32e481a8733" 954 | dependencies = [ 955 | "instant", 956 | "lock_api", 957 | "parking_lot_core", 958 | ] 959 | 960 | [[package]] 961 | name = "parking_lot_core" 962 | version = "0.8.0" 963 | source = "registry+https://github.com/rust-lang/crates.io-index" 964 | checksum = "c361aa727dd08437f2f1447be8b59a33b0edd15e0fcee698f935613d9efbca9b" 965 | dependencies = [ 966 | "cfg-if 0.1.10", 967 | "cloudabi 0.1.0", 968 | "instant", 969 | "libc", 970 | "redox_syscall", 971 | "smallvec", 972 | "winapi", 973 | ] 974 | 975 | [[package]] 976 | name = "petgraph" 977 | version = "0.6.0" 978 | source = "registry+https://github.com/rust-lang/crates.io-index" 979 | checksum = "4a13a2fa9d0b63e5f22328828741e523766fff0ee9e779316902290dff3f824f" 980 | dependencies = [ 981 | "fixedbitset", 982 | "indexmap", 983 | ] 984 | 985 | [[package]] 986 | name = "pin-project" 987 | version = "0.4.29" 988 | source = "registry+https://github.com/rust-lang/crates.io-index" 989 | checksum = "9615c18d31137579e9ff063499264ddc1278e7b1982757ebc111028c4d1dc909" 990 | dependencies = [ 991 | "pin-project-internal", 992 | ] 993 | 994 | [[package]] 995 | name = "pin-project-internal" 996 | version = "0.4.29" 997 | source = "registry+https://github.com/rust-lang/crates.io-index" 998 | checksum = "044964427019eed9d49d9d5bbce6047ef18f37100ea400912a9fa4a3523ab12a" 999 | dependencies = [ 1000 | "proc-macro2", 1001 | "quote", 1002 | "syn", 1003 | ] 1004 | 1005 | [[package]] 1006 | name = "pin-utils" 1007 | version = "0.1.0" 1008 | source = "registry+https://github.com/rust-lang/crates.io-index" 1009 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 1010 | 1011 | [[package]] 1012 | name = "png" 1013 | version = "0.16.7" 1014 | source = "registry+https://github.com/rust-lang/crates.io-index" 1015 | checksum = "dfe7f9f1c730833200b134370e1d5098964231af8450bce9b78ee3ab5278b970" 1016 | dependencies = [ 1017 | "bitflags", 1018 | "crc32fast", 1019 | "deflate", 1020 | "miniz_oxide", 1021 | ] 1022 | 1023 | [[package]] 1024 | name = "ppv-lite86" 1025 | version = "0.2.8" 1026 | source = "registry+https://github.com/rust-lang/crates.io-index" 1027 | checksum = "237a5ed80e274dbc66f86bd59c1e25edc039660be53194b5fe0a482e0f2612ea" 1028 | 1029 | [[package]] 1030 | name = "proc-macro-hack" 1031 | version = "0.5.18" 1032 | source = "registry+https://github.com/rust-lang/crates.io-index" 1033 | checksum = "99c605b9a0adc77b7211c6b1f722dcb613d68d66859a44f3d485a6da332b0598" 1034 | 1035 | [[package]] 1036 | name = "proc-macro-nested" 1037 | version = "0.1.6" 1038 | source = "registry+https://github.com/rust-lang/crates.io-index" 1039 | checksum = "eba180dafb9038b050a4c280019bbedf9f2467b61e5d892dcad585bb57aadc5a" 1040 | 1041 | [[package]] 1042 | name = "proc-macro2" 1043 | version = "1.0.36" 1044 | source = "registry+https://github.com/rust-lang/crates.io-index" 1045 | checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029" 1046 | dependencies = [ 1047 | "unicode-xid", 1048 | ] 1049 | 1050 | [[package]] 1051 | name = "profiling" 1052 | version = "1.0.5" 1053 | source = "registry+https://github.com/rust-lang/crates.io-index" 1054 | checksum = "9145ac0af1d93c638c98c40cf7d25665f427b2a44ad0a99b1dccf3e2f25bb987" 1055 | 1056 | [[package]] 1057 | name = "quote" 1058 | version = "1.0.7" 1059 | source = "registry+https://github.com/rust-lang/crates.io-index" 1060 | checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37" 1061 | dependencies = [ 1062 | "proc-macro2", 1063 | ] 1064 | 1065 | [[package]] 1066 | name = "rand" 1067 | version = "0.6.5" 1068 | source = "registry+https://github.com/rust-lang/crates.io-index" 1069 | checksum = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca" 1070 | dependencies = [ 1071 | "autocfg 0.1.7", 1072 | "libc", 1073 | "rand_chacha 0.1.1", 1074 | "rand_core 0.4.2", 1075 | "rand_hc 0.1.0", 1076 | "rand_isaac", 1077 | "rand_jitter", 1078 | "rand_os", 1079 | "rand_pcg", 1080 | "rand_xorshift", 1081 | "winapi", 1082 | ] 1083 | 1084 | [[package]] 1085 | name = "rand" 1086 | version = "0.7.3" 1087 | source = "registry+https://github.com/rust-lang/crates.io-index" 1088 | checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" 1089 | dependencies = [ 1090 | "getrandom 0.1.14", 1091 | "libc", 1092 | "rand_chacha 0.2.2", 1093 | "rand_core 0.5.1", 1094 | "rand_hc 0.2.0", 1095 | ] 1096 | 1097 | [[package]] 1098 | name = "rand_chacha" 1099 | version = "0.1.1" 1100 | source = "registry+https://github.com/rust-lang/crates.io-index" 1101 | checksum = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" 1102 | dependencies = [ 1103 | "autocfg 0.1.7", 1104 | "rand_core 0.3.1", 1105 | ] 1106 | 1107 | [[package]] 1108 | name = "rand_chacha" 1109 | version = "0.2.2" 1110 | source = "registry+https://github.com/rust-lang/crates.io-index" 1111 | checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" 1112 | dependencies = [ 1113 | "ppv-lite86", 1114 | "rand_core 0.5.1", 1115 | ] 1116 | 1117 | [[package]] 1118 | name = "rand_core" 1119 | version = "0.3.1" 1120 | source = "registry+https://github.com/rust-lang/crates.io-index" 1121 | checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" 1122 | dependencies = [ 1123 | "rand_core 0.4.2", 1124 | ] 1125 | 1126 | [[package]] 1127 | name = "rand_core" 1128 | version = "0.4.2" 1129 | source = "registry+https://github.com/rust-lang/crates.io-index" 1130 | checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" 1131 | 1132 | [[package]] 1133 | name = "rand_core" 1134 | version = "0.5.1" 1135 | source = "registry+https://github.com/rust-lang/crates.io-index" 1136 | checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" 1137 | dependencies = [ 1138 | "getrandom 0.1.14", 1139 | ] 1140 | 1141 | [[package]] 1142 | name = "rand_hc" 1143 | version = "0.1.0" 1144 | source = "registry+https://github.com/rust-lang/crates.io-index" 1145 | checksum = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4" 1146 | dependencies = [ 1147 | "rand_core 0.3.1", 1148 | ] 1149 | 1150 | [[package]] 1151 | name = "rand_hc" 1152 | version = "0.2.0" 1153 | source = "registry+https://github.com/rust-lang/crates.io-index" 1154 | checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" 1155 | dependencies = [ 1156 | "rand_core 0.5.1", 1157 | ] 1158 | 1159 | [[package]] 1160 | name = "rand_isaac" 1161 | version = "0.1.1" 1162 | source = "registry+https://github.com/rust-lang/crates.io-index" 1163 | checksum = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08" 1164 | dependencies = [ 1165 | "rand_core 0.3.1", 1166 | ] 1167 | 1168 | [[package]] 1169 | name = "rand_jitter" 1170 | version = "0.1.4" 1171 | source = "registry+https://github.com/rust-lang/crates.io-index" 1172 | checksum = "1166d5c91dc97b88d1decc3285bb0a99ed84b05cfd0bc2341bdf2d43fc41e39b" 1173 | dependencies = [ 1174 | "libc", 1175 | "rand_core 0.4.2", 1176 | "winapi", 1177 | ] 1178 | 1179 | [[package]] 1180 | name = "rand_os" 1181 | version = "0.1.3" 1182 | source = "registry+https://github.com/rust-lang/crates.io-index" 1183 | checksum = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071" 1184 | dependencies = [ 1185 | "cloudabi 0.0.3", 1186 | "fuchsia-cprng", 1187 | "libc", 1188 | "rand_core 0.4.2", 1189 | "rdrand", 1190 | "winapi", 1191 | ] 1192 | 1193 | [[package]] 1194 | name = "rand_pcg" 1195 | version = "0.1.2" 1196 | source = "registry+https://github.com/rust-lang/crates.io-index" 1197 | checksum = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44" 1198 | dependencies = [ 1199 | "autocfg 0.1.7", 1200 | "rand_core 0.4.2", 1201 | ] 1202 | 1203 | [[package]] 1204 | name = "rand_xorshift" 1205 | version = "0.1.1" 1206 | source = "registry+https://github.com/rust-lang/crates.io-index" 1207 | checksum = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c" 1208 | dependencies = [ 1209 | "rand_core 0.3.1", 1210 | ] 1211 | 1212 | [[package]] 1213 | name = "range-alloc" 1214 | version = "0.1.1" 1215 | source = "registry+https://github.com/rust-lang/crates.io-index" 1216 | checksum = "a871f1e45a3a3f0c73fb60343c811238bb5143a81642e27c2ac7aac27ff01a63" 1217 | 1218 | [[package]] 1219 | name = "raw-window-handle" 1220 | version = "0.4.2" 1221 | source = "registry+https://github.com/rust-lang/crates.io-index" 1222 | checksum = "fba75eee94a9d5273a68c9e1e105d9cffe1ef700532325788389e5a83e2522b7" 1223 | dependencies = [ 1224 | "cty", 1225 | ] 1226 | 1227 | [[package]] 1228 | name = "rayon" 1229 | version = "1.3.1" 1230 | source = "registry+https://github.com/rust-lang/crates.io-index" 1231 | checksum = "62f02856753d04e03e26929f820d0a0a337ebe71f849801eea335d464b349080" 1232 | dependencies = [ 1233 | "autocfg 1.0.0", 1234 | "crossbeam-deque", 1235 | "either", 1236 | "rayon-core", 1237 | ] 1238 | 1239 | [[package]] 1240 | name = "rayon-core" 1241 | version = "1.7.1" 1242 | source = "registry+https://github.com/rust-lang/crates.io-index" 1243 | checksum = "e92e15d89083484e11353891f1af602cc661426deb9564c298b270c726973280" 1244 | dependencies = [ 1245 | "crossbeam-deque", 1246 | "crossbeam-queue", 1247 | "crossbeam-utils", 1248 | "lazy_static", 1249 | "num_cpus", 1250 | ] 1251 | 1252 | [[package]] 1253 | name = "rdrand" 1254 | version = "0.4.0" 1255 | source = "registry+https://github.com/rust-lang/crates.io-index" 1256 | checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" 1257 | dependencies = [ 1258 | "rand_core 0.3.1", 1259 | ] 1260 | 1261 | [[package]] 1262 | name = "redox_syscall" 1263 | version = "0.1.57" 1264 | source = "registry+https://github.com/rust-lang/crates.io-index" 1265 | checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" 1266 | 1267 | [[package]] 1268 | name = "remove_dir_all" 1269 | version = "0.5.3" 1270 | source = "registry+https://github.com/rust-lang/crates.io-index" 1271 | checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" 1272 | dependencies = [ 1273 | "winapi", 1274 | ] 1275 | 1276 | [[package]] 1277 | name = "renderdoc-sys" 1278 | version = "0.7.1" 1279 | source = "registry+https://github.com/rust-lang/crates.io-index" 1280 | checksum = "f1382d1f0a252c4bf97dc20d979a2fdd05b024acd7c2ed0f7595d7817666a157" 1281 | 1282 | [[package]] 1283 | name = "rustc-hash" 1284 | version = "1.1.0" 1285 | source = "registry+https://github.com/rust-lang/crates.io-index" 1286 | checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" 1287 | 1288 | [[package]] 1289 | name = "scopeguard" 1290 | version = "1.1.0" 1291 | source = "registry+https://github.com/rust-lang/crates.io-index" 1292 | checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" 1293 | 1294 | [[package]] 1295 | name = "sha2" 1296 | version = "0.7.1" 1297 | source = "registry+https://github.com/rust-lang/crates.io-index" 1298 | checksum = "9eb6be24e4c23a84d7184280d2722f7f2731fcdd4a9d886efbfe4413e4847ea0" 1299 | dependencies = [ 1300 | "block-buffer", 1301 | "byte-tools", 1302 | "digest", 1303 | "fake-simd", 1304 | ] 1305 | 1306 | [[package]] 1307 | name = "slab" 1308 | version = "0.4.2" 1309 | source = "registry+https://github.com/rust-lang/crates.io-index" 1310 | checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" 1311 | 1312 | [[package]] 1313 | name = "slotmap" 1314 | version = "1.0.6" 1315 | source = "registry+https://github.com/rust-lang/crates.io-index" 1316 | checksum = "e1e08e261d0e8f5c43123b7adf3e4ca1690d655377ac93a03b2c9d3e98de1342" 1317 | dependencies = [ 1318 | "version_check", 1319 | ] 1320 | 1321 | [[package]] 1322 | name = "smallvec" 1323 | version = "1.4.1" 1324 | source = "registry+https://github.com/rust-lang/crates.io-index" 1325 | checksum = "3757cb9d89161a2f24e1cf78efa0c1fcff485d18e3f55e0aa3480824ddaa0f3f" 1326 | 1327 | [[package]] 1328 | name = "spirv" 1329 | version = "0.2.0+1.5.4" 1330 | source = "registry+https://github.com/rust-lang/crates.io-index" 1331 | checksum = "246bfa38fe3db3f1dfc8ca5a2cdeb7348c78be2112740cc0ec8ef18b6d94f830" 1332 | dependencies = [ 1333 | "bitflags", 1334 | "num-traits", 1335 | ] 1336 | 1337 | [[package]] 1338 | name = "syn" 1339 | version = "1.0.85" 1340 | source = "registry+https://github.com/rust-lang/crates.io-index" 1341 | checksum = "a684ac3dcd8913827e18cd09a68384ee66c1de24157e3c556c9ab16d85695fb7" 1342 | dependencies = [ 1343 | "proc-macro2", 1344 | "quote", 1345 | "unicode-xid", 1346 | ] 1347 | 1348 | [[package]] 1349 | name = "synstructure" 1350 | version = "0.12.4" 1351 | source = "registry+https://github.com/rust-lang/crates.io-index" 1352 | checksum = "b834f2d66f734cb897113e34aaff2f1ab4719ca946f9a7358dba8f8064148701" 1353 | dependencies = [ 1354 | "proc-macro2", 1355 | "quote", 1356 | "syn", 1357 | "unicode-xid", 1358 | ] 1359 | 1360 | [[package]] 1361 | name = "tempfile" 1362 | version = "3.1.0" 1363 | source = "registry+https://github.com/rust-lang/crates.io-index" 1364 | checksum = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9" 1365 | dependencies = [ 1366 | "cfg-if 0.1.10", 1367 | "libc", 1368 | "rand 0.7.3", 1369 | "redox_syscall", 1370 | "remove_dir_all", 1371 | "winapi", 1372 | ] 1373 | 1374 | [[package]] 1375 | name = "termcolor" 1376 | version = "1.1.2" 1377 | source = "registry+https://github.com/rust-lang/crates.io-index" 1378 | checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4" 1379 | dependencies = [ 1380 | "winapi-util", 1381 | ] 1382 | 1383 | [[package]] 1384 | name = "thiserror" 1385 | version = "1.0.30" 1386 | source = "registry+https://github.com/rust-lang/crates.io-index" 1387 | checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417" 1388 | dependencies = [ 1389 | "thiserror-impl", 1390 | ] 1391 | 1392 | [[package]] 1393 | name = "thiserror-impl" 1394 | version = "1.0.30" 1395 | source = "registry+https://github.com/rust-lang/crates.io-index" 1396 | checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b" 1397 | dependencies = [ 1398 | "proc-macro2", 1399 | "quote", 1400 | "syn", 1401 | ] 1402 | 1403 | [[package]] 1404 | name = "ttf-parser" 1405 | version = "0.8.2" 1406 | source = "registry+https://github.com/rust-lang/crates.io-index" 1407 | checksum = "d973cfa0e6124166b50a1105a67c85de40bbc625082f35c0f56f84cb1fb0a827" 1408 | 1409 | [[package]] 1410 | name = "twox-hash" 1411 | version = "1.5.0" 1412 | source = "registry+https://github.com/rust-lang/crates.io-index" 1413 | checksum = "3bfd5b7557925ce778ff9b9ef90e3ade34c524b5ff10e239c69a42d546d2af56" 1414 | dependencies = [ 1415 | "rand 0.7.3", 1416 | ] 1417 | 1418 | [[package]] 1419 | name = "typenum" 1420 | version = "1.12.0" 1421 | source = "registry+https://github.com/rust-lang/crates.io-index" 1422 | checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33" 1423 | 1424 | [[package]] 1425 | name = "unicode-width" 1426 | version = "0.1.9" 1427 | source = "registry+https://github.com/rust-lang/crates.io-index" 1428 | checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" 1429 | 1430 | [[package]] 1431 | name = "unicode-xid" 1432 | version = "0.2.1" 1433 | source = "registry+https://github.com/rust-lang/crates.io-index" 1434 | checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" 1435 | 1436 | [[package]] 1437 | name = "version_check" 1438 | version = "0.9.4" 1439 | source = "registry+https://github.com/rust-lang/crates.io-index" 1440 | checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" 1441 | 1442 | [[package]] 1443 | name = "vst" 1444 | version = "0.2.0" 1445 | source = "registry+https://github.com/rust-lang/crates.io-index" 1446 | checksum = "9f282ef6465008618bb1e3de35760f01f9df361ea4f44a47144e96b8b67b9f28" 1447 | dependencies = [ 1448 | "bitflags", 1449 | "libc", 1450 | "libloading 0.5.2", 1451 | "log", 1452 | "num-traits", 1453 | ] 1454 | 1455 | [[package]] 1456 | name = "vst_window" 1457 | version = "0.3.0" 1458 | source = "registry+https://github.com/rust-lang/crates.io-index" 1459 | checksum = "35b4ad8242937daae1264cea9bc18d41a6618254bf0052cf70111e9c9e54880f" 1460 | dependencies = [ 1461 | "cocoa", 1462 | "objc", 1463 | "once_cell", 1464 | "raw-window-handle", 1465 | "winapi", 1466 | "xcb", 1467 | ] 1468 | 1469 | [[package]] 1470 | name = "wasi" 1471 | version = "0.9.0+wasi-snapshot-preview1" 1472 | source = "registry+https://github.com/rust-lang/crates.io-index" 1473 | checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" 1474 | 1475 | [[package]] 1476 | name = "wasi" 1477 | version = "0.10.2+wasi-snapshot-preview1" 1478 | source = "registry+https://github.com/rust-lang/crates.io-index" 1479 | checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" 1480 | 1481 | [[package]] 1482 | name = "wasm-bindgen" 1483 | version = "0.2.79" 1484 | source = "registry+https://github.com/rust-lang/crates.io-index" 1485 | checksum = "25f1af7423d8588a3d840681122e72e6a24ddbcb3f0ec385cac0d12d24256c06" 1486 | dependencies = [ 1487 | "cfg-if 1.0.0", 1488 | "wasm-bindgen-macro", 1489 | ] 1490 | 1491 | [[package]] 1492 | name = "wasm-bindgen-backend" 1493 | version = "0.2.79" 1494 | source = "registry+https://github.com/rust-lang/crates.io-index" 1495 | checksum = "8b21c0df030f5a177f3cba22e9bc4322695ec43e7257d865302900290bcdedca" 1496 | dependencies = [ 1497 | "bumpalo", 1498 | "lazy_static", 1499 | "log", 1500 | "proc-macro2", 1501 | "quote", 1502 | "syn", 1503 | "wasm-bindgen-shared", 1504 | ] 1505 | 1506 | [[package]] 1507 | name = "wasm-bindgen-futures" 1508 | version = "0.4.29" 1509 | source = "registry+https://github.com/rust-lang/crates.io-index" 1510 | checksum = "2eb6ec270a31b1d3c7e266b999739109abce8b6c87e4b31fcfcd788b65267395" 1511 | dependencies = [ 1512 | "cfg-if 1.0.0", 1513 | "js-sys", 1514 | "wasm-bindgen", 1515 | "web-sys", 1516 | ] 1517 | 1518 | [[package]] 1519 | name = "wasm-bindgen-macro" 1520 | version = "0.2.79" 1521 | source = "registry+https://github.com/rust-lang/crates.io-index" 1522 | checksum = "2f4203d69e40a52ee523b2529a773d5ffc1dc0071801c87b3d270b471b80ed01" 1523 | dependencies = [ 1524 | "quote", 1525 | "wasm-bindgen-macro-support", 1526 | ] 1527 | 1528 | [[package]] 1529 | name = "wasm-bindgen-macro-support" 1530 | version = "0.2.79" 1531 | source = "registry+https://github.com/rust-lang/crates.io-index" 1532 | checksum = "bfa8a30d46208db204854cadbb5d4baf5fcf8071ba5bf48190c3e59937962ebc" 1533 | dependencies = [ 1534 | "proc-macro2", 1535 | "quote", 1536 | "syn", 1537 | "wasm-bindgen-backend", 1538 | "wasm-bindgen-shared", 1539 | ] 1540 | 1541 | [[package]] 1542 | name = "wasm-bindgen-shared" 1543 | version = "0.2.79" 1544 | source = "registry+https://github.com/rust-lang/crates.io-index" 1545 | checksum = "3d958d035c4438e28c70e4321a2911302f10135ce78a9c7834c0cab4123d06a2" 1546 | 1547 | [[package]] 1548 | name = "web-sys" 1549 | version = "0.3.56" 1550 | source = "registry+https://github.com/rust-lang/crates.io-index" 1551 | checksum = "c060b319f29dd25724f09a2ba1418f142f539b2be99fbf4d2d5a8f7330afb8eb" 1552 | dependencies = [ 1553 | "js-sys", 1554 | "wasm-bindgen", 1555 | ] 1556 | 1557 | [[package]] 1558 | name = "wgpu" 1559 | version = "0.12.0" 1560 | source = "registry+https://github.com/rust-lang/crates.io-index" 1561 | checksum = "b97cd781ff044d6d697b632a2e212032c2e957d1afaa21dbf58069cbb8f78567" 1562 | dependencies = [ 1563 | "arrayvec", 1564 | "js-sys", 1565 | "log", 1566 | "naga", 1567 | "parking_lot", 1568 | "raw-window-handle", 1569 | "smallvec", 1570 | "wasm-bindgen", 1571 | "wasm-bindgen-futures", 1572 | "web-sys", 1573 | "wgpu-core", 1574 | "wgpu-hal", 1575 | "wgpu-types", 1576 | ] 1577 | 1578 | [[package]] 1579 | name = "wgpu-core" 1580 | version = "0.12.2" 1581 | source = "registry+https://github.com/rust-lang/crates.io-index" 1582 | checksum = "c4688c000eb841ca55f7b35db659b78d6e1cd77d7caf8fb929f4e181f754047d" 1583 | dependencies = [ 1584 | "arrayvec", 1585 | "bitflags", 1586 | "cfg_aliases", 1587 | "codespan-reporting", 1588 | "copyless", 1589 | "fxhash", 1590 | "log", 1591 | "naga", 1592 | "parking_lot", 1593 | "profiling", 1594 | "raw-window-handle", 1595 | "smallvec", 1596 | "thiserror", 1597 | "wgpu-hal", 1598 | "wgpu-types", 1599 | ] 1600 | 1601 | [[package]] 1602 | name = "wgpu-hal" 1603 | version = "0.12.2" 1604 | source = "registry+https://github.com/rust-lang/crates.io-index" 1605 | checksum = "92e33cb9c380dd1166f316dfc511ad9646f72cf2deb47e90bd714db3617a6998" 1606 | dependencies = [ 1607 | "arrayvec", 1608 | "ash", 1609 | "bit-set", 1610 | "bitflags", 1611 | "block", 1612 | "core-graphics-types", 1613 | "d3d12", 1614 | "foreign-types", 1615 | "fxhash", 1616 | "glow", 1617 | "gpu-alloc", 1618 | "gpu-descriptor", 1619 | "inplace_it", 1620 | "js-sys", 1621 | "khronos-egl", 1622 | "libloading 0.7.3", 1623 | "log", 1624 | "metal", 1625 | "naga", 1626 | "objc", 1627 | "parking_lot", 1628 | "profiling", 1629 | "range-alloc", 1630 | "raw-window-handle", 1631 | "renderdoc-sys", 1632 | "thiserror", 1633 | "wasm-bindgen", 1634 | "web-sys", 1635 | "wgpu-types", 1636 | "winapi", 1637 | ] 1638 | 1639 | [[package]] 1640 | name = "wgpu-types" 1641 | version = "0.12.0" 1642 | source = "registry+https://github.com/rust-lang/crates.io-index" 1643 | checksum = "549533d9e1cdd4b4cda7718d33ff500fc4c34b5467b71d76b547ae0324f3b2a2" 1644 | dependencies = [ 1645 | "bitflags", 1646 | ] 1647 | 1648 | [[package]] 1649 | name = "wgpu_glyph" 1650 | version = "0.16.0" 1651 | source = "registry+https://github.com/rust-lang/crates.io-index" 1652 | checksum = "8134edb15ae465caf308125646c9e98bdef7398cdefc69227ac77a5eb795e7fe" 1653 | dependencies = [ 1654 | "bytemuck", 1655 | "glyph_brush", 1656 | "log", 1657 | "wgpu", 1658 | ] 1659 | 1660 | [[package]] 1661 | name = "winapi" 1662 | version = "0.3.9" 1663 | source = "registry+https://github.com/rust-lang/crates.io-index" 1664 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 1665 | dependencies = [ 1666 | "winapi-i686-pc-windows-gnu", 1667 | "winapi-x86_64-pc-windows-gnu", 1668 | ] 1669 | 1670 | [[package]] 1671 | name = "winapi-i686-pc-windows-gnu" 1672 | version = "0.4.0" 1673 | source = "registry+https://github.com/rust-lang/crates.io-index" 1674 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 1675 | 1676 | [[package]] 1677 | name = "winapi-util" 1678 | version = "0.1.5" 1679 | source = "registry+https://github.com/rust-lang/crates.io-index" 1680 | checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" 1681 | dependencies = [ 1682 | "winapi", 1683 | ] 1684 | 1685 | [[package]] 1686 | name = "winapi-x86_64-pc-windows-gnu" 1687 | version = "0.4.0" 1688 | source = "registry+https://github.com/rust-lang/crates.io-index" 1689 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 1690 | 1691 | [[package]] 1692 | name = "xcb" 1693 | version = "0.9.0" 1694 | source = "registry+https://github.com/rust-lang/crates.io-index" 1695 | checksum = "62056f63138b39116f82a540c983cc11f1c90cd70b3d492a70c25eaa50bd22a6" 1696 | dependencies = [ 1697 | "libc", 1698 | "log", 1699 | ] 1700 | 1701 | [[package]] 1702 | name = "xi-unicode" 1703 | version = "0.2.1" 1704 | source = "registry+https://github.com/rust-lang/crates.io-index" 1705 | checksum = "e71b85d8b1b8bfaf4b5c834187554d201a8cd621c2bbfa33efd41a3ecabd48b2" 1706 | 1707 | [[package]] 1708 | name = "zerocopy" 1709 | version = "0.3.0" 1710 | source = "registry+https://github.com/rust-lang/crates.io-index" 1711 | checksum = "6580539ad917b7c026220c4b3f2c08d52ce54d6ce0dc491e66002e35388fab46" 1712 | dependencies = [ 1713 | "byteorder", 1714 | "zerocopy-derive", 1715 | ] 1716 | 1717 | [[package]] 1718 | name = "zerocopy-derive" 1719 | version = "0.2.0" 1720 | source = "registry+https://github.com/rust-lang/crates.io-index" 1721 | checksum = "d498dbd1fd7beb83c86709ae1c33ca50942889473473d287d56ce4770a18edfb" 1722 | dependencies = [ 1723 | "proc-macro2", 1724 | "syn", 1725 | "synstructure", 1726 | ] 1727 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ampli-fe" 3 | version = "0.1.1" 4 | authors = ["Anton Lazarev "] 5 | edition = "2018" 6 | resolver = "2" 7 | license = "MIT OR Apache-2.0" 8 | build = "build.rs" 9 | 10 | # No reason to distribute a VST plugin on crates.io. 11 | publish = false 12 | 13 | [lib] 14 | crate-type = ["cdylib"] 15 | 16 | [dependencies] 17 | vst = "^ 0.2" 18 | vst_window = "^ 0.3" 19 | once_cell = "^ 1.4" 20 | wgpu = { version = "^ 0.12", features = ["spirv"] } 21 | wgpu_glyph = "^ 0.16" 22 | raw-window-handle = "^ 0.4" 23 | zerocopy = "^ 0.3" 24 | cgmath = "^ 0.17" 25 | futures = "^ 0.3" 26 | png = "^ 0.16" 27 | 28 | [build-dependencies] 29 | glsl-to-spirv = "^ 0.1" 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Anton Lazarev 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ampli-Fe 2 | 3 | 4 | 5 | 6 | 7 | ampli-Fe is a fully cross-platform [VST2](https://en.wikipedia.org/wiki/Virtual_Studio_Technology) plugin written in Rust. 8 | It works on Linux, macOS, Windows, without any conditionally compiled code. 9 | 10 | It features a fully-customized editor UI with an interactive knob and corresponding numerical value readout. 11 | 12 | ampli-Fe's code is well-documented and freely licensed - feel free to use it as a starting point for your next VST2 plugin*! 13 | 14 | _*Please note that VST2 has been [deprecated](https://helpcenter.steinberg.de/hc/en-us/articles/4409561018258-VST-2-Discontinued). It may continue to work in some DAWs, but it is no longer recommended for new audio plugin development._ 15 | 16 | ## Functionality 17 | 18 | ![Screenshot of ampli-Fe's custom editor UI](/assets/images/readme_screenshot.png) 19 | 20 | ampli-Fe is a VST2 effect plugin. 21 | It can be added to tracks within a Digital Audio Workstation, or DAW. 22 | 23 | ampli-Fe has a single knob, that can be "turned" by clicking and dragging up or down. 24 | Turning the knob will multiply the track's playback volume by a configurable amount between 0 and 2. 25 | The current value of the knob is displayed on the UI as a reference. 26 | 27 | ## Design overview 28 | 29 | ampli-Fe was written to demonstrate usage of the [`vst_window`](https://crates.io/crates/vst_window) crate for custom, cross-platform plugin interfaces, along with the excellent [`vst`](https://crates.io/crates/vst) bindings for Rust. 30 | The graphics for the editor interface are drawn using [`wgpu`](https://crates.io/crates/wgpu). 31 | 32 | For optimal thread-safety and performance, the plugin's functionality is split between three major components. 33 | 34 | ### Plugin state management 35 | 36 | The [`plugin_state` module](/src/plugin_state.rs) receives a subset of VST API events that can occur on both the UI thread and audio processing thread, and maintains the "ground-truth" representation of the plugin's customized parameters. 37 | It's used to coordinate parameter changes across the host DAW, audio processing logic, and editor interface. 38 | It uses thread-safe interior mutability to ensure that its managed memory is always consistent. 39 | 40 | ### Digital signal processing 41 | 42 | The [`dsp` module](/src/dsp/mod.rs) processes incoming audio and returns it to the host DAW. 43 | It runs fully on the audio processing thread, and its parameters are updated from the plugin state via message passing to avoid performance-costly locking. 44 | 45 | ### Editor interface 46 | 47 | The [`editor` module](/src/editor/mod.rs) displays the plugin state visually and allows interactive editing of the plugin's parameters. 48 | It runs fully on the UI thread. 49 | While open, the editor subscribes to receive update messages from the plugin state, but it also has direct access to the plugin state to allow it to "push" changes to the rest of the plugin in real time. 50 | 51 | ## Build instructions 52 | 53 | Running `cargo build --release` will automatically compile the correct plugin for your current OS platform. 54 | The resulting plugin binary can be found in the `target/release` directory. 55 | 56 | Once the plugin is compiled, you'll need to make it accessible to your DAW, which can vary by platform. 57 | 58 | ### Linux 59 | 60 | The shared object file `target/release/libampli_fe.so` can be copied directly to the user or system VST plugin directory. 61 | This directory may vary by distribution and host DAW, so be sure to check the documentation and settings for each. 62 | 63 | ### macOS 64 | 65 | macOS requires an extra step to "bundle" VST plugins. 66 | After compiling, run the included `bundle_macos.sh` script, which will generate and populate the `ampli-Fe.vst` directory. 67 | That directory can be copied directly to the user or system VST plugin directory, usually either `~/Library/Audio/Plug-Ins/VST/` or `/Library/Audio/Plug-Ins/VST/`. 68 | 69 | ### Windows 70 | 71 | The dynamic-link library file `target/release/ampli_fe.dll` can be copied directly to the dedicated VST plugin directory. 72 | This directory may vary by host DAW, so be sure to check its documentation and settings. 73 | 74 | ## About the name 75 | 76 | ampli-Fe provides a volume adjustment knob, and is written in the Rust programming language. 77 | Its name is a contraction of "amplify" (to increase volume) and "Fe" (the chemical symbol for iron, a natural component of rust). 78 | -------------------------------------------------------------------------------- /assets/fonts/LICENSE.md: -------------------------------------------------------------------------------- 1 | The font is licensed under SIL OFL Version 1.1. 2 | 3 | The support code is licensed under Berkeley Software Distribution license. 4 | 5 | --- 6 | --- 7 | 8 | Copyright (c) 2015-2020 Belleve Invis (belleve@typeof.net). 9 | 10 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 11 | * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 12 | * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 13 | * Neither the name of Belleve Invis nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BELLEVE INVIS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 16 | 17 | ----------------------- 18 | 19 | --- 20 | 21 | Copyright 2015-2020, Belleve Invis (belleve@typeof.net). 22 | 23 | This Font Software is licensed under the SIL Open Font License, Version 1.1. 24 | 25 | This license is copied below, and is also available with a FAQ at: 26 | http://scripts.sil.org/OFL 27 | 28 | -------------------------- 29 | 30 | 31 | SIL Open Font License v1.1 32 | ==================================================== 33 | 34 | 35 | Preamble 36 | ---------- 37 | 38 | The goals of the Open Font License (OFL) are to stimulate worldwide 39 | development of collaborative font projects, to support the font creation 40 | efforts of academic and linguistic communities, and to provide a free and 41 | open framework in which fonts may be shared and improved in partnership 42 | with others. 43 | 44 | The OFL allows the licensed fonts to be used, studied, modified and 45 | redistributed freely as long as they are not sold by themselves. The 46 | fonts, including any derivative works, can be bundled, embedded, 47 | redistributed and/or sold with any software provided that any reserved 48 | names are not used by derivative works. The fonts and derivatives, 49 | however, cannot be released under any other type of license. The 50 | requirement for fonts to remain under this license does not apply 51 | to any document created using the fonts or their derivatives. 52 | 53 | 54 | Definitions 55 | ------------- 56 | 57 | `"Font Software"` refers to the set of files released by the Copyright 58 | Holder(s) under this license and clearly marked as such. This may 59 | include source files, build scripts and documentation. 60 | 61 | `"Reserved Font Name"` refers to any names specified as such after the 62 | copyright statement(s). 63 | 64 | `"Original Version"` refers to the collection of Font Software components as 65 | distributed by the Copyright Holder(s). 66 | 67 | `"Modified Version"` refers to any derivative made by adding to, deleting, 68 | or substituting -- in part or in whole -- any of the components of the 69 | Original Version, by changing formats or by porting the Font Software to a 70 | new environment. 71 | 72 | `"Author"` refers to any designer, engineer, programmer, technical 73 | writer or other person who contributed to the Font Software. 74 | 75 | 76 | Permission & Conditions 77 | ------------------------ 78 | 79 | Permission is hereby granted, free of charge, to any person obtaining 80 | a copy of the Font Software, to use, study, copy, merge, embed, modify, 81 | redistribute, and sell modified and unmodified copies of the Font 82 | Software, subject to the following conditions: 83 | 84 | 1. Neither the Font Software nor any of its individual components, 85 | in Original or Modified Versions, may be sold by itself. 86 | 87 | 2. Original or Modified Versions of the Font Software may be bundled, 88 | redistributed and/or sold with any software, provided that each copy 89 | contains the above copyright notice and this license. These can be 90 | included either as stand-alone text files, human-readable headers or 91 | in the appropriate machine-readable metadata fields within text or 92 | binary files as long as those fields can be easily viewed by the user. 93 | 94 | 3. No Modified Version of the Font Software may use the Reserved Font 95 | Name(s) unless explicit written permission is granted by the corresponding 96 | Copyright Holder. This restriction only applies to the primary font name as 97 | presented to the users. 98 | 99 | 4. The name(s) of the Copyright Holder(s) or the Author(s) of the Font 100 | Software shall not be used to promote, endorse or advertise any 101 | Modified Version, except to acknowledge the contribution(s) of the 102 | Copyright Holder(s) and the Author(s) or with their explicit written 103 | permission. 104 | 105 | 5. The Font Software, modified or unmodified, in part or in whole, 106 | must be distributed entirely under this license, and must not be 107 | distributed under any other license. The requirement for fonts to 108 | remain under this license does not apply to any document created 109 | using the Font Software. 110 | 111 | 112 | 113 | Termination 114 | ----------- 115 | 116 | This license becomes null and void if any of the above conditions are 117 | not met. 118 | 119 | 120 | DISCLAIMER 121 | 122 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 123 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 124 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 125 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 126 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 127 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 128 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 129 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 130 | OTHER DEALINGS IN THE FONT SOFTWARE. 131 | -------------------------------------------------------------------------------- /assets/fonts/iosevka-Iosevka-medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antonok-edm/ampli-Fe/5fa734bd808e43d2c3a515c82f6739c65c547b81/assets/fonts/iosevka-Iosevka-medium.ttf -------------------------------------------------------------------------------- /assets/images/bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antonok-edm/ampli-Fe/5fa734bd808e43d2c3a515c82f6739c65c547b81/assets/images/bg.png -------------------------------------------------------------------------------- /assets/images/bg.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antonok-edm/ampli-Fe/5fa734bd808e43d2c3a515c82f6739c65c547b81/assets/images/bg.xcf -------------------------------------------------------------------------------- /assets/images/pointer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antonok-edm/ampli-Fe/5fa734bd808e43d2c3a515c82f6739c65c547b81/assets/images/pointer.png -------------------------------------------------------------------------------- /assets/images/pointer.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antonok-edm/ampli-Fe/5fa734bd808e43d2c3a515c82f6739c65c547b81/assets/images/pointer.xcf -------------------------------------------------------------------------------- /assets/images/readme_screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antonok-edm/ampli-Fe/5fa734bd808e43d2c3a515c82f6739c65c547b81/assets/images/readme_screenshot.png -------------------------------------------------------------------------------- /assets/shaders/shader.frag: -------------------------------------------------------------------------------- 1 | #version 450 2 | #extension GL_ARB_separate_shader_objects : enable 3 | 4 | // Fragment shader that uses a texture coordinate to sample from a texture 5 | // uniform. 6 | 7 | layout(location = 0) in vec2 textureCoord; 8 | layout(set = 0, binding = 1) uniform texture2D backgroundTexture; 9 | layout(set = 0, binding = 2) uniform sampler textureSampler; 10 | 11 | layout(location = 0) out vec4 outColor; 12 | 13 | void main() { 14 | outColor = texture(sampler2D(backgroundTexture, textureSampler), textureCoord); 15 | } 16 | -------------------------------------------------------------------------------- /assets/shaders/shader.vert: -------------------------------------------------------------------------------- 1 | #version 450 2 | #extension GL_ARB_separate_shader_objects : enable 3 | 4 | // Vertex shader that applies a uniform matrix transformation to the position 5 | // and directly copies the input texture coordinate to the following fragment 6 | // shader. 7 | 8 | layout(location = 0) in vec2 position2D; 9 | layout(location = 1) in vec2 textureCoordInput; 10 | 11 | layout(location = 0) out vec2 textureCoordOutput; 12 | 13 | layout(set = 0, binding = 0) uniform Transform { 14 | mat4 transform; 15 | }; 16 | 17 | out gl_PerVertex { 18 | vec4 gl_Position; 19 | }; 20 | 21 | void main() { 22 | textureCoordOutput = textureCoordInput; 23 | 24 | gl_Position = transform * vec4(position2D, 0.0, 1.0); 25 | } 26 | -------------------------------------------------------------------------------- /build.rs: -------------------------------------------------------------------------------- 1 | //! This build script compiles all shaders from `SHADER_SRC` into SPIR-V representations in 2 | //! `SPIRV_OUT`. 3 | 4 | use std::io::Read; 5 | 6 | const SHADER_SRC: &str = "assets/shaders"; 7 | const SPIRV_OUT: &str = "assets/generated/spirv"; 8 | 9 | fn main() -> Result<(), Box> { 10 | use glsl_to_spirv::ShaderType; 11 | 12 | println!("cargo:rerun-if-changed={}", SHADER_SRC); 13 | 14 | std::fs::create_dir_all(SPIRV_OUT)?; 15 | 16 | let shader_src_path = std::path::Path::new(SHADER_SRC); 17 | for shader_file in ["shader.vert", "shader.frag"].iter() { 18 | let shader_path = shader_src_path.join(shader_file); 19 | 20 | let shader_type = match shader_path 21 | .extension() 22 | .unwrap_or_else(|| panic!("Shader {:?} has no extension", shader_path)) 23 | .to_string_lossy() 24 | .as_ref() 25 | { 26 | "vert" => ShaderType::Vertex, 27 | "frag" => ShaderType::Fragment, 28 | _ => panic!("Unrecognized shader type for {:?}", shader_path), 29 | }; 30 | 31 | let source = std::fs::read_to_string(&shader_path)?; 32 | let mut compiled_file = glsl_to_spirv::compile(&source, shader_type)?; 33 | 34 | let mut compiled_bytes = Vec::new(); 35 | compiled_file.read_to_end(&mut compiled_bytes)?; 36 | 37 | let out_path = format!( 38 | "{}/{}.spv", 39 | SPIRV_OUT, 40 | shader_path.file_name().unwrap().to_string_lossy() 41 | ); 42 | 43 | std::fs::write(&out_path, &compiled_bytes)?; 44 | } 45 | 46 | Ok(()) 47 | } 48 | -------------------------------------------------------------------------------- /bundle_macos.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Modified lightly from 4 | # https://github.com/RustAudio/vst-rs/blob/master/osx_vst_bundler.sh 5 | 6 | SYNTH_NAME=ampli-Fe 7 | BUILD_OUTPUT=target/release/libampli_fe.dylib 8 | 9 | # Make the bundle folder 10 | mkdir -p "$SYNTH_NAME.vst/Contents/MacOS" 11 | 12 | # Create the PkgInfo 13 | echo "BNDL????" > "$SYNTH_NAME.vst/Contents/PkgInfo" 14 | 15 | #build the Info.Plist 16 | echo " 17 | 18 | 19 | 20 | CFBundleDevelopmentRegion 21 | English 22 | 23 | CFBundleExecutable 24 | $SYNTH_NAME 25 | 26 | CFBundleGetInfoString 27 | vst 28 | 29 | CFBundleIconFile 30 | 31 | 32 | CFBundleIdentifier 33 | com.antonok.$SYNTH_NAME 34 | 35 | CFBundleInfoDictionaryVersion 36 | 6.0 37 | 38 | CFBundleName 39 | $SYNTH_NAME 40 | 41 | CFBundlePackageType 42 | BNDL 43 | 44 | CFBundleVersion 45 | 1.0 46 | 47 | CFBundleSignature 48 | $((RANDOM % 9999)) 49 | 50 | CSResourcesFileMapped 51 | 52 | 53 | 54 | " > "$SYNTH_NAME.vst/Contents/Info.plist" 55 | 56 | # move the provided library to the correct location 57 | cp "$BUILD_OUTPUT" "$SYNTH_NAME.vst/Contents/MacOS/$SYNTH_NAME" 58 | 59 | echo "Created bundle $SYNTH_NAME.vst" 60 | -------------------------------------------------------------------------------- /src/dsp/mod.rs: -------------------------------------------------------------------------------- 1 | //! The plugin's digital signal processing is fully implemented within this module. 2 | //! 3 | //! All updates to input parameters are received through message passing to avoid thread locking 4 | //! during audio processing. In particular, note that parameter smoothing is considered within the 5 | //! scope of audio processing rather than state management. This module uses the `SmoothedRange` 6 | //! struct to ensure that parameters are consistently and efficiently interpolated while minimizing 7 | //! the number of messages passed. 8 | 9 | use crate::plugin_state::StateUpdate; 10 | use std::sync::mpsc::Receiver; 11 | 12 | mod smoothed; 13 | use smoothed::SmoothedRange; 14 | 15 | use vst::buffer::AudioBuffer; 16 | 17 | /// Handles all audio processing algorithms for the plugin. 18 | pub(super) struct PluginDsp { 19 | amplitude_range: SmoothedRange, 20 | amplitude: f32, 21 | 22 | messages_from_params: Receiver, 23 | } 24 | 25 | impl PluginDsp { 26 | pub fn new(incoming_messages: Receiver) -> Self { 27 | Self { 28 | amplitude_range: SmoothedRange::new(0.5), 29 | amplitude: 1., 30 | 31 | messages_from_params: incoming_messages, 32 | } 33 | } 34 | 35 | /// Applies any incoming state update events to the audio generation algorithm, and then writes 36 | /// processed audio into the output buffer. 37 | pub fn process(&mut self, buffer: &mut AudioBuffer) { 38 | // First, get any new changes to parameter ranges. 39 | while let Ok(message) = self.messages_from_params.try_recv() { 40 | match message { 41 | StateUpdate::SetKnob(v) => self.amplitude_range.set(v), 42 | } 43 | } 44 | 45 | // To take advantage of SIMD auto-vectorization, and for consistent parameter smoothing, 46 | // audio is processed in "chunks" of 16 samples at a time. The number of samples requested 47 | // by a host will generally be a multiple of 16, although the buffer may be truncated early 48 | // in some cases. 49 | // 50 | // This approach is overly complex for such a simple use-case, but can be particularly 51 | // useful for reducing unnecessary re-computation with many parameters. 52 | let num_samples = buffer.samples(); 53 | let num_chunks = num_samples / 16; 54 | let extra_samples = num_samples % 16; 55 | let num_channels = buffer.input_count(); 56 | 57 | let (inputs, mut outputs) = buffer.split(); 58 | for chunk_start in (0..num_chunks).map(|i| i * 16) { 59 | self.amplitude_range.process(); 60 | 61 | // Prepare the chunk's base amplitude value by placing it into a 16-element array, then 62 | // linearly interpolate them towards the next value if the amplitude has recently been 63 | // changed. 64 | let mut chunk_amplitudes = [self.amplitude; 16]; 65 | if let Some(amplitude_range) = self.amplitude_range.get_new_value() { 66 | let new_amplitude = amplitude_range * 2.; 67 | let per_sample_difference = (new_amplitude - self.amplitude) / 16.; 68 | chunk_amplitudes 69 | .iter_mut() 70 | .zip((0..16).map(|i| i as f32 * per_sample_difference)) 71 | .for_each(|(amplitude, difference)| *amplitude += difference); 72 | self.amplitude = new_amplitude; 73 | } 74 | 75 | // Then, calculate each output sample by multiplying each input sample by its 76 | // corresponding amplitude value. 77 | for channel in 0..num_channels { 78 | for (i, amplitude) in chunk_amplitudes.iter().enumerate() { 79 | outputs[channel][chunk_start + i] = 80 | inputs[channel][chunk_start + i] * amplitude; 81 | } 82 | } 83 | } 84 | 85 | // Finally, process the final <16 samples, if any. 86 | for i in 0..extra_samples { 87 | for channel in 0..num_channels { 88 | // We could precompute extra interpolated amplitude values into a rollover buffer, 89 | // but it's simpler to approximate by just reusing the last known amplitude value. 90 | outputs[channel][num_chunks * 16 + i] = 91 | inputs[channel][num_chunks * 16 + i] * self.amplitude; 92 | } 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/dsp/smoothed.rs: -------------------------------------------------------------------------------- 1 | /// A `SmoothedRange` will get closer to its target value by this proportion of the difference 2 | /// between the current and target value on every `process` call. 3 | const FILTER_FACTOR: f32 = 0.005; 4 | /// If a `SmoothedRange`'s value is at least this close to its target, it will "snap" to the 5 | /// target and stop smoothing. 6 | const SMOOTH_EPSILON: f32 = 0.001; 7 | 8 | /// Represents a value between 0. and 1. that exponentially interpolates towards a settable target 9 | /// value whenever it is processed. Allows efficient calculation of derived values by only 10 | /// returning values when it has been updated or smoothed. 11 | #[derive(Clone, Default)] 12 | pub(super) struct SmoothedRange { 13 | value: f32, 14 | target: f32, 15 | 16 | needs_smooth: bool, 17 | did_change: bool, 18 | } 19 | 20 | impl SmoothedRange { 21 | pub fn new(starting_value: f32) -> Self { 22 | Self { 23 | value: starting_value, 24 | target: starting_value, 25 | needs_smooth: false, 26 | did_change: true, 27 | } 28 | } 29 | 30 | /// Smoothes this parameter towards its target value if necessary. 31 | pub fn process(&mut self) { 32 | if self.needs_smooth { 33 | self.did_change = true; 34 | self.value += (self.target - self.value) * FILTER_FACTOR; 35 | if (self.value - self.target).abs() < SMOOTH_EPSILON { 36 | self.value = self.target; 37 | self.needs_smooth = false; 38 | } 39 | } else { 40 | self.did_change = false; 41 | } 42 | } 43 | 44 | /// Provides a new target to smooth towards. 45 | pub fn set(&mut self, value: f32) { 46 | self.target = value; 47 | self.needs_smooth = true; 48 | self.did_change = true; 49 | } 50 | 51 | /// Return this parameter's value if it is different from its previous value because of 52 | /// smoothing or updating. 53 | pub fn get_new_value(&mut self) -> Option { 54 | if self.did_change { 55 | Some(self.value) 56 | } else { 57 | None 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/editor/interface/graphics.rs: -------------------------------------------------------------------------------- 1 | //! Fully handles drawing of custom graphics to the editor window. 2 | //! 3 | //! In this plugin, rendering is achieved with `wgpu`, which provides a very low-level API. This is 4 | //! very flexible, but requires a lot of setup! 5 | 6 | use cgmath::{prelude::SquareMatrix, Matrix4, Vector3}; 7 | use once_cell::sync::Lazy; 8 | use wgpu::util::DeviceExt; 9 | use wgpu_glyph::{GlyphBrush, GlyphBrushBuilder}; 10 | use zerocopy::AsBytes; 11 | 12 | use super::{ 13 | image_consts::{ORIG_BG_SIZE_X, ORIG_BG_SIZE_Y, ORIG_KNOB_RADIUS, ORIG_KNOB_X, ORIG_KNOB_Y}, 14 | SCALE, SIZE_X, SIZE_Y, 15 | }; 16 | 17 | const MSAA_SAMPLES: u32 = 4; 18 | 19 | /// Contains all handles to GPU resources required for rendering the editor interface. 20 | pub(super) struct Renderer { 21 | device: wgpu::Device, 22 | queue: wgpu::Queue, 23 | multisampled_framebuffer: wgpu::TextureView, 24 | surface: wgpu::Surface, 25 | 26 | text_renderer: GlyphBrush<()>, 27 | /// Required by `wgpu_glyph` 28 | local_pool: futures::executor::LocalPool, 29 | /// Required by `wgpu_glyph` 30 | staging_belt: wgpu::util::StagingBelt, 31 | 32 | pipeline: wgpu::RenderPipeline, 33 | rectangle_index_buffer: wgpu::Buffer, 34 | rectangle_vertex_buffer: wgpu::Buffer, 35 | 36 | background_bind_group: wgpu::BindGroup, 37 | 38 | pointer_bind_group: wgpu::BindGroup, 39 | pointer_transform_buffer: wgpu::Buffer, 40 | } 41 | 42 | /// Low-level representation of a point in 3D space. This representation is designed to be shared 43 | /// directly with GPU memory for use in shaders. 44 | #[repr(C)] 45 | #[derive(Clone, Copy, AsBytes)] 46 | struct Vertex { 47 | /// `[x, y, z, w]` position. For a 2D interface, only `x` and `y` are important. 48 | _pos: [f32; 4], 49 | /// `[u, v]` texture coordinate position used to map parts of an image to a piece of geometry. 50 | _quad_coord: [f32; 2], 51 | } 52 | 53 | impl Vertex { 54 | pub fn new(x: f32, y: f32, u: f32, v: f32) -> Self { 55 | Vertex { 56 | _pos: [x, y, 0., 0.], 57 | _quad_coord: [u, v], 58 | } 59 | } 60 | } 61 | 62 | /// Low-level representation of a 4x4 matrix for transformations in 3D space. This representation 63 | /// is designed to be shared directly with GPU memory for use in shaders. 64 | #[repr(C)] 65 | #[derive(Clone, Copy, AsBytes)] 66 | struct TransformUniform { 67 | transform: [[f32; 4]; 4], 68 | } 69 | 70 | const BACKGROUND_IMAGE: &[u8] = include_bytes!("../../../assets/images/bg.png"); 71 | const POINTER_IMAGE: &[u8] = include_bytes!("../../../assets/images/pointer.png"); 72 | const FONT: &[u8] = include_bytes!("../../../assets/fonts/iosevka-Iosevka-medium.ttf"); 73 | const FONT_COLOR: [f32; 4] = [1.0, 0.51, 0.0, 1.0]; 74 | 75 | const TEXT_RIGHT_ANCHOR: f32 = 460. * SCALE as f32; 76 | const TEXT_CENTER_Y_ANCHOR: f32 = 500. * SCALE as f32; 77 | 78 | /// Scales and moves the original knob image from ([-1,1],[-1,1]) to its correct position on the 79 | /// background image. 80 | static SCALE_MOVE_KNOB_TRANSFORM: Lazy> = Lazy::new(|| { 81 | Matrix4::from_translation(Vector3::new( 82 | 2. * (ORIG_KNOB_X - ORIG_BG_SIZE_X / 2) as f32 / ORIG_BG_SIZE_X as f32, 83 | 2. * -((ORIG_KNOB_Y - ORIG_BG_SIZE_Y / 2) as f32) / ORIG_BG_SIZE_Y as f32, 84 | 0., 85 | )) * Matrix4::from_nonuniform_scale( 86 | (ORIG_KNOB_RADIUS * 2) as f32 / ORIG_BG_SIZE_X as f32, 87 | (ORIG_KNOB_RADIUS * 2) as f32 / ORIG_BG_SIZE_Y as f32, 88 | 1., 89 | ) 90 | }); 91 | 92 | impl Renderer { 93 | /// Creates a new `Renderer` by initializing the GPU to prepare it for rendering. 94 | pub fn new(handle: W) -> Self { 95 | let instance = wgpu::Instance::new(wgpu::Backends::PRIMARY); 96 | 97 | // Acquire the window as a surface to be rendered on. 98 | // This is the only unsafe code in the plugin; it is only required to satisfy the 99 | // `raw_window_handle` API. Safety is upheld by taking ownership of `handle` in the 100 | // function signature, ensuring it is only ever used to create a single surface. 101 | let surface = unsafe { instance.create_surface(&handle) }; 102 | 103 | // Get a handle to the GPU and a queue of commands to be uploaded to it while rendering. 104 | let (device, queue) = futures::executor::block_on(async { 105 | let adapter = instance 106 | .request_adapter(&wgpu::RequestAdapterOptions { 107 | power_preference: wgpu::PowerPreference::HighPerformance, 108 | force_fallback_adapter: false, 109 | compatible_surface: Some(&surface), 110 | }) 111 | .await 112 | .unwrap(); 113 | 114 | adapter 115 | .request_device( 116 | &wgpu::DeviceDescriptor { 117 | label: None, 118 | features: wgpu::Features::empty(), 119 | limits: wgpu::Limits::default(), 120 | }, 121 | None, 122 | ) 123 | .await 124 | .unwrap() 125 | }); 126 | 127 | // Shaders are written in GLSL and compiled to SPIR-V from `build.rs`. They describe how 128 | // to layout points in space (vertex shaders), or how to render triangular fragments to 129 | // the screen (fragment shaders). The resulting SPIR-V is loaded to the GPU at runtime. 130 | let vs_module = device.create_shader_module(&wgpu::include_spirv!( 131 | "../../../assets/generated/spirv/shader.vert.spv" 132 | )); 133 | let fs_module = device.create_shader_module(&wgpu::include_spirv!( 134 | "../../../assets/generated/spirv/shader.frag.spv" 135 | )); 136 | 137 | // Bind group layouts describe data available to the GPU in different shader stages. 138 | let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { 139 | label: None, 140 | entries: &[ 141 | // Binding 0 is a uniform buffer used to hold a transformation matrix for the 142 | // vertex shader. 143 | wgpu::BindGroupLayoutEntry { 144 | binding: 0, 145 | visibility: wgpu::ShaderStages::VERTEX, 146 | ty: wgpu::BindingType::Buffer { 147 | ty: wgpu::BufferBindingType::Uniform, 148 | has_dynamic_offset: false, 149 | min_binding_size: None, 150 | }, 151 | count: None, 152 | }, 153 | // Binding 1 holds a texture that is sampled by texture coordinates to produce the 154 | // appearance of a particular set of geometry in the fragment shader. 155 | wgpu::BindGroupLayoutEntry { 156 | binding: 1, 157 | visibility: wgpu::ShaderStages::FRAGMENT, 158 | ty: wgpu::BindingType::Texture { 159 | multisampled: false, 160 | sample_type: wgpu::TextureSampleType::Float { filterable: true }, 161 | view_dimension: wgpu::TextureViewDimension::D2, 162 | }, 163 | count: None, 164 | }, 165 | // Binding 2 holds a sampling algorithm used to define the behavior when sampling 166 | // the texture in the fragment shader. 167 | wgpu::BindGroupLayoutEntry { 168 | binding: 2, 169 | visibility: wgpu::ShaderStages::FRAGMENT, 170 | ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering), 171 | count: None, 172 | }, 173 | ], 174 | }); 175 | 176 | let render_format = wgpu::TextureFormat::Bgra8Unorm; 177 | let config = wgpu::SurfaceConfiguration { 178 | usage: wgpu::TextureUsages::RENDER_ATTACHMENT, 179 | format: render_format, 180 | width: SIZE_X as u32, 181 | height: SIZE_Y as u32, 182 | present_mode: wgpu::PresentMode::Mailbox, 183 | }; 184 | surface.configure(&device, &config); 185 | 186 | // A multisampled framebuffer is used for anti-aliasing. 187 | let multisampled_framebuffer = 188 | create_multisampled_framebuffer(&device, &config, MSAA_SAMPLES); 189 | 190 | // The graphics pipeline specifies what behavior to use when rendering to the screen. 191 | let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { 192 | label: None, 193 | bind_group_layouts: &[&bind_group_layout], 194 | push_constant_ranges: &[], 195 | }); 196 | let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { 197 | label: None, 198 | layout: Some(&pipeline_layout), 199 | vertex: wgpu::VertexState { 200 | module: &vs_module, 201 | entry_point: "main", 202 | buffers: &[wgpu::VertexBufferLayout { 203 | array_stride: std::mem::size_of::() as wgpu::BufferAddress, 204 | step_mode: wgpu::VertexStepMode::Vertex, 205 | attributes: &wgpu::vertex_attr_array![0 => Float32x4, 1 => Float32x2], 206 | }], 207 | }, 208 | fragment: Some(wgpu::FragmentState { 209 | module: &fs_module, 210 | entry_point: "main", 211 | targets: &[wgpu::ColorTargetState { 212 | format: config.format, 213 | blend: Some(wgpu::BlendState { 214 | color: wgpu::BlendComponent { 215 | src_factor: wgpu::BlendFactor::SrcAlpha, 216 | dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha, 217 | operation: wgpu::BlendOperation::Add, 218 | }, 219 | alpha: wgpu::BlendComponent { 220 | src_factor: wgpu::BlendFactor::One, 221 | dst_factor: wgpu::BlendFactor::One, 222 | operation: wgpu::BlendOperation::Add, 223 | }, 224 | }), 225 | write_mask: wgpu::ColorWrites::ALL, 226 | }], 227 | }), 228 | primitive: wgpu::PrimitiveState { 229 | topology: wgpu::PrimitiveTopology::TriangleList, 230 | front_face: wgpu::FrontFace::Ccw, 231 | cull_mode: Some(wgpu::Face::Back), 232 | ..Default::default() 233 | }, 234 | depth_stencil: None, 235 | multisample: wgpu::MultisampleState { 236 | count: MSAA_SAMPLES, 237 | mask: !0, 238 | alpha_to_coverage_enabled: false, 239 | }, 240 | multiview: None, 241 | }); 242 | 243 | let sampler = device.create_sampler(&wgpu::SamplerDescriptor { 244 | address_mode_u: wgpu::AddressMode::ClampToEdge, 245 | address_mode_v: wgpu::AddressMode::ClampToEdge, 246 | address_mode_w: wgpu::AddressMode::ClampToEdge, 247 | mag_filter: wgpu::FilterMode::Nearest, 248 | min_filter: wgpu::FilterMode::Linear, 249 | mipmap_filter: wgpu::FilterMode::Nearest, 250 | ..Default::default() 251 | }); 252 | 253 | // We render the background and pointer to the screen as two rectangles with various 254 | // rotations. These index and vertex buffers describe a single rectangle split into two 255 | // triangular fragments that is reused for both images. 256 | let rectangle_vertex_buffer = 257 | device.create_buffer_init(&wgpu::util::BufferInitDescriptor { 258 | label: None, 259 | contents: &[ 260 | Vertex::new(1., 1., 1., 0.), 261 | Vertex::new(-1., 1., 0., 0.), 262 | Vertex::new(-1., -1., 0., 1.), 263 | Vertex::new(1., -1., 1., 1.), 264 | ] 265 | .as_bytes(), 266 | usage: wgpu::BufferUsages::VERTEX, 267 | }); 268 | let rectangle_index_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { 269 | label: None, 270 | contents: &[0u32, 1, 2, 2, 3, 0].as_bytes(), 271 | usage: wgpu::BufferUsages::INDEX, 272 | }); 273 | 274 | // Different bind groups for the background and pointer allow them to be rendered with a 275 | // different appearance. We also save the uniform buffer used to transform the pointer, so 276 | // that we can give it a different rotation later on. The background doesn't move, so we 277 | // never need to update its uniform buffer. 278 | let (background_bind_group, _) = make_bind_group( 279 | &device, 280 | &queue, 281 | &bind_group_layout, 282 | &sampler, 283 | BACKGROUND_IMAGE, 284 | Matrix4::identity(), 285 | ); 286 | let (pointer_bind_group, pointer_transform_buffer) = make_bind_group( 287 | &device, 288 | &queue, 289 | &bind_group_layout, 290 | &sampler, 291 | POINTER_IMAGE, 292 | *SCALE_MOVE_KNOB_TRANSFORM, 293 | ); 294 | 295 | // Font rendering is conveniently handled by `wgpu_glyph` :) 296 | let fonts: Vec = 297 | vec![wgpu_glyph::ab_glyph::FontArc::try_from_slice(FONT).unwrap()]; 298 | let text_renderer = GlyphBrushBuilder::using_fonts(fonts).build(&device, render_format); 299 | 300 | Self { 301 | device, 302 | queue, 303 | multisampled_framebuffer, 304 | surface, 305 | 306 | text_renderer, 307 | local_pool: futures::executor::LocalPool::new(), 308 | staging_belt: wgpu::util::StagingBelt::new(1024), 309 | 310 | pipeline, 311 | rectangle_index_buffer, 312 | rectangle_vertex_buffer, 313 | 314 | background_bind_group, 315 | 316 | pointer_bind_group, 317 | pointer_transform_buffer, 318 | } 319 | } 320 | 321 | /// Render a single frame of the given interface state to the screen. 322 | pub fn draw_frame(&mut self, state: &super::state::InterfaceState) { 323 | if let Ok(frame) = self.surface.get_current_texture() { 324 | let mut encoder = self 325 | .device 326 | .create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None }); 327 | { 328 | // Pointer starts at top position in source image. Knob limits are 150 degrees in 329 | // both directions. 330 | let data = TransformUniform { 331 | transform: (*SCALE_MOVE_KNOB_TRANSFORM 332 | * Matrix4::from_angle_z(cgmath::Deg(-state.amplitude_value * 300. + 150.))) 333 | .into(), 334 | }; 335 | self.queue.write_buffer( 336 | &self.pointer_transform_buffer, 337 | 0 as wgpu::BufferAddress, 338 | data.as_bytes(), 339 | ); 340 | 341 | let view = frame.texture.create_view(&wgpu::TextureViewDescriptor::default()); 342 | 343 | { 344 | let mut rpass = Self::start_renderpass( 345 | &mut encoder, 346 | &view, 347 | &self.multisampled_framebuffer, 348 | ); 349 | rpass.set_pipeline(&self.pipeline); 350 | rpass.set_index_buffer(self.rectangle_index_buffer.slice(..), wgpu::IndexFormat::Uint32); 351 | rpass.set_vertex_buffer(0, self.rectangle_vertex_buffer.slice(..)); 352 | 353 | // draw background 354 | rpass.set_bind_group(0, &self.background_bind_group, &[]); 355 | rpass.draw_indexed(0..6, 0, 0..1); 356 | 357 | // draw knob pointer 358 | rpass.set_bind_group(0, &self.pointer_bind_group, &[]); 359 | rpass.draw_indexed(0..6, 0, 0..1); 360 | } 361 | 362 | let display_val = state.amplitude_value * 2.; 363 | 364 | let int_text = display_val.trunc() as u8; 365 | let frac_text = (display_val.fract() * 100.).trunc() as u8; 366 | let text = if frac_text < 10 { 367 | format!("{}.0{}", int_text, frac_text) 368 | } else { 369 | format!("{}.{}", int_text, frac_text) 370 | }; 371 | 372 | self.text_renderer.queue(wgpu_glyph::Section { 373 | text: vec![wgpu_glyph::Text::default() 374 | .with_text(&text) 375 | .with_color(FONT_COLOR) 376 | .with_font_id(wgpu_glyph::FontId(0)) 377 | .with_scale(100. * SCALE as f32)], 378 | layout: wgpu_glyph::Layout::default_single_line() 379 | .h_align(wgpu_glyph::HorizontalAlign::Right) 380 | .v_align(wgpu_glyph::VerticalAlign::Center), 381 | screen_position: (TEXT_RIGHT_ANCHOR, TEXT_CENTER_Y_ANCHOR), 382 | bounds: (SIZE_X as f32, SIZE_Y as f32), 383 | }); 384 | self.text_renderer 385 | .draw_queued( 386 | &self.device, 387 | &mut self.staging_belt, 388 | &mut encoder, 389 | &view, 390 | SIZE_X as u32, 391 | SIZE_Y as u32, 392 | ) 393 | .unwrap(); 394 | } 395 | self.staging_belt.finish(); 396 | self.queue.submit(std::iter::once(encoder.finish())); 397 | 398 | use futures::task::SpawnExt; 399 | self.local_pool 400 | .spawner() 401 | .spawn(self.staging_belt.recall()) 402 | .expect("Recall staging belt"); 403 | self.local_pool.run_until_stalled(); 404 | 405 | frame.present(); 406 | } 407 | } 408 | 409 | /// Begin a renderpass for the background and knob pointer. Text will be drawn in a separate 410 | /// pass by `wgpu_glyph`. 411 | fn start_renderpass<'a>( 412 | encoder: &'a mut wgpu::CommandEncoder, 413 | view: &'a wgpu::TextureView, 414 | multisampled_framebuffer: &'a wgpu::TextureView, 415 | ) -> wgpu::RenderPass<'a> { 416 | let rpass_color_attachment = wgpu::RenderPassColorAttachment { 417 | view: multisampled_framebuffer, 418 | resolve_target: Some(view), 419 | ops: wgpu::Operations { 420 | load: wgpu::LoadOp::Clear(wgpu::Color::BLACK), 421 | store: true, 422 | }, 423 | }; 424 | 425 | encoder.begin_render_pass(&wgpu::RenderPassDescriptor { 426 | label: None, 427 | color_attachments: &[rpass_color_attachment], 428 | depth_stencil_attachment: None, 429 | }) 430 | } 431 | } 432 | 433 | /// Different bind groups are used to render sets of geometry in different ways. In this case, the 434 | /// two geometries on the interface (background and knob pointer) are rendered with different 435 | /// textures and 2D positions. 436 | fn make_bind_group( 437 | device: &wgpu::Device, 438 | queue: &wgpu::Queue, 439 | bind_group_layout: &wgpu::BindGroupLayout, 440 | sampler: &wgpu::Sampler, 441 | png_image: &[u8], 442 | initial_transform: Matrix4, 443 | ) -> (wgpu::BindGroup, wgpu::Buffer) { 444 | let uniform_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { 445 | label: None, 446 | contents: TransformUniform { 447 | transform: initial_transform.into(), 448 | } 449 | .as_bytes(), 450 | usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, 451 | }); 452 | 453 | let decoder = png::Decoder::new(png_image); 454 | let (info, mut reader) = decoder.read_info().unwrap(); 455 | let mut image_data = vec![0; info.buffer_size()]; 456 | reader.next_frame(&mut image_data).unwrap(); 457 | 458 | let texture_extent = wgpu::Extent3d { 459 | width: info.width, 460 | height: info.height, 461 | depth_or_array_layers: 1, 462 | }; 463 | let texture = device.create_texture(&wgpu::TextureDescriptor { 464 | label: None, 465 | size: texture_extent, 466 | mip_level_count: 1, 467 | sample_count: 1, 468 | dimension: wgpu::TextureDimension::D2, 469 | format: wgpu::TextureFormat::Rgba8Unorm, 470 | usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST, 471 | }); 472 | 473 | let texture_view = texture.create_view(&wgpu::TextureViewDescriptor::default()); 474 | queue.write_texture( 475 | texture.as_image_copy(), 476 | &image_data, 477 | wgpu::ImageDataLayout { 478 | offset: 0, 479 | bytes_per_row: Some(std::num::NonZeroU32::new(4 * info.width).unwrap()), 480 | rows_per_image: None, 481 | }, 482 | texture_extent, 483 | ); 484 | 485 | let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { 486 | layout: &bind_group_layout, 487 | entries: &[ 488 | wgpu::BindGroupEntry { 489 | binding: 0, 490 | resource: uniform_buf.as_entire_binding(), 491 | }, 492 | wgpu::BindGroupEntry { 493 | binding: 1, 494 | resource: wgpu::BindingResource::TextureView(&texture_view), 495 | }, 496 | wgpu::BindGroupEntry { 497 | binding: 2, 498 | resource: wgpu::BindingResource::Sampler(&sampler), 499 | }, 500 | ], 501 | label: None, 502 | }); 503 | 504 | (bind_group, uniform_buf) 505 | } 506 | 507 | /// Creates a new buffer that is sampled `sample_count` times more densely than the target output 508 | /// surface, producing a more smooth anti-aliased appearance. 509 | fn create_multisampled_framebuffer( 510 | device: &wgpu::Device, 511 | sc_desc: &wgpu::SurfaceConfiguration, 512 | sample_count: u32, 513 | ) -> wgpu::TextureView { 514 | let multisampled_texture_extent = wgpu::Extent3d { 515 | width: sc_desc.width, 516 | height: sc_desc.height, 517 | depth_or_array_layers: 1, 518 | }; 519 | let multisampled_frame_descriptor = &wgpu::TextureDescriptor { 520 | label: None, 521 | size: multisampled_texture_extent, 522 | mip_level_count: 1, 523 | sample_count, 524 | dimension: wgpu::TextureDimension::D2, 525 | format: sc_desc.format, 526 | usage: wgpu::TextureUsages::RENDER_ATTACHMENT, 527 | }; 528 | 529 | device 530 | .create_texture(multisampled_frame_descriptor) 531 | .create_view(&wgpu::TextureViewDescriptor::default()) 532 | } 533 | -------------------------------------------------------------------------------- /src/editor/interface/mod.rs: -------------------------------------------------------------------------------- 1 | //! All the logic behind the editor UI is contained within this module. 2 | //! 3 | //! Fundamentally, the UI is split into graphics rendering and state management in response to 4 | //! input events, both of which are managed within the `EditorInterface` type. 5 | 6 | use std::sync::mpsc::Receiver; 7 | 8 | use vst_window::{EditorWindow, EventSource}; 9 | 10 | use crate::plugin_state::StateUpdate; 11 | 12 | mod graphics; 13 | mod state; 14 | 15 | use super::EditorRemoteState; 16 | pub(super) use state::InterfaceState; 17 | 18 | /// Dimensions and layout of image assets. 19 | mod image_consts { 20 | /// Original horizontal dimension of the background image, in pixels. 21 | pub const ORIG_BG_SIZE_X: usize = 1200; 22 | /// Original vertical dimension of the background image, in pixels. 23 | pub const ORIG_BG_SIZE_Y: usize = 800; 24 | 25 | /// Original radius of the knob image, in pixels. 26 | pub const ORIG_KNOB_RADIUS: usize = 200; 27 | /// Original center x-coordinate of the knob image, in pixels. 28 | pub const ORIG_KNOB_X: usize = 800; 29 | /// Original center y-coordinate of the knob image, in pixels. 30 | pub const ORIG_KNOB_Y: usize = 500; 31 | } 32 | 33 | /// Display scale of the entire UI. 34 | const SCALE: f64 = 0.5; 35 | 36 | /// Actual pixel width of the editor window. 37 | pub(super) const SIZE_X: usize = (image_consts::ORIG_BG_SIZE_X as f64 * SCALE) as usize; 38 | /// Actual pixel height of the editor window. 39 | pub(super) const SIZE_Y: usize = (image_consts::ORIG_BG_SIZE_Y as f64 * SCALE) as usize; 40 | 41 | /// Represents a window containing an editor interface. A new one is used each time the parent 42 | /// window provided by the host DAW is opened or closed. 43 | pub(super) struct EditorInterface { 44 | renderer: graphics::Renderer, 45 | event_source: EventSource, 46 | state: InterfaceState, 47 | } 48 | 49 | impl EditorInterface { 50 | /// Setup the `EditorInterface` within the provided parent `EditorWindow` to respond to events 51 | /// from the corresponding `EventSource`. 52 | pub fn new( 53 | window: EditorWindow, 54 | event_source: EventSource, 55 | initial_state: InterfaceState, 56 | ) -> Self { 57 | let renderer = graphics::Renderer::new(window); 58 | 59 | Self { 60 | renderer, 61 | event_source, 62 | state: initial_state, 63 | } 64 | } 65 | 66 | /// Run as much as possible of the editor interface without blocking. This means acting on any 67 | /// pending state change events from remote state storage, responding to any new window input 68 | /// events, and then rendering the new state of the UI. 69 | pub fn run_tasks( 70 | &mut self, 71 | remote_state: &S, 72 | incoming: &mut Receiver, 73 | ) { 74 | while let Ok(event) = incoming.try_recv() { 75 | self.state.react_to_control_event(event); 76 | } 77 | 78 | while let Some(event) = self.event_source.poll_event() { 79 | self.state.react_to_window_event(event, remote_state); 80 | } 81 | 82 | self.renderer.draw_frame(&self.state); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/editor/interface/state.rs: -------------------------------------------------------------------------------- 1 | //! The editor interface is scheduled to be drawn periodically by the host DAW. Some state must be 2 | //! kept to maintain a consistent appearance across frames. This module contains the 3 | //! `InterfaceState` struct along with logic to update it in response to window events like clicks, 4 | //! drags, etc. as well as from external state updates. 5 | 6 | use vst_window::WindowEvent; 7 | 8 | use super::{ 9 | image_consts::{ORIG_KNOB_RADIUS, ORIG_KNOB_X, ORIG_KNOB_Y}, 10 | SCALE, SIZE_X, SIZE_Y, 11 | }; 12 | use crate::plugin_state::StateUpdate; 13 | 14 | /// All the possible ways a click+drag operation on the interface window might be interpreted. 15 | enum DragBehavior { 16 | TurnAmplitudeKnob { click_y: isize, original_value: f32 }, 17 | } 18 | 19 | /// Holds any state required to render and update the editor interface. 20 | pub(in crate::editor) struct InterfaceState { 21 | /// Represents the position of the knob, from 0 to 1. 22 | pub amplitude_value: f32, 23 | /// (X, Y) pixel coordinate of the cursor, from the top-left corner. 24 | /// Coordinates could be negative if the cursor is dragged outside of the window! 25 | cursor_pos: (isize, isize), 26 | drag_behavior: Option, 27 | } 28 | 29 | const KNOB_CENTER_X: usize = (ORIG_KNOB_X as f64 * SCALE) as usize; 30 | const KNOB_CENTER_Y: usize = (ORIG_KNOB_Y as f64 * SCALE) as usize; 31 | const KNOB_RADIUS: usize = (ORIG_KNOB_RADIUS as f64 * SCALE) as usize; 32 | 33 | const KNOB_CHANGE_SPEED: f32 = 0.5; 34 | 35 | impl InterfaceState { 36 | pub fn new(amplitude_value: f32) -> Self { 37 | Self { 38 | amplitude_value, 39 | cursor_pos: Default::default(), 40 | drag_behavior: None, 41 | } 42 | } 43 | 44 | /// Update the editor state in response to an external message. 45 | pub fn react_to_control_event(&mut self, event: StateUpdate) { 46 | match event { 47 | StateUpdate::SetKnob(value) => self.amplitude_value = value, 48 | } 49 | } 50 | 51 | /// Update the editor state and remote state store as necessary in response to an interaction 52 | /// with the editor window. 53 | pub fn react_to_window_event( 54 | &mut self, 55 | event: WindowEvent, 56 | remote_state: &S, 57 | ) { 58 | match event { 59 | WindowEvent::CursorMovement(x, y) => { 60 | self.cursor_pos = ((x * SIZE_X as f32) as isize, (y * SIZE_Y as f32) as isize); 61 | if let Some(DragBehavior::TurnAmplitudeKnob { 62 | click_y, 63 | original_value, 64 | .. 65 | }) = self.drag_behavior 66 | { 67 | let diff_y = click_y - self.cursor_pos.1; 68 | self.amplitude_value = (original_value 69 | + diff_y as f32 / SIZE_Y as f32 * KNOB_CHANGE_SPEED) 70 | .max(0.) 71 | .min(1.); 72 | remote_state.set_amplitude_control(self.amplitude_value); 73 | } 74 | } 75 | WindowEvent::MouseClick(button) => { 76 | let (x, y) = self.cursor_pos; 77 | if (x - KNOB_CENTER_X as isize).pow(2) + (y - KNOB_CENTER_Y as isize).pow(2) 78 | < KNOB_RADIUS.pow(2) as isize 79 | { 80 | if button == vst_window::MouseButton::Left { 81 | self.drag_behavior = Some(DragBehavior::TurnAmplitudeKnob { 82 | click_y: y, 83 | original_value: self.amplitude_value, 84 | }); 85 | } else if button == vst_window::MouseButton::Right { 86 | self.amplitude_value = 0.5; 87 | remote_state.set_amplitude_control(self.amplitude_value); 88 | } 89 | } 90 | } 91 | WindowEvent::MouseRelease(vst_window::MouseButton::Left) => { 92 | drop(self.drag_behavior.take()); 93 | } 94 | _ => (), 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/editor/mod.rs: -------------------------------------------------------------------------------- 1 | //! In VST terminology, the editor is a graphical window that can be used to display and interact 2 | //! with a plugin using a custom visual appearance. 3 | //! 4 | //! The editor interface runs fully on the UI thread. It manages an OS window through a 5 | //! cross-platform API exposed by the `vst_window` crate. It displays the state of the plugin 6 | //! graphically, instructs the overall plugin state to update in response to window input events, 7 | //! and handles notifications of state updates that occur on the processing thread. 8 | 9 | use std::sync::{mpsc::Receiver, Arc}; 10 | 11 | use vst::editor::Editor; 12 | use vst::plugin::PluginParameters; 13 | use vst_window::setup; 14 | 15 | use crate::plugin_state::{PluginState, StateUpdate}; 16 | 17 | mod interface; 18 | use interface::{EditorInterface, InterfaceState, SIZE_X, SIZE_Y}; 19 | 20 | /// Persistent VST-compatible wrapper that opens and closes an `EditorInterface`. 21 | pub(super) struct PluginEditor { 22 | opened_interface: Option, 23 | remote_state: Arc, 24 | incoming: Receiver, 25 | } 26 | 27 | impl PluginEditor { 28 | pub fn new(remote_state: Arc, incoming: Receiver) -> Self { 29 | Self { 30 | opened_interface: None, 31 | remote_state, 32 | incoming, 33 | } 34 | } 35 | } 36 | 37 | /// `PluginEditor` responds directly to VST API calls specific to the UI thread. 38 | impl Editor for PluginEditor { 39 | fn size(&self) -> (i32, i32) { 40 | (SIZE_X as i32, SIZE_Y as i32) 41 | } 42 | 43 | fn position(&self) -> (i32, i32) { 44 | (0, 0) 45 | } 46 | 47 | fn open(&mut self, parent: *mut core::ffi::c_void) -> bool { 48 | if self.opened_interface.is_none() { 49 | let (window, event_source) = setup(parent, (SIZE_X as i32, SIZE_Y as i32)); 50 | (*self.remote_state).set_event_subscription(true); 51 | let initial_state = InterfaceState::new(self.remote_state.get_parameter(0)); 52 | self.opened_interface = Some(EditorInterface::new(window, event_source, initial_state)); 53 | true 54 | } else { 55 | false 56 | } 57 | } 58 | 59 | fn close(&mut self) { 60 | self.remote_state.set_event_subscription(false); 61 | drop(self.opened_interface.take()); 62 | } 63 | 64 | fn is_open(&mut self) -> bool { 65 | self.opened_interface.is_some() 66 | } 67 | 68 | fn idle(&mut self) { 69 | if let Some(opened_interface) = &mut self.opened_interface { 70 | opened_interface.run_tasks(&*self.remote_state, &mut self.incoming); 71 | } 72 | } 73 | } 74 | 75 | /// The editor interface holds a handle directly to the remote VST plugin state, which should 76 | /// implement this trait. It should be possible to update the remote state through these trait 77 | /// methods. 78 | /// 79 | /// Each of these methods should do three things as necessary: 80 | /// - Update the remote long-term internal state 81 | /// - Pass a message to the audio processing thread, instructing it to affect its algorithm 82 | /// accordingly 83 | /// - Notify the host DAW if any of its knobs need to be re-rendered. 84 | pub(super) trait EditorRemoteState { 85 | /// While the event subscription is enabled, state update events will be sent over the 86 | /// `control_send` channel. 87 | fn set_event_subscription(&self, enabled: bool); 88 | /// Sets the position of the amplitude control to a new fraction of its full range between 0 89 | /// and 1. 90 | fn set_amplitude_control(&self, value: f32); 91 | } 92 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! ampli-Fe is a minimal yet complete VST2 plugin designed to demonstrate usage of the 2 | //! `vst_window` crate. 3 | //! 4 | //! It features a fully-customized editor UI with an interactive knob and corresponding numerical 5 | //! value readout. 6 | //! 7 | //! ampli-Fe's code is well-documented - feel free to use it as a starting point for your next VST2 8 | //! plugin in Rust. 9 | 10 | use std::sync::{mpsc::channel, Arc}; 11 | 12 | use vst::{ 13 | api::Supported, 14 | buffer::AudioBuffer, 15 | editor::Editor, 16 | plugin::{CanDo, HostCallback, Info, Plugin, PluginParameters}, 17 | }; 18 | 19 | mod dsp; 20 | use dsp::PluginDsp; 21 | 22 | mod editor; 23 | use editor::PluginEditor; 24 | 25 | mod plugin_state; 26 | use plugin_state::PluginState; 27 | 28 | /// Top level wrapper that exposes a full `vst::Plugin` implementation. 29 | struct AmpliFeVst { 30 | /// The `PluginDsp` handles all of the plugin's audio processing, and is only accessed from the 31 | /// audio processing thread. 32 | dsp: PluginDsp, 33 | 34 | /// The `PluginState` holds the long-term state of the plugin and distributes raw parameter 35 | /// updates as they occur to other parts of the plugin. It is shared on both the audio 36 | /// processing thread and the UI thread, and updated using thread-safe interior mutability. 37 | state_handle: Arc, 38 | 39 | /// The `PluginEditor` implements the plugin's custom editor interface. It's temporarily stored 40 | /// here until being moved to the UI thread by the first `get_editor` method call. 41 | editor_placeholder: Option, 42 | } 43 | 44 | impl AmpliFeVst { 45 | /// Initializes the VST plugin, along with an optional `HostCallback` handle. 46 | fn new_maybe_host(maybe_host: Option) -> Self { 47 | let host = maybe_host.unwrap_or_default(); 48 | 49 | let (to_editor, editor_recv) = channel(); 50 | let (to_dsp, dsp_recv) = channel(); 51 | 52 | let state_handle = Arc::new(PluginState::new(host, to_dsp, to_editor)); 53 | 54 | let editor_placeholder = Some(PluginEditor::new(Arc::clone(&state_handle), editor_recv)); 55 | 56 | let dsp = PluginDsp::new(dsp_recv); 57 | 58 | Self { 59 | dsp, 60 | state_handle, 61 | editor_placeholder, 62 | } 63 | } 64 | } 65 | 66 | /// `vst::plugin_main` requires a `Default` implementation. 67 | impl Default for AmpliFeVst { 68 | fn default() -> Self { 69 | Self::new_maybe_host(None) 70 | } 71 | } 72 | 73 | /// Main `vst` plugin implementation. 74 | impl Plugin for AmpliFeVst { 75 | fn new(host: HostCallback) -> Self { 76 | Self::new_maybe_host(Some(host)) 77 | } 78 | 79 | fn get_info(&self) -> Info { 80 | /// Use a hash of a string describing this plugin to avoid unique ID conflicts. 81 | const UNIQUE_ID_SEED: &str = "ampli-Fe Amplitude Effect VST2 Plugin"; 82 | static UNIQUE_ID: once_cell::sync::Lazy = once_cell::sync::Lazy::new(|| { 83 | use std::collections::hash_map::DefaultHasher; 84 | use std::hash::{Hash, Hasher}; 85 | 86 | let mut s = DefaultHasher::new(); 87 | UNIQUE_ID_SEED.hash(&mut s); 88 | s.finish() as i32 89 | }); 90 | 91 | Info { 92 | name: "ampli-Fe".to_string(), 93 | vendor: "antonok".to_string(), 94 | unique_id: *UNIQUE_ID, 95 | inputs: 2, 96 | outputs: 2, 97 | parameters: 1, 98 | initial_delay: 0, 99 | preset_chunks: true, 100 | ..Info::default() 101 | } 102 | } 103 | 104 | fn process(&mut self, buffer: &mut AudioBuffer) { 105 | self.dsp.process(buffer); 106 | } 107 | 108 | fn can_do(&self, _can_do: CanDo) -> Supported { 109 | Supported::Maybe 110 | } 111 | 112 | fn get_parameter_object(&mut self) -> Arc { 113 | Arc::clone(&self.state_handle) as Arc 114 | } 115 | 116 | fn get_editor(&mut self) -> Option> { 117 | self.editor_placeholder 118 | .take() 119 | .map(|editor| Box::new(editor) as Box) 120 | } 121 | } 122 | 123 | vst::plugin_main!(AmpliFeVst); 124 | -------------------------------------------------------------------------------- /src/plugin_state.rs: -------------------------------------------------------------------------------- 1 | //! Parameters are kept as the single "source of truth" for the long-term state of the plugin. As 2 | //! used by the VST API, the parameter bank is accessible by both the audio processing thread and 3 | //! the UI thread, and updated using thread-safe interior mutability. However, to avoid costly 4 | //! synchronization overhead, and to reduce recalculation of derived parameters, the audio 5 | //! processing and UI threads subscribe to parameter updates through cross-thread message passing. 6 | //! 7 | //! This plugin's long-term state only consists of a single floating-point value (the value of the 8 | //! amplitude knob), but it should be simple to extend this scheme to work with multiple knobs, 9 | //! toggles, node locations, waveforms, user-defined labels, and so on. 10 | 11 | use std::sync::{ 12 | atomic::{AtomicBool, Ordering}, 13 | mpsc::Sender, 14 | Mutex, 15 | }; 16 | 17 | use vst::{ 18 | host::Host, 19 | plugin::{HostCallback, PluginParameters}, 20 | }; 21 | 22 | /// Describes a discrete operation that can update this plugin's long-term state. 23 | #[derive(Clone)] 24 | pub enum StateUpdate { 25 | SetKnob(f32), 26 | } 27 | 28 | pub struct PluginState { 29 | host: HostCallback, 30 | to_dsp: Mutex>, 31 | to_editor: Mutex>, 32 | editor_is_open: AtomicBool, 33 | 34 | state_record: Mutex>, 35 | } 36 | 37 | /// VST-accessible long-term plugin state storage. This is accessed through the audio processing 38 | /// thread and the UI thread, so all fields are protected by thread-safe interior mutable 39 | /// constructs. 40 | impl PluginState { 41 | pub fn new( 42 | host: HostCallback, 43 | to_dsp: Sender, 44 | to_editor: Sender, 45 | ) -> Self { 46 | Self { 47 | host, 48 | to_dsp: Mutex::new(to_dsp), 49 | to_editor: Mutex::new(to_editor), 50 | editor_is_open: AtomicBool::new(false), 51 | state_record: Mutex::new(vec![0.5, 0., 0., 0.]), 52 | } 53 | } 54 | } 55 | 56 | /// The DAW directly accesses the plugin state through the VST API to get reports on knob states. 57 | impl PluginParameters for PluginState { 58 | fn set_parameter(&self, index: i32, value: f32) { 59 | let state_update = StateUpdate::SetKnob(value); 60 | if self.editor_is_open.load(Ordering::Relaxed) { 61 | self.to_editor 62 | .lock() 63 | .unwrap() 64 | .send(state_update.clone()) 65 | .unwrap(); 66 | } 67 | self.to_dsp.lock().unwrap().send(state_update).unwrap(); 68 | self.state_record.lock().unwrap()[index as usize] = value; 69 | } 70 | 71 | fn get_parameter(&self, index: i32) -> f32 { 72 | self.state_record.lock().unwrap()[index as usize] 73 | } 74 | 75 | fn get_parameter_label(&self, index: i32) -> String { 76 | match index { 77 | 0 => "x".to_string(), 78 | _ => unreachable!(), 79 | } 80 | } 81 | 82 | fn get_parameter_text(&self, index: i32) -> String { 83 | match index { 84 | 0 => format!( 85 | "{:.2}", 86 | self.state_record.lock().unwrap()[index as usize] * 2. 87 | ), 88 | _ => unreachable!(), 89 | } 90 | } 91 | 92 | fn get_parameter_name(&self, index: i32) -> String { 93 | match index { 94 | 0 => "Amplitude", 95 | _ => unreachable!(), 96 | } 97 | .to_string() 98 | } 99 | 100 | fn string_to_parameter(&self, index: i32, text: String) -> bool { 101 | dbg!("Set string to parameter for {}, {}", index, &text); 102 | match index { 103 | 0 => match text.parse::() { 104 | Ok(value) if value <= 2. && value >= 0. => { 105 | self.set_parameter(index, value / 2.); 106 | true 107 | } 108 | _ => false, 109 | }, 110 | _ => unreachable!(), 111 | } 112 | } 113 | } 114 | 115 | /// The editor interface also directly accesses the plugin state through its own API. 116 | impl crate::editor::EditorRemoteState for PluginState { 117 | fn set_amplitude_control(&self, value: f32) { 118 | self.state_record.lock().unwrap()[0] = value; 119 | 120 | self.to_dsp 121 | .lock() 122 | .unwrap() 123 | .send(StateUpdate::SetKnob(value)) 124 | .unwrap(); 125 | 126 | self.host.automate(0, value); 127 | } 128 | 129 | fn set_event_subscription(&self, enabled: bool) { 130 | self.editor_is_open.store(enabled, Ordering::Relaxed); 131 | } 132 | } 133 | --------------------------------------------------------------------------------