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