| Crates.io | sevenate |
| lib.rs | sevenate |
| version | 0.6.2 |
| created_at | 2025-12-29 14:31:20.421858+00 |
| updated_at | 2025-12-29 14:31:20.421858+00 |
| description | Rust library for working with Yamaha DX7 patches |
| homepage | |
| repository | |
| max_upload_size | |
| id | 2010687 |
| size | 85,734 |
Rust library for working with Yamaha DX7 patches.
The crate contains several types that implement the Ranged trait.
Each of them is newtype, followed by the trait implementation.
For example, the Algorithm type represents values from 1 to 32 inclusive:
pub struct Algorithm(i32);
impl Ranged for Algorithm {
const FIRST: i32 = 1;
const LAST: i32 = 32;
const DEFAULT: i32 = 32;
fn new(value: i32) -> Self {
if Self::contains(value) {
Self(value)
}
else {
panic!("expected value in range {}...{}, got {}",
Self::FIRST, Self::LAST, value);
}
}
fn value(&self) -> i32 { self.0 }
fn contains(value: i32) -> bool {
value >= Self::FIRST && value <= Self::LAST
}
fn random_value() -> Self {
let mut rng = rand::thread_rng();
Self::new(rng.gen_range(Self::FIRST..=Self::LAST))
}
}
ranged_impl! macroIt becomes quite tedious to implement the Ranged trait for all the
required types, so the ranged_impl! macro is defined to handle the
grunt work. It generates an implementation of the Ranged trait for a given type,
along with the Default and Display traits. The default value is the
DEFAULT associated constant, while the displayed value is the actual
value wrapped by the type.
Remember to install cargo-expand if you want to see the result of the
macro expansion:
cargo install cargo-expand
Then use the cargo expand command to view the generated code.
Alternatively, you can also use Rust Analyzer in Visual Studio Code to recursively expand the macro; see the Expand macro recursively at caret command.
The Ranged trait defines the basis for constrained integer types,
with associated consts. This allows you leave the actual values of
the consts to be determined by the implementer of the trait. In this
case, the domain types defined in the crate implement Ranged.
Implementations of the Ranged trait wrap a single i32 value, since
that was the lowest common denominator between the various values of
the domain types. This could be made generic, but this seems to work
for now and is easy to use. The parameter values would fit into an i16;
for example, the value of the detune parameter ranges from -7 to 7, and
it is represented in System Exclusive messages as a value from 0 to 14.
Since i32 is the integer type inferred by default, it is much more convenient
to use.
Each domain type is a newtype ("a struct with a single component that you define to get stricter type checking" ("Programming Rust, 2nd Edition", p. 213)).
From traitMost DX7 parameters appear as one byte in the System Exclusive data. However, they often need a little adjustment to get them from the 7-bit MIDI byte to their respective range. For example, the algorithm used for a voice is 1...32, but it is stored in the SysEx data as a value in the range 0...31. Many other parameters are expressed similarly.
The From<u8> trait implementation on the Algorithm newtype is used to convert
from a System Exclusive data byte representing the algorithm (0...31)
to the actual algorithm value (1...32):
impl From<u8> for Algorithm {
fn from(item: u8) -> Self {
Algorithm(item + 1) // bring into 1...32
}
}
The detune parameter ranges from -7 to +7, so there are 15 discrete values. In the SysEx data, the value zero denotes -7, while the value 14 is +7:
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14
-7 -6 -5 -4 -3 -2 -1 0 +1 +2 +3 +4 +5 +6 +7
To get from the SysEx data byte to the actual value you need to subtract 7.
For the history and rationale of the sevenate-rs crate
see Flecks of Rust #12, Subrange types in Rust.
It contains most of the material that used to be in this README.