Skip to content
This repository was archived by the owner on Dec 29, 2024. It is now read-only.

Commit 24687ef

Browse files
committed
Big player perf improvement
Now we cache the sample data pointer and the fractional step, instead of calculating it every time we play a sample (?!?). Now it runs at 44,100 Hz on a Neotron Pico quite happily.
1 parent 94e7083 commit 24687ef

File tree

2 files changed

+40
-35
lines changed

2 files changed

+40
-35
lines changed

neoplay/src/main.rs

Lines changed: 3 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
use core::fmt::Write;
55

6-
static mut FILE_BUFFER: [u8; 128 * 1024] = [0u8; 128 * 1024];
6+
static mut FILE_BUFFER: [u8; 192 * 1024] = [0u8; 192 * 1024];
77

88
mod player;
99

@@ -43,7 +43,7 @@ fn real_main() -> Result<(), neotron_sdk::Error> {
4343
return neotron_sdk::Result::Err(neotron_sdk::Error::DeviceSpecific);
4444
}
4545

46-
let mut player = match player::Player::new(file_buffer, 11025) {
46+
let mut player = match player::Player::new(file_buffer, 44100) {
4747
Ok(player) => player,
4848
Err(e) => {
4949
let _ = writeln!(stdout, "Failed to create player: {:?}", e);
@@ -59,27 +59,14 @@ fn real_main() -> Result<(), neotron_sdk::Error> {
5959
}
6060

6161
loop {
62-
for chunk in sample_buffer.chunks_exact_mut(16) {
62+
for chunk in sample_buffer.chunks_exact_mut(4) {
6363
let (left, right) = player.next_sample(&mut stdout);
6464
let left_bytes = left.to_le_bytes();
6565
let right_bytes = right.to_le_bytes();
66-
// copy samples four times
6766
chunk[0] = left_bytes[0];
6867
chunk[1] = left_bytes[1];
6968
chunk[2] = right_bytes[0];
7069
chunk[3] = right_bytes[1];
71-
chunk[4] = left_bytes[0];
72-
chunk[5] = left_bytes[1];
73-
chunk[6] = right_bytes[0];
74-
chunk[7] = right_bytes[1];
75-
chunk[8] = left_bytes[0];
76-
chunk[9] = left_bytes[1];
77-
chunk[10] = right_bytes[0];
78-
chunk[11] = right_bytes[1];
79-
chunk[12] = left_bytes[0];
80-
chunk[13] = left_bytes[1];
81-
chunk[14] = right_bytes[0];
82-
chunk[15] = right_bytes[1];
8370
}
8471
let _ = dsp.write(&sample_buffer);
8572
let mut in_buf = [0u8; 1];

neoplay/src/player.rs

Lines changed: 37 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,15 @@
22
33
#[derive(Debug, Default)]
44
struct Channel {
5-
sample_num: u8,
5+
sample_data: Option<*const u8>,
6+
sample_loops: bool,
7+
sample_length: usize,
8+
repeat_length: usize,
9+
repeat_point: usize,
610
volume: u8,
711
note_period: u16,
812
sample_position: neotracker::Fractional,
13+
note_step: neotracker::Fractional,
914
effect: Option<neotracker::Effect>,
1015
}
1116

@@ -103,12 +108,20 @@ impl<'a> Player<'a> {
103108
if note.is_empty() {
104109
let _ = write!(out, "--- -----|");
105110
} else {
106-
if let Some(sample) = self.modfile.sample_info(note.sample_no()) {
111+
if let Some(sample) = self.modfile.sample(note.sample_no()) {
112+
// if the period is zero, keep playing the old note
107113
if note.period() != 0 {
108114
ch.note_period = note.period();
115+
ch.note_step = self
116+
.clock_ticks_per_device_sample
117+
.apply_period(ch.note_period);
109118
}
110119
ch.volume = sample.volume();
111-
ch.sample_num = note.sample_no();
120+
ch.sample_data = Some(sample.raw_sample_bytes().as_ptr());
121+
ch.sample_loops = sample.loops();
122+
ch.sample_length = sample.sample_length_bytes();
123+
ch.repeat_length = sample.repeat_length_bytes();
124+
ch.repeat_point = sample.repeat_point_bytes();
112125
ch.sample_position = neotracker::Fractional::default();
113126
}
114127
let _ = write!(
@@ -177,6 +190,9 @@ impl<'a> Player<'a> {
177190
neotracker::shift_period(ch.note_period, half_steps)
178191
{
179192
ch.note_period = new_period;
193+
ch.note_step = self
194+
.clock_ticks_per_device_sample
195+
.apply_period(ch.note_period);
180196
}
181197
} else if self.ticks_left == lower_third {
182198
let first_half_steps = n >> 4;
@@ -186,14 +202,23 @@ impl<'a> Player<'a> {
186202
second_half_steps - first_half_steps,
187203
) {
188204
ch.note_period = new_period;
205+
ch.note_step = self
206+
.clock_ticks_per_device_sample
207+
.apply_period(ch.note_period);
189208
}
190209
}
191210
}
192211
Some(neotracker::Effect::SlideUp(n)) => {
193212
ch.note_period -= u16::from(n);
213+
ch.note_step = self
214+
.clock_ticks_per_device_sample
215+
.apply_period(ch.note_period);
194216
}
195217
Some(neotracker::Effect::SlideDown(n)) => {
196218
ch.note_period += u16::from(n);
219+
ch.note_step = self
220+
.clock_ticks_per_device_sample
221+
.apply_period(ch.note_period);
197222
}
198223
Some(neotracker::Effect::VolumeSlide(n)) => {
199224
let new_volume = (ch.volume as i8) + n;
@@ -215,34 +240,27 @@ impl<'a> Player<'a> {
215240
let mut left_sample = 0;
216241
let mut right_sample = 0;
217242
for (ch_idx, ch) in self.channels.iter_mut().enumerate() {
218-
if ch.sample_num == 0 || ch.note_period == 0 {
243+
if ch.note_period == 0 || ch.sample_length == 0 {
219244
continue;
220245
}
221-
let current_sample = self.modfile.sample(ch.sample_num).expect("bad sample");
222-
let sample_data = current_sample.raw_sample_bytes();
223-
if sample_data.is_empty() {
246+
let Some(sample_data) = ch.sample_data else {
224247
continue;
225-
}
248+
};
226249
let integer_pos = ch.sample_position.as_index();
227-
let sample_byte = sample_data.get(integer_pos).cloned().unwrap_or_default();
250+
let sample_byte = unsafe { sample_data.add(integer_pos).read() } as i8;
228251
let mut channel_value = (sample_byte as i8) as i32;
229252
// max channel vol (64), sample range [-128,127] scaled to [-32768, 32767]
230253
channel_value *= 256;
231254
channel_value *= i32::from(ch.volume);
232255
channel_value /= 64;
233256
// move the sample index by a non-integer amount
234-
ch.sample_position += self
235-
.clock_ticks_per_device_sample
236-
.apply_period(ch.note_period);
257+
ch.sample_position += ch.note_step;
237258
// loop sample if required
238-
if current_sample.loops() {
239-
if ch.sample_position.as_index()
240-
>= (current_sample.repeat_point_bytes() + current_sample.repeat_length_bytes())
241-
{
242-
ch.sample_position =
243-
neotracker::Fractional::new(current_sample.repeat_point_bytes() as u32);
259+
if ch.sample_loops {
260+
if ch.sample_position.as_index() >= (ch.repeat_point + ch.repeat_length) {
261+
ch.sample_position = neotracker::Fractional::new(ch.repeat_point as u32);
244262
}
245-
} else if ch.sample_position.as_index() >= current_sample.sample_length_bytes() {
263+
} else if ch.sample_position.as_index() >= ch.sample_length {
246264
// stop playing sample
247265
ch.note_period = 0;
248266
}

0 commit comments

Comments
 (0)