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] const 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 "SLI"
68];
69
70#[rustfmt::skip] const 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 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}