m8_file_parser/instruments/
sampler.rs1use 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] const 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 "SLI",
74 "ERR"
75];
76
77#[rustfmt::skip] const 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 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}