m8_files/instruments/
sampler.rs

1use crate::instruments::common::*;
2use crate::reader::*;
3use crate::version::*;
4use crate::writer::Writer;
5use num_enum::IntoPrimitive;
6use num_enum::TryFromPrimitive;
7
8use super::dests;
9use super::CommandPack;
10
11#[repr(u8)]
12#[allow(non_camel_case_types)]
13#[derive(IntoPrimitive, TryFromPrimitive, PartialEq, Copy, Clone, Default, Debug)]
14pub enum SamplePlayMode {
15    #[default]
16    FWD,
17    REV,
18    FWDLOOP,
19    REVLOOP,
20    FWD_PP,
21    REV_PP,
22    OSC,
23    OSC_REV,
24    OSC_PP,
25}
26
27#[derive(PartialEq, Debug, Clone)]
28pub struct Sampler {
29    pub number: u8,
30    pub name: String,
31    pub transpose: bool,
32    pub table_tick: u8,
33    pub synth_params: SynthParams,
34
35    pub sample_path: String,
36    pub play_mode: SamplePlayMode,
37    pub slice: u8,
38    pub start: u8,
39    pub loop_start: u8,
40    pub length: u8,
41    pub degrade: u8,
42}
43
44#[rustfmt::skip] // Keep constants with important order vertical for maintenance
45const SAMPLER_FX_COMMANDS : [&'static str; CommandPack::BASE_INSTRUMENT_COMMAND_COUNT + 1] = [
46    "VOL",
47    "PIT",
48    "FIN",
49    "PLY",
50    "STA",
51    "LOP",
52    "LEN",
53    "DEG",
54    "FLT",
55    "CUT",
56    "RES",
57    "AMP",
58    "LIM",
59    "PAN",
60    "DRY",
61
62    "SCH",
63    "SDL",
64    "SRV",
65
66    // EXTRA command
67    "SLI"
68];
69
70#[rustfmt::skip] // Keep constants with important order vertical for maintenance
71const DESTINATIONS : [&'static str; 14] = [
72    dests::OFF,
73    dests::VOLUME,
74    dests::PITCH,
75
76    "LOOP ST",
77    "LENGTH",
78    dests::DEGRADE,
79    dests::CUTOFF,
80    dests::RES,
81    dests::AMP,
82    dests::PAN,
83    dests::MOD_AMT,
84    dests::MOD_RATE,
85    dests::MOD_BOTH,
86    dests::MOD_BINV,
87];
88
89impl Sampler {
90    pub const MOD_OFFSET: usize = 29;
91
92    pub fn command_name(&self, _ver: Version) -> &'static [&'static str] {
93        &SAMPLER_FX_COMMANDS
94    }
95
96    pub fn destination_names(&self, _ver: Version) -> &'static [&'static str] {
97        &DESTINATIONS
98    }
99
100    /// List of all the applyable filter types for the instrument
101    pub fn filter_types(&self, _ver: Version) -> &'static [&'static str] {
102        &COMMON_FILTER_TYPES
103    }
104
105    pub fn write(&self, ver: Version, w: &mut Writer) {
106        let pos = w.pos();
107        w.write_string(&self.name, 12);
108        w.write(TranspEq::from(ver, self.transpose, self.synth_params.associated_eq).into());
109        w.write(self.table_tick);
110        w.write(self.synth_params.volume);
111        w.write(self.synth_params.pitch);
112        w.write(self.synth_params.fine_tune);
113
114        w.write(self.play_mode.into());
115        w.write(self.slice);
116        w.write(self.start);
117        w.write(self.loop_start);
118        w.write(self.length);
119        w.write(self.degrade);
120
121        self.synth_params.write(ver, w, Sampler::MOD_OFFSET);
122
123        w.seek(pos + 0x56);
124        w.write_string(&self.sample_path, 128);
125    }
126
127    pub fn from_reader(
128        ver: Version,
129        reader: &mut Reader,
130        start_pos: usize,
131        number: u8,
132        version: Version,
133    ) -> M8Result<Self> {
134        let name = reader.read_string(12);
135
136        let transp_eq = TranspEq::from_version(ver, reader.read());
137        let table_tick = reader.read();
138        let volume = reader.read();
139        let pitch = reader.read();
140        let fine_tune = reader.read();
141
142        let play_mode = reader.read();
143        let slice = reader.read();
144        let start = reader.read();
145        let loop_start = reader.read();
146        let length = reader.read();
147        let degrade = reader.read();
148
149        let synth_params = if version.at_least(3, 0) {
150            SynthParams::from_reader3(
151                ver,
152                reader,
153                volume,
154                pitch,
155                fine_tune,
156                transp_eq.eq,
157                Sampler::MOD_OFFSET,
158            )?
159        } else {
160            SynthParams::from_reader2(reader, volume, pitch, fine_tune)?
161        };
162
163        reader.set_pos(start_pos + 0x57);
164        let sample_path = reader.read_string(128);
165
166        Ok(Sampler {
167            number,
168            name,
169            transpose: transp_eq.transpose,
170            table_tick,
171            synth_params,
172
173            sample_path,
174            play_mode: play_mode
175                .try_into()
176                .map_err(|_| ParseError(format!("Invalid play mode")))?,
177            slice,
178            start,
179            loop_start,
180            length,
181            degrade,
182        })
183    }
184}