mirror of
https://github.com/ivabus/rinth
synced 2024-11-21 16:05:09 +03:00
0.0.1: First alpha release
Signed-off-by: Ivan Bushchik <ivabus@ivabus.dev>
This commit is contained in:
commit
1aff34a06a
25 changed files with 1383 additions and 0 deletions
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
target/
|
||||
*.DS_Store
|
||||
.idea
|
||||
*.wav
|
||||
*.m4a
|
9
.rustfmt.toml
Normal file
9
.rustfmt.toml
Normal file
|
@ -0,0 +1,9 @@
|
|||
edition = "2021"
|
||||
hard_tabs = true
|
||||
merge_derives = true
|
||||
reorder_imports = true
|
||||
reorder_modules = true
|
||||
use_field_init_shorthand = true
|
||||
use_small_heuristics = "Off"
|
||||
wrap_comments = true
|
||||
comment_width = 80
|
464
Cargo.lock
generated
Normal file
464
Cargo.lock
generated
Normal file
|
@ -0,0 +1,464 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "anstream"
|
||||
version = "0.6.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "628a8f9bd1e24b4e0db2b4bc2d000b001e7dd032d54afa60a68836aeec5aa54a"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"anstyle-parse",
|
||||
"anstyle-query",
|
||||
"anstyle-wincon",
|
||||
"colorchoice",
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87"
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-parse"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c"
|
||||
dependencies = [
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-query"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648"
|
||||
dependencies = [
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-wincon"
|
||||
version = "3.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.4.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e578d6ec4194633722ccf9544794b71b1385c3c027efe0c55db226fc880865c"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.4.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4df4df40ec50c46000231c914968278b1eb05098cf8f1b3a518a95030e71d1c7"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
"clap_lex",
|
||||
"strsim",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "4.4.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1"
|
||||
|
||||
[[package]]
|
||||
name = "colorchoice"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-deque"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d"
|
||||
dependencies = [
|
||||
"crossbeam-epoch",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-epoch"
|
||||
version = "0.9.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
|
||||
dependencies = [
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.8.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345"
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
|
||||
|
||||
[[package]]
|
||||
name = "fnv"
|
||||
version = "1.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.14.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
|
||||
|
||||
[[package]]
|
||||
name = "hound"
|
||||
version = "3.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "62adaabb884c94955b19907d60019f4e145d091c75345379e70d1ee696f7854f"
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149"
|
||||
|
||||
[[package]]
|
||||
name = "meval"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f79496a5651c8d57cd033c5add8ca7ee4e3d5f7587a4777484640d9cb60392d9"
|
||||
dependencies = [
|
||||
"fnv",
|
||||
"nom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "midly"
|
||||
version = "0.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "207d755f4cb882d20c4da58d707ca9130a0c9bc5061f657a4f299b8e36362b7a"
|
||||
dependencies = [
|
||||
"rayon",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nom"
|
||||
version = "1.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a5b8c256fd9471521bcb84c3cdba98921497f1a331cbc15b8030fc63b82050ce"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.76"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "95fc56cda0b5c3325f5fbbd7ff9fda9e02bb00bb3dac51252d2f1bfa1cb8cc8c"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rayon"
|
||||
version = "1.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c27db03db7734835b3f53954b534c91069375ce6ccaa2e065441e07d9b6cdb1"
|
||||
dependencies = [
|
||||
"either",
|
||||
"rayon-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rayon-core"
|
||||
version = "1.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed"
|
||||
dependencies = [
|
||||
"crossbeam-deque",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rinth-midi"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"midly",
|
||||
"rinth-types",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rinth-synth"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"hound",
|
||||
"meval",
|
||||
"rinth-types",
|
||||
"serde",
|
||||
"serde_yaml",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rinth-types"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"toml",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.195"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "63261df402c67811e9ac6def069e4786148c4563f4b50fd4bf30aa370d626b02"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.195"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "46fe8f8603d81ba86327b23a2e9cdf49e1255fb94a4c5f297f6ee0547178ea2c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_spanned"
|
||||
version = "0.6.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_yaml"
|
||||
version = "0.9.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1bf28c79a99f70ee1f1d83d10c875d2e70618417fda01ad1785e027579d9d38"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"itoa",
|
||||
"ryu",
|
||||
"serde",
|
||||
"unsafe-libyaml",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.48"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.8.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1a195ec8c9da26928f773888e0742ca3ca1040c6cd859c919c9f59c1954ab35"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_spanned",
|
||||
"toml_datetime",
|
||||
"toml_edit",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_datetime"
|
||||
version = "0.6.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_edit"
|
||||
version = "0.21.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d34d383cd00a163b4a5b85053df514d45bc330f6de7737edfe0a93311d1eaa03"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"serde",
|
||||
"serde_spanned",
|
||||
"toml_datetime",
|
||||
"winnow",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||
|
||||
[[package]]
|
||||
name = "unsafe-libyaml"
|
||||
version = "0.2.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ab4c90930b95a82d00dc9e9ac071b4991924390d46cbd0dfe566148667605e4b"
|
||||
|
||||
[[package]]
|
||||
name = "utf8parse"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_gnullvm",
|
||||
"windows_x86_64_msvc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04"
|
||||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
version = "0.5.34"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b7cf47b659b318dccbd69cc4797a39ae128f533dce7902a1096044d1967b9c16"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
3
Cargo.toml
Normal file
3
Cargo.toml
Normal file
|
@ -0,0 +1,3 @@
|
|||
[workspace]
|
||||
members = [ "rinth-midi", "rinth-synth", "rinth-types" ]
|
||||
resolver = "2"
|
3
LICENSE
Normal file
3
LICENSE
Normal file
|
@ -0,0 +1,3 @@
|
|||
Rinth and all of it's subcrates are available for non-commercial (non-profit) personal use under the terms of BSD 3-Clause New (Revised) License license, see LICENSE-BSD
|
||||
|
||||
At the moment, the commercial use of Rinth is completely prohibited at the moment.
|
28
LICENSE-BSD
Normal file
28
LICENSE-BSD
Normal file
|
@ -0,0 +1,28 @@
|
|||
BSD 3-Clause License
|
||||
|
||||
Copyright (c) 2024, Ivan Bushchik
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
2. 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.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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.
|
92
README.md
Normal file
92
README.md
Normal file
|
@ -0,0 +1,92 @@
|
|||
# Rinth synthesiser
|
||||
|
||||
FM and SSG synthesiser
|
||||
|
||||
## Usage
|
||||
|
||||
```shell
|
||||
rinth-synth b[uild] <path/to/project.yml> # Will build separate tracks
|
||||
rinth-synth m[aster] <path/to/project.yml> # Will "master" tracks into one track
|
||||
```
|
||||
|
||||
See `--help` for more
|
||||
|
||||
## File formats
|
||||
|
||||
### \<project\>.yml
|
||||
|
||||
```yaml
|
||||
name: <NAME>
|
||||
bpm: <BPM>
|
||||
channels:
|
||||
- type: <SSG|FM>
|
||||
path: <RELATIVE_TO_YML_PATH>
|
||||
volume: [Optional in [0;1]]
|
||||
```
|
||||
|
||||
Example:
|
||||
|
||||
```yaml
|
||||
name: Megalovania
|
||||
bpm: 210
|
||||
channels:
|
||||
- type: SSG
|
||||
path: _channel
|
||||
- type: FM
|
||||
path: _channel
|
||||
volume: 0.7
|
||||
```
|
||||
|
||||
### _channel (FM)
|
||||
|
||||
```
|
||||
/ Comment
|
||||
#<Frequency deviation>
|
||||
@<Modulating frequency>
|
||||
<Note in SPN> <Note value> [Time from previous note]
|
||||
```
|
||||
|
||||
Example:
|
||||
|
||||
```
|
||||
#1
|
||||
@2000
|
||||
D4 0.0625
|
||||
Dd5 0.0625 0.0125
|
||||
Db5 1/32+1/32 1/80
|
||||
```
|
||||
|
||||
### _channel (SSG)
|
||||
|
||||
```
|
||||
/ Comment
|
||||
# Ignored
|
||||
@ Ignored
|
||||
<Note in SPN> <Note value> [Time from previous note]
|
||||
```
|
||||
|
||||
Example:
|
||||
|
||||
```
|
||||
/ Comment
|
||||
# Comment 2
|
||||
@ Comment 3
|
||||
D4 0.0625
|
||||
Dd5 0.0625 0.0125
|
||||
Db5 1/32+1/32 1/80
|
||||
```
|
||||
More examples in [examples/](./examples)
|
||||
|
||||
## MIDI Converter
|
||||
|
||||
WIP MIDI converter is available in the `rinth-midi` crate.
|
||||
|
||||
### Usage
|
||||
|
||||
```shell
|
||||
rinth-midi <path/to/midi.mid>
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
The project is available for non-commercial personal use under the terms of the [BSD 3-Clause New (Revised) License](./LICENSE) and fully unavailable for any other kind of use.
|
165
examples/determination/main
Normal file
165
examples/determination/main
Normal file
|
@ -0,0 +1,165 @@
|
|||
#1
|
||||
@4
|
||||
|
||||
G5 1/8
|
||||
Fd5 1/8
|
||||
E5 1/8
|
||||
D5 1/8
|
||||
E5 1/8
|
||||
B4 1/8
|
||||
|
||||
Cd5 1/4
|
||||
A4 1/4
|
||||
|
||||
E5 1/8
|
||||
Fd5 1/8
|
||||
|
||||
G5 1/4
|
||||
A5 1/4
|
||||
D6 1/4
|
||||
|
||||
B5 1/2+1/4
|
||||
|
||||
|
||||
G5 1/8
|
||||
Fd5 1/8
|
||||
E5 1/8
|
||||
D5 1/8
|
||||
E5 1/8
|
||||
B4 1/8
|
||||
|
||||
Cd5 1/4
|
||||
A4 1/4
|
||||
|
||||
E4 1/8
|
||||
Fd4 1/8
|
||||
|
||||
G4 1/4
|
||||
Fd4 1/4
|
||||
D4 1/4
|
||||
E4 1/2+1/4
|
||||
|
||||
G5 1/8
|
||||
Fd5 1/8
|
||||
E5 1/8
|
||||
D5 1/8
|
||||
E5 1/8
|
||||
B4 1/8
|
||||
|
||||
Cd5 1/4
|
||||
A4 1/4
|
||||
|
||||
E5 1/8
|
||||
Fd5 1/8
|
||||
|
||||
G5 1/4
|
||||
A5 1/4
|
||||
D6 1/4
|
||||
|
||||
B5 1/2+1/4
|
||||
|
||||
|
||||
G5 1/8
|
||||
Fd5 1/8
|
||||
E5 1/8
|
||||
D5 1/8
|
||||
E5 1/8
|
||||
B4 1/8
|
||||
|
||||
Cd5 1/4
|
||||
A4 1/4
|
||||
|
||||
E4 1/8
|
||||
Fd4 1/8
|
||||
|
||||
G4 1/4
|
||||
Fd4 1/4
|
||||
D4 1/4
|
||||
E4 1/2+1/4
|
||||
|
||||
|
||||
|
||||
|
||||
A5 1/8
|
||||
G5 1/8
|
||||
F5 1/8
|
||||
E5 1/8
|
||||
D5 1/8
|
||||
F5 1/8
|
||||
|
||||
E5 1/4
|
||||
B4 1/4
|
||||
|
||||
B4 1/8
|
||||
E5 1/8
|
||||
|
||||
A5 1/8
|
||||
G5 1/8
|
||||
F5 1/8
|
||||
E5 1/8
|
||||
D5 1/8
|
||||
F5 1/8
|
||||
|
||||
E5 1/2
|
||||
E4 1/8
|
||||
A4 1/8
|
||||
|
||||
D5 1/8
|
||||
Cd5 1/8
|
||||
B4 1/8
|
||||
A4 1/8
|
||||
B4 1/8
|
||||
Cd5 1/8
|
||||
|
||||
B4 1/4
|
||||
E4 1/4
|
||||
|
||||
E4 1/8
|
||||
Fd4 1/8
|
||||
|
||||
G4 1/4
|
||||
A4 1/4
|
||||
C5 1/4
|
||||
|
||||
B4 1/2+1/4
|
||||
|
||||
A5 1/8
|
||||
G5 1/8
|
||||
F5 1/8
|
||||
E5 1/8
|
||||
D5 1/8
|
||||
F5 1/8
|
||||
|
||||
E5 1/4
|
||||
B4 1/4
|
||||
|
||||
B4 1/8
|
||||
E5 1/8
|
||||
|
||||
A5 1/8
|
||||
G5 1/8
|
||||
F5 1/8
|
||||
E5 1/8
|
||||
D5 1/8
|
||||
F5 1/8
|
||||
|
||||
E5 1/2
|
||||
E4 1/8
|
||||
A4 1/8
|
||||
|
||||
D5 1/8
|
||||
Cd5 1/8
|
||||
B4 1/8
|
||||
A4 1/8
|
||||
B4 1/8
|
||||
Cd5 1/8
|
||||
|
||||
B4 1/4
|
||||
E4 1/4
|
||||
E4 1/8
|
||||
Fd4 1/8
|
||||
|
||||
G4 1/4
|
||||
Fd4 1/4
|
||||
D4 1/4
|
||||
E4 1/2+1/4
|
6
examples/determination/project.yml
Normal file
6
examples/determination/project.yml
Normal file
|
@ -0,0 +1,6 @@
|
|||
name: Determination
|
||||
bpm: 120
|
||||
channels:
|
||||
- type: FM
|
||||
path: main
|
||||
volume: 0.7
|
22
examples/megalovania-no-delay/channel
Normal file
22
examples/megalovania-no-delay/channel
Normal file
|
@ -0,0 +1,22 @@
|
|||
@2000
|
||||
D4 0.0625 0
|
||||
D4 0.0625 0
|
||||
D5 0.1875 0
|
||||
A4 0.1875 0
|
||||
Gd4 0.125 0
|
||||
G4 0.125 0
|
||||
F4 0.125 0
|
||||
D4 0.125 0.0
|
||||
F4 0.0625 0
|
||||
G4 0.0625 0
|
||||
@1000
|
||||
C4 0.0625 0.0
|
||||
C4 0.0625 0
|
||||
D5 0.1875 0
|
||||
A4 0.1875 0
|
||||
Gd4 0.125 0
|
||||
G4 0.125 0
|
||||
F4 0.125 0
|
||||
D4 0.0625 0.0
|
||||
F4 0.0625 0
|
||||
G4 0.0625 0.0
|
7
examples/megalovania-no-delay/project.yml
Normal file
7
examples/megalovania-no-delay/project.yml
Normal file
|
@ -0,0 +1,7 @@
|
|||
name: Megalovania
|
||||
bpm: 210
|
||||
channels:
|
||||
- type: SSG
|
||||
path: channel
|
||||
- type: FM
|
||||
path: channel
|
24
examples/megalovania/channel
Normal file
24
examples/megalovania/channel
Normal file
|
@ -0,0 +1,24 @@
|
|||
#1
|
||||
@1
|
||||
D4 0.0625 0.05
|
||||
D4 0.0625 1/80
|
||||
D5 0.1875 0.0125
|
||||
A4 0.1875 0.0125
|
||||
Gd4 0.125 0.0625
|
||||
G4 0.125 0.0625
|
||||
F4 0.125 0.0625
|
||||
D4 0.125 0.025
|
||||
F4 0.0625 0.0125
|
||||
G4 0.0625 0.0125
|
||||
#-1
|
||||
@-1
|
||||
C4 0.0625 0.05
|
||||
C4 0.0625 0.0125
|
||||
D5 0.1875 0.0125
|
||||
A4 0.1875 0.0125
|
||||
Gd4 0.125 0.0625
|
||||
G4 0.125 0.0625
|
||||
F4 0.125 0.0625
|
||||
D4 0.0625 0.025
|
||||
F4 0.0625 0.0125
|
||||
G4 0.0625 0.0125
|
9
examples/megalovania/project.yml
Normal file
9
examples/megalovania/project.yml
Normal file
|
@ -0,0 +1,9 @@
|
|||
name: Megalovania
|
||||
bpm: 210
|
||||
channels:
|
||||
- type: SSG
|
||||
path: channel
|
||||
volume: 0.3
|
||||
- type: FM
|
||||
path: channel
|
||||
volume: 1
|
12
rinth-midi/Cargo.toml
Normal file
12
rinth-midi/Cargo.toml
Normal file
|
@ -0,0 +1,12 @@
|
|||
[package]
|
||||
name = "rinth-midi"
|
||||
version = "0.0.1"
|
||||
edition = "2021"
|
||||
publish = false
|
||||
license-file = "../LICENSE"
|
||||
authors = [ "Ivan Bushchik <ivabus@ivabus.dev>" ]
|
||||
|
||||
[dependencies]
|
||||
rinth-types = { path="../rinth-types" }
|
||||
clap = { version = "4.4.18", features = ["derive"] }
|
||||
midly = "0.5.3"
|
73
rinth-midi/src/main.rs
Normal file
73
rinth-midi/src/main.rs
Normal file
|
@ -0,0 +1,73 @@
|
|||
use clap::Parser;
|
||||
use midly::num::{u28, u7};
|
||||
use midly::{MidiMessage, Smf, TrackEventKind};
|
||||
use rinth_types::note::{Note, Tone};
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[derive(Parser)]
|
||||
#[command(version)]
|
||||
struct Args {
|
||||
midi: PathBuf,
|
||||
|
||||
#[arg(short, default_value = "0")]
|
||||
channel: u8,
|
||||
}
|
||||
fn main() {
|
||||
let args = Args::parse();
|
||||
let binding = std::fs::read(args.midi).unwrap();
|
||||
let smf = Smf::parse(binding.as_slice()).unwrap();
|
||||
let mut is_pressed = false;
|
||||
let mut delay = 0;
|
||||
let mut note_pressed = u7::new(0);
|
||||
let mut timer = u28::new(0);
|
||||
let mut notes: Vec<Note> = vec![];
|
||||
let note_names = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"];
|
||||
for track in smf.tracks.iter() {
|
||||
for event in track {
|
||||
if let TrackEventKind::Midi {
|
||||
channel: _channel,
|
||||
message,
|
||||
} = event.kind
|
||||
{
|
||||
match message {
|
||||
MidiMessage::NoteOff {
|
||||
key,
|
||||
..
|
||||
} => {
|
||||
if is_pressed && note_pressed == key {
|
||||
let note = Note {
|
||||
tone: Tone::from(&format!(
|
||||
"{}{}",
|
||||
note_names[(key.as_int() % 12) as usize],
|
||||
(key.as_int() / 12) - 1
|
||||
)),
|
||||
length: event.delta.as_int() as f32 / 1000.0,
|
||||
delay: delay as f32 / 1000.0,
|
||||
};
|
||||
notes.push(note);
|
||||
note_pressed = u7::new(0);
|
||||
is_pressed = false;
|
||||
}
|
||||
}
|
||||
MidiMessage::NoteOn {
|
||||
key,
|
||||
..
|
||||
} => {
|
||||
if !is_pressed {
|
||||
eprintln!("pressing on {}", key);
|
||||
is_pressed = true;
|
||||
note_pressed = key;
|
||||
timer = u28::new(0);
|
||||
delay = event.delta.as_int();
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
timer += event.delta;
|
||||
}
|
||||
}
|
||||
for note in notes {
|
||||
println!("{} {} {}", note.tone, note.length, note.delay);
|
||||
}
|
||||
}
|
15
rinth-synth/Cargo.toml
Normal file
15
rinth-synth/Cargo.toml
Normal file
|
@ -0,0 +1,15 @@
|
|||
[package]
|
||||
name = "rinth-synth"
|
||||
version = "0.0.1"
|
||||
edition = "2021"
|
||||
publish = false
|
||||
license-file = "../LICENSE"
|
||||
authors = [ "Ivan Bushchik <ivabus@ivabus.dev>" ]
|
||||
|
||||
[dependencies]
|
||||
rinth-types = {path = "../rinth-types" }
|
||||
hound = "3.5.1"
|
||||
serde_yaml = "0.9.30"
|
||||
clap = { version = "4.4", features = ["derive"] }
|
||||
serde = { version = "1.0.195", features = ["derive"] }
|
||||
meval = "0.2.0"
|
101
rinth-synth/src/fm.rs
Normal file
101
rinth-synth/src/fm.rs
Normal file
|
@ -0,0 +1,101 @@
|
|||
use crate::shared::{add_delay, get_note};
|
||||
use rinth_types::file::Channel;
|
||||
use rinth_types::note::get_note_len;
|
||||
use rinth_types::{file::ChannelType, note::Note, traits::Synth};
|
||||
use std::f32::consts::PI;
|
||||
use std::fs;
|
||||
|
||||
pub struct FM {
|
||||
pub bpm: u16,
|
||||
pub notes: Vec<(Note, ModulationFreq, FrequencyDeviation)>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct ModulationFreq(f32);
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct FrequencyDeviation(f32);
|
||||
|
||||
const FIGHT_CLICKS: usize = 128;
|
||||
|
||||
impl Synth for FM {
|
||||
fn from_channel(channel: Channel, bpm: u16) -> Self
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
let channel = match channel {
|
||||
Channel {
|
||||
path,
|
||||
channel_type: ChannelType::FM,
|
||||
..
|
||||
} => path,
|
||||
_ => {
|
||||
unreachable!()
|
||||
}
|
||||
};
|
||||
|
||||
let contents = fs::read(channel).unwrap();
|
||||
|
||||
let mut notes: Vec<(Note, ModulationFreq, FrequencyDeviation)> = vec![];
|
||||
let mut current_tone = ModulationFreq(440.0);
|
||||
let mut current_deviation = FrequencyDeviation(8.0);
|
||||
|
||||
for line in contents.split(|&x| x == b'\n') {
|
||||
if line.is_empty() {
|
||||
continue;
|
||||
}
|
||||
let s = String::from_utf8_lossy(line);
|
||||
let l = s.split_ascii_whitespace().collect::<Vec<&str>>();
|
||||
// Ignore comment
|
||||
if l[0].as_bytes()[0] == b'/' {
|
||||
continue;
|
||||
}
|
||||
// String in FM channel may be or carrier tone or note
|
||||
if l[0].as_bytes()[0] == b'@' {
|
||||
current_tone = ModulationFreq(l[0][1..].parse::<f32>().unwrap());
|
||||
continue;
|
||||
}
|
||||
if l[0].as_bytes()[0] == b'#' {
|
||||
current_deviation = FrequencyDeviation(l[0][1..].parse::<f32>().unwrap());
|
||||
continue;
|
||||
}
|
||||
notes.push((get_note(l), current_tone, current_deviation))
|
||||
}
|
||||
FM {
|
||||
bpm,
|
||||
notes,
|
||||
}
|
||||
}
|
||||
|
||||
fn synthesise(&self, sample_rate: u32) -> Vec<f32> {
|
||||
// t - time, m - modulating freq (carrier)
|
||||
let y: fn(f32, f32, f32, f32) -> f32 =
|
||||
|t: f32, f: f32, m: f32, d| (2_f32 * PI * m * t + d * (2_f32 * PI * f * t).sin()).cos();
|
||||
let mut stream: Vec<f32> = vec![];
|
||||
for (note, tone, deviation) in &self.notes {
|
||||
if note.delay != 0_f32 {
|
||||
add_delay(&mut stream, note.delay, self.bpm, sample_rate);
|
||||
}
|
||||
// Samples in _THIS_ note
|
||||
let samples = (get_note_len(note.length, self.bpm) * sample_rate as f32) as usize;
|
||||
for k in 0..samples {
|
||||
stream.push(
|
||||
y(k as f32 / sample_rate as f32, tone.0, note.tone.get_freq(), deviation.0)
|
||||
// This makes a little linear fade-in-out so we don't get "clicks"
|
||||
* if k <= FIGHT_CLICKS {
|
||||
k as f32 / FIGHT_CLICKS as f32
|
||||
} else if k
|
||||
>= samples
|
||||
- FIGHT_CLICKS
|
||||
{
|
||||
(samples
|
||||
- k) as f32 / FIGHT_CLICKS as f32
|
||||
} else {
|
||||
1.0
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
stream
|
||||
}
|
||||
}
|
127
rinth-synth/src/main.rs
Normal file
127
rinth-synth/src/main.rs
Normal file
|
@ -0,0 +1,127 @@
|
|||
use clap::Parser;
|
||||
use rinth_types::file::ChannelType;
|
||||
use rinth_types::traits::Synth;
|
||||
use std::cmp::max;
|
||||
use std::io::Read;
|
||||
use std::path::PathBuf;
|
||||
|
||||
mod fm;
|
||||
mod shared;
|
||||
mod ssg;
|
||||
|
||||
#[derive(Parser)]
|
||||
enum Commands {
|
||||
#[command(visible_alias = "b")]
|
||||
Build(Build),
|
||||
#[command(visible_alias = "m")]
|
||||
Master(Build),
|
||||
}
|
||||
|
||||
#[derive(Parser)]
|
||||
#[command(author, about, version)]
|
||||
struct Build {
|
||||
path: PathBuf,
|
||||
#[arg(short, long, default_value = "44100")]
|
||||
sample_rate: u32,
|
||||
}
|
||||
|
||||
fn truncate(sample: f32) -> f32 {
|
||||
if sample.abs() > 1_f32 {
|
||||
sample.signum()
|
||||
} else {
|
||||
sample
|
||||
}
|
||||
}
|
||||
|
||||
fn merge(master: &mut Vec<f32>, slave: Vec<f32>) {
|
||||
master.resize(max(master.len(), slave.len()), 0_f32);
|
||||
let slave_len = slave.len();
|
||||
for (n, sample) in &mut master.iter_mut().enumerate() {
|
||||
*sample = if n < slave_len {
|
||||
truncate(*sample + slave[n])
|
||||
} else {
|
||||
truncate(*sample)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let args = Commands::parse();
|
||||
|
||||
let (args, mastering) = match args {
|
||||
Commands::Build(args) => (args, false),
|
||||
Commands::Master(args) => (args, true),
|
||||
};
|
||||
let mut project_file = std::fs::File::open(&args.path).unwrap();
|
||||
let mut project = String::new();
|
||||
project_file.read_to_string(&mut project).unwrap();
|
||||
|
||||
let project: rinth_types::file::Header = serde_yaml::from_str(&project).unwrap();
|
||||
let spec = hound::WavSpec {
|
||||
channels: 1,
|
||||
sample_rate: args.sample_rate,
|
||||
bits_per_sample: 32,
|
||||
sample_format: hound::SampleFormat::Float,
|
||||
};
|
||||
let mut master = vec![];
|
||||
for channel in project.channels {
|
||||
let mut channel = channel;
|
||||
let mut channel_path = args.path.parent().unwrap().to_path_buf();
|
||||
channel_path.push(channel.path);
|
||||
channel.path = channel_path;
|
||||
|
||||
let synth: Box<dyn Synth> = match channel.channel_type {
|
||||
ChannelType::FM => Box::new(fm::FM::from_channel(channel.clone(), project.bpm)),
|
||||
ChannelType::SSG => Box::new(ssg::SSG::from_channel(channel.clone(), project.bpm)),
|
||||
};
|
||||
if !mastering {
|
||||
let mut writer = hound::WavWriter::create(
|
||||
format!(
|
||||
"{}-{:?}.wav",
|
||||
channel.path.as_os_str().to_str().unwrap(),
|
||||
channel.channel_type
|
||||
),
|
||||
spec,
|
||||
)
|
||||
.unwrap();
|
||||
synth.synthesise(args.sample_rate).iter().for_each(|sample| {
|
||||
writer
|
||||
.write_sample(if let Some(volume) = channel.volume {
|
||||
*sample * volume
|
||||
} else {
|
||||
*sample
|
||||
})
|
||||
.unwrap()
|
||||
});
|
||||
writer.finalize().unwrap();
|
||||
} else {
|
||||
merge(
|
||||
&mut master,
|
||||
synth
|
||||
.synthesise(args.sample_rate)
|
||||
.iter()
|
||||
.map(|sample| {
|
||||
if let Some(volume) = channel.volume {
|
||||
*sample * volume
|
||||
} else {
|
||||
*sample
|
||||
}
|
||||
})
|
||||
.collect(),
|
||||
);
|
||||
}
|
||||
}
|
||||
if mastering {
|
||||
let mut writer = hound::WavWriter::create(
|
||||
format!(
|
||||
"{}/{}-master.wav",
|
||||
args.path.parent().unwrap().to_path_buf().to_str().unwrap(),
|
||||
project.name
|
||||
),
|
||||
spec,
|
||||
)
|
||||
.unwrap();
|
||||
master.iter().for_each(|sample| writer.write_sample(*sample).unwrap());
|
||||
writer.finalize().unwrap();
|
||||
}
|
||||
}
|
27
rinth-synth/src/shared.rs
Normal file
27
rinth-synth/src/shared.rs
Normal file
|
@ -0,0 +1,27 @@
|
|||
use rinth_types::note::{get_note_len, Note, Tone};
|
||||
|
||||
pub const DEFAULT_DELAY: f32 = 0.0;
|
||||
|
||||
#[inline(always)]
|
||||
pub fn add_delay(stream: &mut Vec<f32>, delay: f32, bpm: u16, sample_rate: u32) {
|
||||
// println!("Pushing delay of len {}s", get_note_len(delay, bpm));
|
||||
stream.append(&mut vec![0_f32; (get_note_len(delay, bpm) * sample_rate as f32) as usize]);
|
||||
}
|
||||
|
||||
pub fn get_note(l: Vec<&str>) -> Note {
|
||||
Note {
|
||||
tone: Tone::from(l[0]),
|
||||
length: match l[1].parse::<f32>() {
|
||||
Ok(f) => f,
|
||||
Err(_) => meval::eval_str(l[1]).unwrap() as f32,
|
||||
},
|
||||
delay: if l.len() > 2 {
|
||||
match l[2].parse::<f32>() {
|
||||
Ok(f) => f,
|
||||
Err(_) => meval::eval_str(l[2]).unwrap() as f32,
|
||||
}
|
||||
} else {
|
||||
DEFAULT_DELAY
|
||||
},
|
||||
}
|
||||
}
|
68
rinth-synth/src/ssg.rs
Normal file
68
rinth-synth/src/ssg.rs
Normal file
|
@ -0,0 +1,68 @@
|
|||
use crate::shared::{add_delay, get_note};
|
||||
use rinth_types::file::Channel;
|
||||
use rinth_types::note::get_note_len;
|
||||
use rinth_types::{file::ChannelType, note::Note, traits::Synth};
|
||||
use std::f32::consts::PI;
|
||||
use std::fs;
|
||||
|
||||
#[allow(clippy::upper_case_acronyms)]
|
||||
pub struct SSG {
|
||||
pub bpm: u16,
|
||||
pub notes: Vec<Note>,
|
||||
}
|
||||
|
||||
impl Synth for SSG {
|
||||
fn from_channel(channel: Channel, bpm: u16) -> Self
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
let channel = match channel {
|
||||
Channel {
|
||||
path,
|
||||
channel_type: ChannelType::SSG,
|
||||
..
|
||||
} => path,
|
||||
_ => {
|
||||
unreachable!()
|
||||
}
|
||||
};
|
||||
|
||||
let contents = fs::read(channel).unwrap();
|
||||
|
||||
let mut notes: Vec<Note> = vec![];
|
||||
for line in contents.split(|&x| x == b'\n') {
|
||||
if line.is_empty() {
|
||||
continue;
|
||||
}
|
||||
let s = String::from_utf8_lossy(line);
|
||||
let l = s.split_ascii_whitespace().collect::<Vec<&str>>();
|
||||
// Ignoring FM-only lines and comments
|
||||
if l[0].as_bytes()[0] == b'@'
|
||||
|| l[0].as_bytes()[0] == b'#'
|
||||
|| l[0].as_bytes()[0] == b'/'
|
||||
{
|
||||
continue;
|
||||
}
|
||||
notes.push(get_note(l))
|
||||
}
|
||||
SSG {
|
||||
bpm,
|
||||
notes,
|
||||
}
|
||||
}
|
||||
|
||||
fn synthesise(&self, sample_rate: u32) -> Vec<f32> {
|
||||
// t - time, m - modulating freq (carrier)
|
||||
let y: fn(f32, f32) -> f32 = |t: f32, f: f32| (2_f32 * PI * f * t).sin().signum();
|
||||
let mut stream: Vec<f32> = vec![];
|
||||
for note in &self.notes {
|
||||
if note.delay != 0_f32 {
|
||||
add_delay(&mut stream, note.delay, self.bpm, sample_rate);
|
||||
}
|
||||
for k in 0..(get_note_len(note.length, self.bpm) * sample_rate as f32) as usize {
|
||||
stream.push(y(k as f32 / sample_rate as f32, note.tone.get_freq()))
|
||||
}
|
||||
}
|
||||
stream
|
||||
}
|
||||
}
|
11
rinth-types/Cargo.toml
Normal file
11
rinth-types/Cargo.toml
Normal file
|
@ -0,0 +1,11 @@
|
|||
[package]
|
||||
name = "rinth-types"
|
||||
version = "0.0.1"
|
||||
edition = "2021"
|
||||
publish = false
|
||||
license-file = "../LICENSE"
|
||||
authors = [ "Ivan Bushchik <ivabus@ivabus.dev>" ]
|
||||
|
||||
[dependencies]
|
||||
toml = "0.8.8"
|
||||
serde = { version = "1.0.195", features = ["derive"] }
|
28
rinth-types/src/file.rs
Normal file
28
rinth-types/src/file.rs
Normal file
|
@ -0,0 +1,28 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
use std::path::PathBuf;
|
||||
|
||||
pub enum FileType {
|
||||
Header(Header),
|
||||
Channel(Channel),
|
||||
}
|
||||
|
||||
#[derive(Clone, Deserialize, Serialize, Debug)]
|
||||
pub enum ChannelType {
|
||||
FM,
|
||||
SSG,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
pub struct Header {
|
||||
pub name: String,
|
||||
pub bpm: u16,
|
||||
pub channels: Vec<Channel>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Deserialize, Serialize)]
|
||||
pub struct Channel {
|
||||
pub path: PathBuf,
|
||||
#[serde(alias = "type")]
|
||||
pub channel_type: ChannelType,
|
||||
pub volume: Option<f32>,
|
||||
}
|
3
rinth-types/src/lib.rs
Normal file
3
rinth-types/src/lib.rs
Normal file
|
@ -0,0 +1,3 @@
|
|||
pub mod file;
|
||||
pub mod note;
|
||||
pub mod traits;
|
72
rinth-types/src/note.rs
Normal file
72
rinth-types/src/note.rs
Normal file
|
@ -0,0 +1,72 @@
|
|||
//use rinth_macros::*;
|
||||
use std::fmt::{Display, Formatter};
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// Get real length of note from musical length
|
||||
pub fn get_note_len(len: f32, bpm: u16) -> f32 {
|
||||
len * 240_f32 / bpm as f32
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct Note {
|
||||
pub tone: Tone,
|
||||
pub length: f32,
|
||||
pub delay: f32,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ToneError {
|
||||
InvalidTone(String),
|
||||
}
|
||||
|
||||
impl Display for ToneError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{}",
|
||||
match self {
|
||||
ToneError::InvalidTone(s) => format!("Invalid tone ({})", s),
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct Tone(String);
|
||||
|
||||
impl Tone {
|
||||
pub fn from(t: &str) -> Self {
|
||||
Tone(t.to_string())
|
||||
}
|
||||
|
||||
pub fn get_freq(&self) -> f32 {
|
||||
const BASE_FREQ: f32 = 440.0;
|
||||
let sr = self.to_string();
|
||||
let modifier: i8 = if sr.as_bytes()[1] == b'b' {
|
||||
-1
|
||||
} else if sr.as_bytes()[1] == b'd' {
|
||||
1
|
||||
} else {
|
||||
0
|
||||
};
|
||||
let magic_num = (12 * sr.get(sr.len() - 1..).unwrap().parse::<i8>().unwrap() + modifier)
|
||||
as u8 + match sr.as_bytes()[0] {
|
||||
b'C' => 2,
|
||||
b'D' => 4,
|
||||
b'E' => 6,
|
||||
b'F' => 7,
|
||||
b'G' => 9,
|
||||
b'A' => 11,
|
||||
b'B' => 13,
|
||||
_ => 0,
|
||||
};
|
||||
BASE_FREQ * 2_f32.powf((magic_num as f32 - 59_f32) / 12_f32)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Tone {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_str(&self.0.to_string())
|
||||
}
|
||||
}
|
9
rinth-types/src/traits.rs
Normal file
9
rinth-types/src/traits.rs
Normal file
|
@ -0,0 +1,9 @@
|
|||
use crate::file::Channel;
|
||||
|
||||
pub trait Synth {
|
||||
fn from_channel(channel: Channel, bpm: u16) -> Self
|
||||
where
|
||||
Self: Sized;
|
||||
|
||||
fn synthesise(&self, sample_rate: u32) -> Vec<f32>;
|
||||
}
|
Loading…
Reference in a new issue