mirror of
https://github.com/italicsjenga/agb.git
synced 2025-02-02 20:46:40 +11:00
Merge pull request #67 from gwilymk/more-exciting-mixer
More exciting mixer
This commit is contained in:
commit
53cf1a5064
5 changed files with 300 additions and 195 deletions
|
@ -86,6 +86,7 @@ fixed_width_unsigned_integer_impl!(i16);
|
||||||
fixed_width_unsigned_integer_impl!(u16);
|
fixed_width_unsigned_integer_impl!(u16);
|
||||||
fixed_width_unsigned_integer_impl!(i32);
|
fixed_width_unsigned_integer_impl!(i32);
|
||||||
fixed_width_unsigned_integer_impl!(u32);
|
fixed_width_unsigned_integer_impl!(u32);
|
||||||
|
fixed_width_unsigned_integer_impl!(usize);
|
||||||
|
|
||||||
fixed_width_signed_integer_impl!(i16);
|
fixed_width_signed_integer_impl!(i16);
|
||||||
fixed_width_signed_integer_impl!(i32);
|
fixed_width_signed_integer_impl!(i32);
|
||||||
|
@ -102,6 +103,15 @@ impl<I: FixedWidthUnsignedInteger, const N: usize> From<I> for Num<I, N> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<I, const N: usize> Default for Num<I, N>
|
||||||
|
where
|
||||||
|
I: FixedWidthUnsignedInteger,
|
||||||
|
{
|
||||||
|
fn default() -> Self {
|
||||||
|
Num(I::zero())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<I, T, const N: usize> Add<T> for Num<I, N>
|
impl<I, T, const N: usize> Add<T> for Num<I, N>
|
||||||
where
|
where
|
||||||
I: FixedWidthUnsignedInteger,
|
I: FixedWidthUnsignedInteger,
|
||||||
|
|
|
@ -1,195 +0,0 @@
|
||||||
use crate::memory_mapped::MemoryMapped;
|
|
||||||
|
|
||||||
#[non_exhaustive]
|
|
||||||
pub struct MixerController {}
|
|
||||||
|
|
||||||
impl MixerController {
|
|
||||||
pub(crate) const fn new() -> Self {
|
|
||||||
MixerController {}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn mixer(&mut self) -> Mixer {
|
|
||||||
Mixer::new()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Mixer {
|
|
||||||
buffer: MixerBuffer,
|
|
||||||
channels: [Option<SoundChannel>; 16],
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Mixer {
|
|
||||||
fn new() -> Self {
|
|
||||||
Mixer {
|
|
||||||
buffer: MixerBuffer::new(),
|
|
||||||
channels: Default::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn enable(&self) {
|
|
||||||
set_timer_counter_for_frequency_and_enable(SOUND_FREQUENCY);
|
|
||||||
set_sound_control_register_for_mixer();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn vblank(&mut self) {
|
|
||||||
self.buffer.swap();
|
|
||||||
self.buffer.clear();
|
|
||||||
|
|
||||||
for channel in self.channels.iter_mut() {
|
|
||||||
let mut has_finished = false;
|
|
||||||
|
|
||||||
if let Some(some_channel) = channel {
|
|
||||||
self.buffer.write_channel(some_channel);
|
|
||||||
some_channel.pos += SOUND_BUFFER_SIZE;
|
|
||||||
|
|
||||||
if some_channel.pos >= some_channel.data.len() {
|
|
||||||
if some_channel.should_loop {
|
|
||||||
some_channel.pos = 0;
|
|
||||||
} else {
|
|
||||||
has_finished = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if has_finished {
|
|
||||||
channel.take();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn play_sound(&mut self, new_channel: SoundChannel) {
|
|
||||||
for channel in self.channels.iter_mut() {
|
|
||||||
if channel.is_some() {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
channel.replace(new_channel);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
panic!("Cannot play more than 16 sounds at once");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct SoundChannel {
|
|
||||||
data: &'static [u8],
|
|
||||||
pos: usize,
|
|
||||||
should_loop: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SoundChannel {
|
|
||||||
pub fn new(data: &'static [u8]) -> Self {
|
|
||||||
SoundChannel {
|
|
||||||
data,
|
|
||||||
pos: 0,
|
|
||||||
should_loop: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn should_loop(mut self) -> Self {
|
|
||||||
self.should_loop = true;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// I've picked one frequency that works nicely. But there are others that work nicely
|
|
||||||
// which we may want to consider in the future: https://web.archive.org/web/20070608011909/http://deku.gbadev.org/program/sound1.html
|
|
||||||
const SOUND_FREQUENCY: i32 = 10512;
|
|
||||||
const SOUND_BUFFER_SIZE: usize = 176;
|
|
||||||
|
|
||||||
struct MixerBuffer {
|
|
||||||
buffer1: [i8; SOUND_BUFFER_SIZE],
|
|
||||||
buffer2: [i8; SOUND_BUFFER_SIZE],
|
|
||||||
|
|
||||||
buffer_1_active: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl MixerBuffer {
|
|
||||||
fn new() -> Self {
|
|
||||||
MixerBuffer {
|
|
||||||
buffer1: [0; SOUND_BUFFER_SIZE],
|
|
||||||
buffer2: [0; SOUND_BUFFER_SIZE],
|
|
||||||
|
|
||||||
buffer_1_active: true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn swap(&mut self) {
|
|
||||||
self.buffer_1_active = !self.buffer_1_active;
|
|
||||||
|
|
||||||
if self.buffer_1_active {
|
|
||||||
enable_dma1_for_sound(&self.buffer1);
|
|
||||||
} else {
|
|
||||||
enable_dma1_for_sound(&self.buffer2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn clear(&mut self) {
|
|
||||||
self.get_write_buffer().fill(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_channel(&mut self, channel: &SoundChannel) {
|
|
||||||
let data_to_copy = &channel.data[channel.pos..];
|
|
||||||
let place_to_write_to = self.get_write_buffer();
|
|
||||||
|
|
||||||
for (i, v) in data_to_copy.iter().take(SOUND_BUFFER_SIZE).enumerate() {
|
|
||||||
let v = *v as i8;
|
|
||||||
place_to_write_to[i] = place_to_write_to[i].saturating_add(v);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_write_buffer(&mut self) -> &mut [i8; SOUND_BUFFER_SIZE] {
|
|
||||||
if self.buffer_1_active {
|
|
||||||
&mut self.buffer2
|
|
||||||
} else {
|
|
||||||
&mut self.buffer1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Once we have proper DMA support, we should use that rather than hard coding these here too
|
|
||||||
const DMA1_SOURCE_ADDR: MemoryMapped<u32> = unsafe { MemoryMapped::new(0x0400_00bc) };
|
|
||||||
const DMA1_DEST_ADDR: MemoryMapped<u32> = unsafe { MemoryMapped::new(0x0400_00c0) };
|
|
||||||
const _DMA1_WORD_COUNT: MemoryMapped<u16> = unsafe { MemoryMapped::new(0x0400_00c4) }; // sound ignores this for some reason
|
|
||||||
const DMA1_CONTROL: MemoryMapped<u16> = unsafe { MemoryMapped::new(0x0400_00c6) };
|
|
||||||
|
|
||||||
const FIFOA_DEST_ADDR: u32 = 0x0400_00a0;
|
|
||||||
|
|
||||||
// Similarly for proper timer support
|
|
||||||
const TIMER0_COUNTER: MemoryMapped<u16> = unsafe { MemoryMapped::new(0x0400_0100) };
|
|
||||||
const TIMER0_CONTROL: MemoryMapped<u16> = unsafe { MemoryMapped::new(0x0400_0102) };
|
|
||||||
|
|
||||||
const SOUND_CONTROL: MemoryMapped<u16> = unsafe { MemoryMapped::new(0x0400_0082) };
|
|
||||||
const SOUND_CONTROL_X: MemoryMapped<u16> = unsafe { MemoryMapped::new(0x0400_0084) };
|
|
||||||
|
|
||||||
fn enable_dma1_for_sound(sound_memory: &[i8]) {
|
|
||||||
let dest_fixed: u16 = 2 << 5; // dest addr control = fixed
|
|
||||||
let repeat: u16 = 1 << 9;
|
|
||||||
let transfer_type: u16 = 1 << 10; // transfer in words
|
|
||||||
let dma_start_timing: u16 = 3 << 12; // sound fifo timing
|
|
||||||
let enable: u16 = 1 << 15; // enable
|
|
||||||
|
|
||||||
DMA1_CONTROL.set(0);
|
|
||||||
DMA1_SOURCE_ADDR.set(sound_memory.as_ptr() as u32);
|
|
||||||
DMA1_DEST_ADDR.set(FIFOA_DEST_ADDR);
|
|
||||||
DMA1_CONTROL.set(dest_fixed | repeat | transfer_type | dma_start_timing | enable);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_sound_control_register_for_mixer() {
|
|
||||||
let sound_a_volume_100: u16 = 1 << 2;
|
|
||||||
let sound_a_rout: u16 = 1 << 8;
|
|
||||||
let sound_a_lout: u16 = 1 << 9;
|
|
||||||
let sound_a_fifo_reset: u16 = 1 << 11;
|
|
||||||
|
|
||||||
SOUND_CONTROL.set(sound_a_volume_100 | sound_a_rout | sound_a_lout | sound_a_fifo_reset);
|
|
||||||
|
|
||||||
// master sound enable
|
|
||||||
SOUND_CONTROL_X.set(1 << 7);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_timer_counter_for_frequency_and_enable(frequency: i32) {
|
|
||||||
let counter = 65536 - (16777216 / frequency);
|
|
||||||
TIMER0_COUNTER.set(counter as u16);
|
|
||||||
|
|
||||||
TIMER0_CONTROL.set(1 << 7); // enable the timer
|
|
||||||
}
|
|
102
agb/src/sound/mixer/hw.rs
Normal file
102
agb/src/sound/mixer/hw.rs
Normal file
|
@ -0,0 +1,102 @@
|
||||||
|
use crate::memory_mapped::MemoryMapped;
|
||||||
|
|
||||||
|
const fn dma_source_addr(dma: usize) -> usize {
|
||||||
|
0x0400_00b0 + 0x0c * dma
|
||||||
|
}
|
||||||
|
|
||||||
|
const fn dma_dest_addr(dma: usize) -> usize {
|
||||||
|
0x0400_00b4 + 0x0c * dma
|
||||||
|
}
|
||||||
|
|
||||||
|
const fn dma_control_addr(dma: usize) -> usize {
|
||||||
|
0x0400_00ba + 0x0c * dma
|
||||||
|
}
|
||||||
|
|
||||||
|
// Once we have proper DMA support, we should use that rather than hard coding these here too
|
||||||
|
const DMA1_SOURCE_ADDR: MemoryMapped<u32> = unsafe { MemoryMapped::new(dma_source_addr(1)) };
|
||||||
|
const DMA1_DEST_ADDR: MemoryMapped<u32> = unsafe { MemoryMapped::new(dma_dest_addr(1)) };
|
||||||
|
const DMA1_CONTROL: MemoryMapped<u16> = unsafe { MemoryMapped::new(dma_control_addr(1)) };
|
||||||
|
|
||||||
|
const DMA2_SOURCE_ADDR: MemoryMapped<u32> = unsafe { MemoryMapped::new(dma_source_addr(2)) };
|
||||||
|
const DMA2_DEST_ADDR: MemoryMapped<u32> = unsafe { MemoryMapped::new(dma_dest_addr(2)) };
|
||||||
|
const DMA2_CONTROL: MemoryMapped<u16> = unsafe { MemoryMapped::new(dma_control_addr(2)) };
|
||||||
|
|
||||||
|
const FIFOA_DEST_ADDR: u32 = 0x0400_00a0;
|
||||||
|
const FIFOB_DEST_ADDR: u32 = 0x0400_00a4;
|
||||||
|
|
||||||
|
// Similarly for proper timer support
|
||||||
|
const TIMER0_COUNTER: MemoryMapped<u16> = unsafe { MemoryMapped::new(0x0400_0100) };
|
||||||
|
const TIMER0_CONTROL: MemoryMapped<u16> = unsafe { MemoryMapped::new(0x0400_0102) };
|
||||||
|
|
||||||
|
const SOUND_CONTROL: MemoryMapped<u16> = unsafe { MemoryMapped::new(0x0400_0082) };
|
||||||
|
const SOUND_CONTROL_X: MemoryMapped<u16> = unsafe { MemoryMapped::new(0x0400_0084) };
|
||||||
|
|
||||||
|
const DMA_CONTROL_SETTING_FOR_SOUND: u16 = {
|
||||||
|
let dest_fixed: u16 = 2 << 5; // dest addr control = fixed
|
||||||
|
let repeat: u16 = 1 << 9;
|
||||||
|
let transfer_type: u16 = 1 << 10; // transfer in words
|
||||||
|
let dma_start_timing: u16 = 3 << 12; // sound fifo timing
|
||||||
|
let enable: u16 = 1 << 15; // enable
|
||||||
|
|
||||||
|
dest_fixed | repeat | transfer_type | dma_start_timing | enable
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub(super) enum LeftOrRight {
|
||||||
|
Left,
|
||||||
|
Right,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn enable_dma_for_sound(sound_memory: &[i8], lr: LeftOrRight) {
|
||||||
|
match lr {
|
||||||
|
LeftOrRight::Left => enable_dma1_for_sound(sound_memory),
|
||||||
|
LeftOrRight::Right => enable_dma2_for_sound(sound_memory),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn enable_dma1_for_sound(sound_memory: &[i8]) {
|
||||||
|
DMA1_CONTROL.set(0);
|
||||||
|
DMA1_SOURCE_ADDR.set(sound_memory.as_ptr() as u32);
|
||||||
|
DMA1_DEST_ADDR.set(FIFOA_DEST_ADDR);
|
||||||
|
DMA1_CONTROL.set(DMA_CONTROL_SETTING_FOR_SOUND);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn enable_dma2_for_sound(sound_memory: &[i8]) {
|
||||||
|
DMA2_CONTROL.set(0);
|
||||||
|
DMA2_SOURCE_ADDR.set(sound_memory.as_ptr() as u32);
|
||||||
|
DMA2_DEST_ADDR.set(FIFOB_DEST_ADDR);
|
||||||
|
DMA2_CONTROL.set(DMA_CONTROL_SETTING_FOR_SOUND);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn set_sound_control_register_for_mixer() {
|
||||||
|
let sound_a_volume_100: u16 = 1 << 2;
|
||||||
|
let sound_a_rout: u16 = 0 << 8; // sound A is for left channel only
|
||||||
|
let sound_a_lout: u16 = 1 << 9;
|
||||||
|
let sound_a_fifo_reset: u16 = 1 << 11;
|
||||||
|
|
||||||
|
let sound_b_volume_100: u16 = 1 << 3;
|
||||||
|
let sound_b_rout: u16 = 1 << 12;
|
||||||
|
let sound_b_lout: u16 = 0 << 13;
|
||||||
|
let sound_b_fifo_reset: u16 = 1 << 15;
|
||||||
|
|
||||||
|
SOUND_CONTROL.set(
|
||||||
|
sound_a_volume_100
|
||||||
|
| sound_a_rout
|
||||||
|
| sound_a_lout
|
||||||
|
| sound_a_fifo_reset
|
||||||
|
| sound_b_volume_100
|
||||||
|
| sound_b_rout
|
||||||
|
| sound_b_lout
|
||||||
|
| sound_b_fifo_reset,
|
||||||
|
);
|
||||||
|
|
||||||
|
// master sound enable
|
||||||
|
SOUND_CONTROL_X.set(1 << 7);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn set_timer_counter_for_frequency_and_enable(frequency: i32) {
|
||||||
|
let counter = 65536 - (16777216 / frequency);
|
||||||
|
TIMER0_COUNTER.set(counter as u16);
|
||||||
|
|
||||||
|
TIMER0_CONTROL.set(1 << 7); // enable the timer
|
||||||
|
}
|
61
agb/src/sound/mixer/mod.rs
Normal file
61
agb/src/sound/mixer/mod.rs
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
mod hw;
|
||||||
|
mod sw_mixer;
|
||||||
|
|
||||||
|
pub use sw_mixer::Mixer;
|
||||||
|
|
||||||
|
use crate::number::Num;
|
||||||
|
|
||||||
|
#[non_exhaustive]
|
||||||
|
pub struct MixerController {}
|
||||||
|
|
||||||
|
impl MixerController {
|
||||||
|
pub(crate) const fn new() -> Self {
|
||||||
|
MixerController {}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mixer(&mut self) -> Mixer {
|
||||||
|
Mixer::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct SoundChannel {
|
||||||
|
data: &'static [u8],
|
||||||
|
pos: Num<usize, 8>,
|
||||||
|
should_loop: bool,
|
||||||
|
|
||||||
|
playback_speed: Num<usize, 8>,
|
||||||
|
|
||||||
|
panning: Num<i16, 4>, // between -1 and 1
|
||||||
|
is_done: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SoundChannel {
|
||||||
|
pub fn new(data: &'static [u8]) -> Self {
|
||||||
|
SoundChannel {
|
||||||
|
data,
|
||||||
|
pos: 0.into(),
|
||||||
|
should_loop: false,
|
||||||
|
playback_speed: 1.into(),
|
||||||
|
panning: 0.into(),
|
||||||
|
is_done: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn should_loop(mut self) -> Self {
|
||||||
|
self.should_loop = true;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn playback(mut self, playback_speed: Num<usize, 8>) -> Self {
|
||||||
|
self.playback_speed = playback_speed;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn panning(mut self, panning: Num<i16, 4>) -> Self {
|
||||||
|
debug_assert!(panning >= Num::new(-1), "panning value must be >= -1");
|
||||||
|
debug_assert!(panning <= Num::new(1), "panning value must be <= 1");
|
||||||
|
|
||||||
|
self.panning = panning;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
127
agb/src/sound/mixer/sw_mixer.rs
Normal file
127
agb/src/sound/mixer/sw_mixer.rs
Normal file
|
@ -0,0 +1,127 @@
|
||||||
|
use super::hw;
|
||||||
|
use super::hw::LeftOrRight;
|
||||||
|
use super::SoundChannel;
|
||||||
|
use crate::number::Num;
|
||||||
|
|
||||||
|
pub struct Mixer {
|
||||||
|
buffer: MixerBuffer,
|
||||||
|
channels: [Option<SoundChannel>; 16],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Mixer {
|
||||||
|
pub(super) fn new() -> Self {
|
||||||
|
Mixer {
|
||||||
|
buffer: MixerBuffer::new(),
|
||||||
|
channels: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn enable(&self) {
|
||||||
|
hw::set_timer_counter_for_frequency_and_enable(SOUND_FREQUENCY);
|
||||||
|
hw::set_sound_control_register_for_mixer();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn vblank(&mut self) {
|
||||||
|
self.buffer.swap();
|
||||||
|
self.buffer.clear();
|
||||||
|
|
||||||
|
self.buffer
|
||||||
|
.write_channels(self.channels.iter_mut().flatten());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn play_sound(&mut self, new_channel: SoundChannel) {
|
||||||
|
for channel in self.channels.iter_mut() {
|
||||||
|
if let Some(some_channel) = channel {
|
||||||
|
if !some_channel.is_done {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
channel.replace(new_channel);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
panic!("Cannot play more than 16 sounds at once");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// I've picked one frequency that works nicely. But there are others that work nicely
|
||||||
|
// which we may want to consider in the future: http://deku.gbadev.org/program/sound1.html
|
||||||
|
const SOUND_FREQUENCY: i32 = 10512;
|
||||||
|
const SOUND_BUFFER_SIZE: usize = 176;
|
||||||
|
|
||||||
|
struct MixerBuffer {
|
||||||
|
buffer1: [i8; SOUND_BUFFER_SIZE * 2], // first half is left, second is right
|
||||||
|
buffer2: [i8; SOUND_BUFFER_SIZE * 2],
|
||||||
|
|
||||||
|
buffer_1_active: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MixerBuffer {
|
||||||
|
fn new() -> Self {
|
||||||
|
MixerBuffer {
|
||||||
|
buffer1: [0; SOUND_BUFFER_SIZE * 2],
|
||||||
|
buffer2: [0; SOUND_BUFFER_SIZE * 2],
|
||||||
|
|
||||||
|
buffer_1_active: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn swap(&mut self) {
|
||||||
|
let (left_buffer, right_buffer) = self.get_write_buffer().split_at(SOUND_BUFFER_SIZE);
|
||||||
|
|
||||||
|
hw::enable_dma_for_sound(left_buffer, LeftOrRight::Left);
|
||||||
|
hw::enable_dma_for_sound(right_buffer, LeftOrRight::Right);
|
||||||
|
|
||||||
|
self.buffer_1_active = !self.buffer_1_active;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clear(&mut self) {
|
||||||
|
self.get_write_buffer().fill(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_channels<'a>(&mut self, channels: impl Iterator<Item = &'a mut SoundChannel>) {
|
||||||
|
let mut buffer: [Num<i16, 4>; SOUND_BUFFER_SIZE * 2] = [Num::new(0); SOUND_BUFFER_SIZE * 2];
|
||||||
|
|
||||||
|
for channel in channels {
|
||||||
|
if channel.is_done {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let right_amount = (channel.panning + 1) / 2;
|
||||||
|
let left_amount = -right_amount + 1;
|
||||||
|
|
||||||
|
if channel.pos + channel.playback_speed * SOUND_BUFFER_SIZE >= channel.data.len().into()
|
||||||
|
{
|
||||||
|
// TODO: This should probably play what's left rather than skip the last bit
|
||||||
|
if channel.should_loop {
|
||||||
|
channel.pos -= channel.data.len();
|
||||||
|
} else {
|
||||||
|
channel.is_done = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for i in 0..SOUND_BUFFER_SIZE {
|
||||||
|
let v = (channel.data[channel.pos.floor()] as i8) as i16;
|
||||||
|
channel.pos += channel.playback_speed;
|
||||||
|
|
||||||
|
buffer[i] += left_amount * v;
|
||||||
|
buffer[i + SOUND_BUFFER_SIZE] += right_amount * v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let write_buffer = self.get_write_buffer();
|
||||||
|
for i in 0..SOUND_BUFFER_SIZE * 2 {
|
||||||
|
write_buffer[i] = buffer[i].floor().clamp(i8::MIN as i16, i8::MAX as i16) as i8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_write_buffer(&mut self) -> &mut [i8; SOUND_BUFFER_SIZE * 2] {
|
||||||
|
if self.buffer_1_active {
|
||||||
|
&mut self.buffer2
|
||||||
|
} else {
|
||||||
|
&mut self.buffer1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue