1
0
Fork 0
mirror of https://github.com/doukutsu-rs/doukutsu-rs synced 2024-11-19 12:12:44 +00:00
doukutsu-rs/src/sound/pixtone.rs

451 lines
13 KiB
Rust
Raw Normal View History

2020-09-16 13:21:30 +00:00
use std::collections::HashMap;
use vec_mut_scan::VecMutScan;
2020-08-18 16:46:07 +00:00
use lazy_static::lazy_static;
lazy_static! {
static ref WAVEFORMS: [[i8; 0x100]; 6] = {
let mut sine = [0i8; 0x100];
let mut triangle = [0i8; 0x100];
let mut saw_up = [0i8; 0x100];
let mut saw_down = [0i8; 0x100];
let mut square = [0i8; 0x100];
let mut random = [0i8; 0x100];
2020-09-16 13:21:30 +00:00
let ref_data = include_bytes!("pixtone_ref.dat");
unsafe {
sine.copy_from_slice(&*(&ref_data[0..0x100] as *const [u8] as *const [i8]));
triangle.copy_from_slice(&*(&ref_data[0x100..0x200] as *const [u8] as *const [i8]));
saw_up.copy_from_slice(&*(&ref_data[0x200..0x300] as *const [u8] as *const [i8]));
saw_down.copy_from_slice(&*(&ref_data[0x300..0x400] as *const [u8] as *const [i8]));
square.copy_from_slice(&*(&ref_data[0x400..0x500] as *const [u8] as *const [i8]));
random.copy_from_slice(&*(&ref_data[0x500..0x600] as *const [u8] as *const [i8]));
}
// todo i can't get this shit right
/*
let mut seed = 0i32;
2020-08-18 16:46:07 +00:00
for i in 0..255 {
2020-09-16 13:21:30 +00:00
seed = seed.wrapping_mul(214013).wrapping_add(2531011);
sine[i] = (64.0 * (i as f64 * std::f64::consts::PI).sin()) as i8;
triangle[i] = (if (0x40i32.wrapping_add(i as i32)) & 0x80 != 0 { 0x80i32.wrapping_sub(i as i32) } else { i as i32 }) as i8;
saw_up[i] = (-0x40i32).wrapping_add(i as i32 / 2) as i8;
saw_down[i] = (0x40i32.wrapping_sub(i as i32 / 2)) as i8;
square[i] = (0x40i32.wrapping_sub(i as i32 & 0x80)) as i8;
2020-08-18 16:46:07 +00:00
random[i] = ((seed >> 16) / 2) as i8;
2020-09-16 13:21:30 +00:00
}*/
2020-08-18 16:46:07 +00:00
[sine, triangle, saw_up, saw_down, square, random]
};
}
2020-09-16 13:21:30 +00:00
#[test]
fn test_waveforms() {
let reference = include_bytes!("pixtone_ref.dat");
2020-08-18 16:46:07 +00:00
2020-09-16 13:21:30 +00:00
for n in 1..(WAVEFORMS.len()) {
for (i, &val) in WAVEFORMS[n].iter().enumerate() {
assert_eq!((val as u8, i, n), (reference[n as usize * 256 + i], i, n));
}
}
}
static PIXTONE_TABLE: [PixToneParameters; 17] = [
PixToneParameters::empty(),
PixToneParameters {
channels: [
Channel {
enabled: true,
length: 3000,
carrier: Waveform {
waveform_type: 0,
pitch: 99.0,
level: 32,
offset: 0,
},
frequency: Waveform {
waveform_type: 2,
pitch: 1.0,
level: 55,
offset: 197,
},
amplitude: Waveform {
waveform_type: 5,
pitch: 0.0,
level: 0,
offset: 0,
},
envelope: Envelope {
initial: 63,
time_a: 0,
value_a: 63,
time_b: 164,
value_b: 28,
time_c: 255,
value_c: 0,
},
}, Channel::disabled(), Channel::disabled(), Channel::disabled()
]
},
PixToneParameters {
channels: [
Channel {
enabled: true,
length: 4000,
carrier: Waveform {
waveform_type: 1,
pitch: 54.0,
level: 32,
offset: 0,
},
frequency: Waveform {
waveform_type: 5,
pitch: 0.1,
level: 33,
offset: 0,
},
amplitude: Waveform {
waveform_type: 0,
pitch: 0.0,
level: 32,
offset: 0,
},
envelope: Envelope {
initial: 53,
time_a: 57,
value_a: 44,
time_b: 128,
value_b: 24,
time_c: 255,
value_c: 0,
},
}, Channel::disabled(), Channel::disabled(), Channel::disabled()
]
},
PixToneParameters::empty(),
PixToneParameters::empty(),
PixToneParameters::empty(),
PixToneParameters::empty(),
PixToneParameters::empty(),
PixToneParameters::empty(),
PixToneParameters::empty(),
PixToneParameters::empty(),
PixToneParameters::empty(),
PixToneParameters::empty(),
PixToneParameters::empty(),
PixToneParameters::empty(),
PixToneParameters {
channels: [Channel {
enabled: true,
length: 1000,
carrier: Waveform {
waveform_type: 5,
pitch: 1.0,
level: 32,
offset: 0,
},
frequency: Waveform {
waveform_type: 3,
pitch: 1.0,
level: 63,
offset: 0,
},
amplitude: Waveform {
waveform_type: 0,
pitch: 0.0,
level: 63,
offset: 0,
},
envelope: Envelope {
initial: 0,
time_a: 28,
value_a: 63,
time_b: 53,
value_b: 31,
time_c: 210,
value_c: 31,
},
}, Channel::disabled(), Channel::disabled(), Channel::disabled()]
},
PixToneParameters {
channels: [Channel {
enabled: true,
length: 5000,
carrier: Waveform {
waveform_type: 2,
pitch: 50.0,
level: 39,
offset: 0,
},
frequency: Waveform {
waveform_type: 3,
pitch: 0.5,
level: 40,
offset: 217,
},
amplitude: Waveform {
waveform_type: 1,
pitch: 0.0,
level: 32,
offset: 0,
},
envelope: Envelope {
initial: 63,
time_a: 64,
value_a: 63,
time_b: 128,
value_b: 34,
time_c: 198,
value_c: 32,
},
}, Channel {
enabled: true,
length: 5000,
carrier: Waveform {
waveform_type: 5,
pitch: 10.0,
level: 39,
offset: 0,
},
frequency: Waveform {
waveform_type: 3,
pitch: 0.5,
level: 24,
offset: 217,
},
amplitude: Waveform {
waveform_type: 1,
pitch: 4.0,
level: 32,
offset: 0,
},
envelope: Envelope {
initial: 0,
time_a: 4,
value_a: 63,
time_b: 128,
value_b: 34,
time_c: 198,
value_c: 32,
},
}, Channel::disabled(), Channel::disabled()]
}
];
pub struct Waveform {
waveform_type: u8,
pitch: f32,
level: i32,
offset: i32,
}
impl Waveform {
pub fn get_waveform(&self) -> &[i8; 0x100] {
&WAVEFORMS[self.waveform_type as usize % WAVEFORMS.len()]
}
}
pub struct Envelope {
initial: i32,
time_a: i32,
value_a: i32,
time_b: i32,
value_b: i32,
time_c: i32,
value_c: i32,
}
impl Envelope {
pub fn evaluate(&self, i: i32) -> i32 {
let (prev_time, prev_val) = {
if i >= self.time_a {
(self.time_a, self.value_a)
} else if i >= self.time_b {
(self.time_b, self.value_b)
} else if i >= self.time_c {
(self.time_c, self.value_c)
} else {
(0, self.initial)
}
};
let (next_time, next_val) = {
if i < self.time_c {
(self.time_c, self.value_c)
} else if i < self.time_b {
(self.time_b, self.value_b)
} else if i < self.time_a {
(self.time_a, self.value_a)
} else {
(256, 0)
}
};
if next_time <= prev_time {
return prev_val;
}
(i - prev_time) * (next_val - prev_val) / (next_time - prev_time) + prev_val
}
}
pub struct Channel {
enabled: bool,
length: u32,
carrier: Waveform,
frequency: Waveform,
amplitude: Waveform,
envelope: Envelope,
}
impl Channel {
pub const fn disabled() -> Channel {
Channel {
enabled: false,
length: 0,
carrier: Waveform {
waveform_type: 0,
pitch: 0.0,
level: 0,
offset: 0,
},
frequency: Waveform {
waveform_type: 0,
pitch: 0.0,
level: 0,
offset: 0,
},
amplitude: Waveform {
waveform_type: 0,
pitch: 0.0,
level: 0,
offset: 0,
},
envelope: Envelope {
initial: 0,
time_a: 0,
value_a: 0,
time_b: 0,
value_b: 0,
time_c: 0,
value_c: 0,
},
}
}
}
pub struct PixToneParameters {
channels: [Channel; 4],
}
impl PixToneParameters {
pub const fn empty() -> PixToneParameters {
PixToneParameters {
channels: [Channel::disabled(), Channel::disabled(), Channel::disabled(), Channel::disabled()]
}
}
2020-08-18 16:46:07 +00:00
2020-09-16 13:21:30 +00:00
pub fn synth(&self) -> Vec<i16> {
let length = self.channels.iter().map(|c| c.length as usize).max().unwrap_or(0);
if length == 0 {
return Vec::new();
}
let mut samples = vec![0; length];
for channel in self.channels.iter() {
if !channel.enabled { continue; }
fn s(p: f32, i: usize, length: u32) -> f32 {
256.0 * p * i as f32 / length as f32
}
let mut phase = channel.carrier.offset as f32;
let delta = 256.0 * channel.carrier.pitch as f32 / channel.length as f32;
let carrier_wave = channel.carrier.get_waveform();
let frequency_wave = channel.frequency.get_waveform();
let amplitude_wave = channel.amplitude.get_waveform();
for (i, result) in samples.iter_mut().enumerate() {
let carrier = carrier_wave[0xff & phase as usize] as i32 * channel.carrier.level;
let freq = frequency_wave[0xff & (channel.frequency.offset as f32 + s(channel.frequency.pitch, i, channel.length)) as usize] as i32 * channel.frequency.level;
let amp = amplitude_wave[0xff & (channel.amplitude.offset as f32 + s(channel.amplitude.pitch, i, channel.length)) as usize] as i32 * channel.amplitude.level;
*result += ((carrier * (amp + 4096) / 4096 * channel.envelope.evaluate(s(1.0, i, channel.length) as i32) / 4096) * 256) as i16;
phase += delta * (1.0 + (freq as f32 / (if freq < 0 { 8192.0 } else { 2048.0 })));
}
}
samples
}
}
#[derive(Copy, Clone, PartialEq)]
pub struct PlaybackState(u8, f32);
pub struct PixTonePlayback {
pub samples: HashMap<u8, Vec<i16>>,
pub playback_state: Vec<PlaybackState>,
}
impl PixTonePlayback {
pub fn new() -> PixTonePlayback {
PixTonePlayback {
samples: HashMap::new(),
playback_state: vec![],
}
}
pub fn create_samples(&mut self) {
for (i, params) in PIXTONE_TABLE.iter().enumerate() {
self.samples.insert(i as u8, params.synth());
}
}
pub fn play_sfx(&mut self, id: u8) {
for state in self.playback_state.iter_mut() {
if state.0 == id {
state.1 = 0.0;
return;
}
}
self.playback_state.push(PlaybackState(id, 0.0));
}
pub fn mix(&mut self, dst: &mut [u16], sample_rate: f32) {
let mut scan = VecMutScan::new(&mut self.playback_state);
let delta = 22050.0 / sample_rate;
while let Some(item) = scan.next() {
let mut state = *item;
let mut remove = false;
if let Some(sample) = self.samples.get(&state.0) {
if sample.is_empty() {
item.remove();
continue;
};
for result in dst.iter_mut() {
if state.1 >= sample.len() as f32 {
remove = true;
break;
} else {
*result = ((*result as u16 ^ 0x8000u16) as i16).saturating_add(sample[state.1 as usize]) as u16 ^ 0x8000u16;
state.1 += delta;
}
}
if remove {
item.remove();
} else {
item.replace(state);
}
}
}
}
}