1use std::cmp::max;
2
3use crate::blip_buf::BlipBuf;
4use crate::oscillator::{Effect, Gain, Oscillator, ToneIndex};
5use crate::settings::{
6 EFFECT_NONE, EFFECT_SLIDE, INITIAL_CHANNEL_GAIN, MAX_EFFECT, MAX_NOTE, MAX_TONE, MAX_VOLUME,
7 TONE_TRIANGLE,
8};
9use crate::sound::{SharedSound, Sound};
10
11pub type Note = i16;
12pub type Volume = u16;
13pub type Speed = u32;
14pub type Detune = i32;
15
16pub struct Channel {
17 oscillator: Oscillator,
18 is_first_note: bool,
19 is_playing: bool,
20 should_loop: bool,
21 should_resume: bool,
22 sound_index: u32,
23 note_index: u32,
24 tick_count: u32,
25 resume_sounds: Vec<Sound>,
26 resume_start_tick: u32,
27 resume_should_loop: bool,
28 pub sounds: Vec<Sound>,
29 pub gain: Gain,
30 pub detune: Detune,
31}
32
33pub type SharedChannel = shared_type!(Channel);
34
35impl Channel {
36 pub fn new() -> SharedChannel {
37 new_shared_type!(Self {
38 oscillator: Oscillator::new(),
39 is_first_note: true,
40 is_playing: false,
41 should_loop: false,
42 should_resume: false,
43 sound_index: 0,
44 note_index: 0,
45 tick_count: 0,
46 resume_sounds: Vec::new(),
47 resume_start_tick: 0,
48 resume_should_loop: false,
49 sounds: Vec::new(),
50 gain: INITIAL_CHANNEL_GAIN,
51 detune: 0,
52 })
53 }
54
55 pub fn play(
56 &mut self,
57 sounds: Vec<SharedSound>,
58 start_tick: Option<u32>,
59 should_loop: bool,
60 should_resume: bool,
61 ) {
62 let sounds: Vec<Sound> = sounds.iter().map(|sound| sound.lock().clone()).collect();
63 if sounds.is_empty() || sounds.iter().all(|sound| sound.notes.is_empty()) {
64 return;
65 }
66
67 if !should_resume {
68 self.resume_sounds.clone_from(&sounds);
69 self.resume_should_loop = should_loop;
70 self.resume_start_tick = start_tick.unwrap_or(0);
71 }
72
73 self.sounds = sounds;
74 self.should_loop = should_loop;
75 self.should_resume = self.is_playing && should_resume;
76 self.sound_index = 0;
77 self.note_index = 0;
78 self.tick_count = start_tick.unwrap_or(0);
79
80 loop {
81 let sound = &self.sounds[self.sound_index as usize];
82 let sound_ticks = sound.notes.len() as u32 * sound.speed;
83 if self.tick_count < sound_ticks {
84 self.note_index = self.tick_count / sound.speed;
85 self.tick_count %= sound.speed;
86 break;
87 }
88 self.tick_count -= sound_ticks;
89 self.sound_index += 1;
90 if self.sound_index >= self.sounds.len() as u32 {
91 if should_loop {
92 self.sound_index = 0;
93 } else {
94 return;
95 }
96 }
97 }
98
99 self.is_first_note = true;
100 self.is_playing = true;
101 }
102
103 pub fn play1(
104 &mut self,
105 sound: SharedSound,
106 start_tick: Option<u32>,
107 should_loop: bool,
108 should_resume: bool,
109 ) {
110 self.play(vec![sound], start_tick, should_loop, should_resume);
111 }
112
113 pub fn stop(&mut self) {
114 self.is_playing = false;
115 self.oscillator.stop();
116 }
117
118 pub fn play_pos(&mut self) -> Option<(u32, u32)> {
119 if self.is_playing {
120 Some((self.sound_index, self.note_index))
121 } else {
122 None
123 }
124 }
125
126 pub(crate) fn update(&mut self, blip_buf: &mut BlipBuf) {
127 if !self.is_playing {
128 return;
129 }
130
131 let mut sound = &self.sounds[self.sound_index as usize];
132 let speed = max(sound.speed, 1);
133
134 if self.tick_count % speed == 0 {
135 if self.tick_count > 0 {
136 self.note_index += 1;
137 }
138
139 while self.note_index >= sound.notes.len() as u32 {
140 self.is_first_note = true;
141 self.sound_index += 1;
142 self.note_index = 0;
143
144 if self.sound_index >= self.sounds.len() as u32 {
145 if self.should_loop {
146 self.sound_index = 0;
147 } else {
148 self.stop();
149 if self.should_resume {
150 let sounds = self
151 .resume_sounds
152 .iter()
153 .map(|sound| new_shared_type!(sound.clone()))
154 .collect();
155 self.play(
156 sounds,
157 Some(self.resume_start_tick + 1),
158 self.resume_should_loop,
159 false,
160 );
161 }
162 return;
163 }
164 }
165
166 sound = &self.sounds[self.sound_index as usize];
167 }
168
169 let note = Self::circular_note(&sound.notes, self.note_index);
170 assert!(note <= MAX_NOTE, "invalid sound note {note}");
171 let volume = Self::circular_volume(&sound.volumes, self.note_index);
172 assert!(volume <= MAX_VOLUME, "invalid sound volume {volume}");
173 let tone = Self::circular_tone(&sound.tones, self.note_index);
174 assert!(tone <= MAX_TONE, "invalid sound tone {tone}");
175 let mut effect = Self::circular_effect(&sound.effects, self.note_index);
176 assert!(effect <= MAX_EFFECT, "invalid sound effect {effect}");
177 let speed = max(sound.speed, 1);
178
179 if note >= 0 && volume > 0 {
180 if self.is_first_note {
181 self.is_first_note = false;
182 if effect == EFFECT_SLIDE {
183 effect = EFFECT_NONE;
184 }
185 }
186 self.oscillator.play(
187 note as f64 + self.detune as f64 / 200.0,
188 tone,
189 self.gain * volume as f64 / MAX_VOLUME as f64,
190 effect,
191 speed,
192 );
193 }
194 }
195
196 self.oscillator.update(blip_buf);
197 self.tick_count += 1;
198 self.resume_start_tick += 1;
199 }
200
201 const fn circular_note(notes: &[Note], index: u32) -> Note {
202 let len = notes.len();
203 if len > 0 {
204 notes[index as usize % len]
205 } else {
206 0
207 }
208 }
209
210 const fn circular_tone(tones: &[ToneIndex], index: u32) -> ToneIndex {
211 let len = tones.len();
212 if len > 0 {
213 tones[index as usize % len]
214 } else {
215 TONE_TRIANGLE
216 }
217 }
218
219 const fn circular_volume(volumes: &[Volume], index: u32) -> Volume {
220 let len = volumes.len();
221 if len > 0 {
222 volumes[index as usize % len]
223 } else {
224 MAX_VOLUME
225 }
226 }
227
228 const fn circular_effect(effects: &[Effect], index: u32) -> Effect {
229 let len = effects.len();
230 if len > 0 {
231 effects[index as usize % len]
232 } else {
233 EFFECT_NONE
234 }
235 }
236}