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