torque_tracker_engine/project/
song.rs1use std::array;
2use std::fmt::{Debug, Formatter};
3use std::num::NonZero;
4
5use super::pattern::{Pattern, PatternOperation};
6use crate::channel::Pan;
7use crate::file::impulse_format;
8use crate::file::impulse_format::header::PatternOrder;
9use crate::manager::Collector;
10use crate::sample::{Sample, SampleMetaData};
11
12#[derive(Clone, Debug)]
13pub struct Song {
14 pub global_volume: u8,
15 pub mix_volume: u8,
16 pub initial_speed: NonZero<u8>,
18 pub initial_tempo: NonZero<u8>,
20 pub pan_separation: u8,
21 pub pitch_wheel_depth: u8,
22
23 pub patterns: [Pattern; Song::MAX_PATTERNS],
24 pub pattern_order: [PatternOrder; Song::MAX_ORDERS],
25 pub volume: [u8; Song::MAX_CHANNELS],
26 pub pan: [Pan; Song::MAX_CHANNELS],
27 pub samples: [Option<(SampleMetaData, Sample)>; Song::MAX_SAMPLES_INSTR],
28}
29
30impl Song {
31 pub const MAX_ORDERS: usize = 256;
32 pub const MAX_PATTERNS: usize = 240;
33 pub const MAX_SAMPLES_INSTR: usize = 236;
34 pub const MAX_CHANNELS: usize = 64;
35
36 pub fn next_pattern(&self, order: &mut u16) -> Option<u8> {
39 loop {
40 match self.get_order(*order) {
41 PatternOrder::Number(pattern) => break Some(pattern),
42 PatternOrder::EndOfSong => break None,
43 PatternOrder::SkipOrder => (),
44 }
45 *order += 1;
46 }
47 }
48
49 pub(crate) fn get_order(&self, order: u16) -> PatternOrder {
51 self.pattern_order
52 .get(usize::from(order))
53 .copied()
54 .unwrap_or_default()
55 }
56
57 pub fn copy_values_from_header(&mut self, header: &impulse_format::header::ImpulseHeader) {
62 self.global_volume = header.global_volume;
63 self.initial_speed = NonZero::new(header.initial_speed).unwrap();
65 self.initial_tempo = NonZero::new(header.initial_tempo).unwrap();
66 self.mix_volume = header.mix_volume;
67 self.pan_separation = header.pan_separation;
68 self.pitch_wheel_depth = header.pitch_wheel_depth;
69
70 self.pan = header.channel_pan;
71 self.volume = header.channel_volume;
72
73 for (idx, order) in header.orders.iter().enumerate() {
74 self.pattern_order[idx] = *order;
75 }
76 }
77
78 pub fn dbg_relevant(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
80 write!(f, "global_volume: {}, ", self.global_volume)?;
81 write!(f, "mix_volume: {}, ", self.mix_volume)?;
82 write!(f, "initial_speed: {}, ", self.initial_speed)?;
83 write!(f, "initial_tempo: {}, ", self.initial_tempo)?;
84 write!(f, "pan_seperation: {}, ", self.pan_separation)?;
85 write!(f, "pitch_wheel_depth: {}, ", self.pitch_wheel_depth)?;
86 write!(
87 f,
88 "{} not empty patterns, ",
89 self.patterns.iter().filter(|p| !p.is_empty()).count()
90 )?;
91 write!(
92 f,
93 "{} orders, ",
94 self.pattern_order
95 .iter()
96 .filter(|o| **o != PatternOrder::EndOfSong)
97 .count()
98 )?;
99 Ok(())
100 }
101}
102
103impl Default for Song {
104 fn default() -> Self {
105 Self {
106 global_volume: 128,
107 mix_volume: Default::default(),
108 initial_speed: NonZero::new(6).unwrap(),
109 initial_tempo: NonZero::new(125).unwrap(),
110 pan_separation: 128,
111 pitch_wheel_depth: Default::default(),
112 patterns: array::from_fn(|_| Pattern::default()),
113 pattern_order: array::from_fn(|_| PatternOrder::default()),
114 volume: array::from_fn(|_| 64),
115 pan: array::from_fn(|_| Pan::default()),
116 samples: array::from_fn(|_| None),
117 }
118 }
119}
120
121#[derive(Clone, Debug)]
123pub enum SongOperation {
124 SetVolume(u8, u8),
125 SetPan(u8, Pan),
126 SetSample(u8, SampleMetaData, Sample),
127 RemoveSample(u8),
128 PatternOperation(u8, PatternOperation),
129 SetOrder(u16, PatternOrder),
130 SetInitialSpeed(NonZero<u8>),
131 SetInitialTempo(NonZero<u8>),
132 SetGlobalVol(u8),
133}
134
135#[derive(Clone, Debug)]
137pub(crate) enum ValidOperation {
138 SetVolume(u8, u8),
139 SetPan(u8, Pan),
140 SetSample(u8, SampleMetaData, Sample),
141 RemoveSample(u8),
142 PatternOperation(u8, PatternOperation),
143 SetOrder(u16, PatternOrder),
144 SetInitialSpeed(NonZero<u8>),
145 SetInitialTempo(NonZero<u8>),
146 SetGlobalVol(u8),
147}
148
149impl ValidOperation {
150 pub(crate) fn new(
151 op: SongOperation,
152 handle: &mut Collector,
153 song: &Song,
154 ) -> Result<ValidOperation, SongOperation> {
155 let valid = match op {
156 SongOperation::SetVolume(c, _) => usize::from(c) < Song::MAX_CHANNELS,
157 SongOperation::SetPan(c, _) => usize::from(c) < Song::MAX_CHANNELS,
158 SongOperation::SetSample(idx, _, _) => usize::from(idx) < Song::MAX_SAMPLES_INSTR,
159 SongOperation::RemoveSample(idx) => usize::from(idx) < Song::MAX_SAMPLES_INSTR,
160 SongOperation::PatternOperation(idx, op) => match song.patterns.get(usize::from(idx)) {
161 Some(pattern) => pattern.operation_is_valid(&op),
162 None => false,
163 },
164 SongOperation::SetOrder(idx, _) => usize::from(idx) < Song::MAX_ORDERS,
165 SongOperation::SetInitialSpeed(_) => true,
166 SongOperation::SetInitialTempo(_) => true,
167 SongOperation::SetGlobalVol(_) => true,
168 };
169
170 if valid {
171 Ok(match op {
172 SongOperation::SetVolume(c, v) => Self::SetVolume(c, v),
173 SongOperation::SetPan(c, pan) => Self::SetPan(c, pan),
174 SongOperation::SetSample(i, meta_data, sample) => {
175 handle.add_sample(sample.clone());
176 Self::SetSample(i, meta_data, sample)
177 }
178 SongOperation::RemoveSample(i) => Self::RemoveSample(i),
179 SongOperation::PatternOperation(i, pattern_operation) => {
180 Self::PatternOperation(i, pattern_operation)
181 }
182 SongOperation::SetOrder(i, pattern_order) => Self::SetOrder(i, pattern_order),
183 SongOperation::SetInitialSpeed(s) => Self::SetInitialSpeed(s),
184 SongOperation::SetInitialTempo(t) => Self::SetInitialTempo(t),
185 SongOperation::SetGlobalVol(v) => Self::SetGlobalVol(v),
186 })
187 } else {
188 Err(op)
189 }
190 }
191}
192
193impl simple_left_right::Absorb<ValidOperation> for Song {
194 fn absorb(&mut self, operation: ValidOperation) {
195 match operation {
196 ValidOperation::SetVolume(i, val) => self.volume[usize::from(i)] = val,
197 ValidOperation::SetPan(i, val) => self.pan[usize::from(i)] = val,
198 ValidOperation::SetSample(i, meta, sample) => {
199 self.samples[usize::from(i)] = Some((meta, sample))
200 }
201 ValidOperation::RemoveSample(i) => self.samples[usize::from(i)] = None,
202 ValidOperation::PatternOperation(i, op) => {
203 self.patterns[usize::from(i)].apply_operation(op)
204 }
205 ValidOperation::SetOrder(i, order) => self.pattern_order[usize::from(i)] = order,
206 ValidOperation::SetInitialSpeed(s) => self.initial_speed = s,
207 ValidOperation::SetInitialTempo(t) => self.initial_tempo = t,
208 ValidOperation::SetGlobalVol(v) => self.global_volume = v,
209 }
210 }
211}