mirror of
https://github.com/italicsjenga/agb.git
synced 2025-01-04 06:01:35 +11:00
Make the tracker generic on the mixer
This commit is contained in:
parent
d00de7b2a4
commit
3305ca0ff2
|
@ -65,13 +65,14 @@
|
||||||
|
|
||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
|
|
||||||
|
mod mixer;
|
||||||
|
|
||||||
use agb_tracker_interop::{PatternEffect, Sample};
|
use agb_tracker_interop::{PatternEffect, Sample};
|
||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
|
|
||||||
use agb::{
|
pub use mixer::{Mixer, SoundChannel};
|
||||||
fixnum::Num,
|
|
||||||
sound::mixer::{ChannelId, Mixer, SoundChannel},
|
use agb::fixnum::Num;
|
||||||
};
|
|
||||||
|
|
||||||
/// Import an XM file. Only available if you have the `xm` feature enabled (enabled by default).
|
/// Import an XM file. Only available if you have the `xm` feature enabled (enabled by default).
|
||||||
#[cfg(feature = "xm")]
|
#[cfg(feature = "xm")]
|
||||||
|
@ -94,9 +95,9 @@ pub mod __private {
|
||||||
pub use agb_tracker_interop::Track;
|
pub use agb_tracker_interop::Track;
|
||||||
|
|
||||||
/// Stores the required state in order to play tracker music.
|
/// Stores the required state in order to play tracker music.
|
||||||
pub struct Tracker {
|
pub struct Tracker<M: Mixer> {
|
||||||
track: &'static Track,
|
track: &'static Track,
|
||||||
channels: Vec<TrackerChannel>,
|
channels: Vec<TrackerChannel<M>>,
|
||||||
envelopes: Vec<Option<EnvelopeState>>,
|
envelopes: Vec<Option<EnvelopeState>>,
|
||||||
|
|
||||||
frame: Num<u32, 8>,
|
frame: Num<u32, 8>,
|
||||||
|
@ -109,9 +110,8 @@ pub struct Tracker {
|
||||||
current_pattern: usize,
|
current_pattern: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
struct TrackerChannel<M: Mixer> {
|
||||||
struct TrackerChannel {
|
channel_id: Option<M::ChannelId>,
|
||||||
channel_id: Option<ChannelId>,
|
|
||||||
original_speed: Num<u32, 16>,
|
original_speed: Num<u32, 16>,
|
||||||
base_speed: Num<u32, 16>,
|
base_speed: Num<u32, 16>,
|
||||||
volume: Num<i32, 8>,
|
volume: Num<i32, 8>,
|
||||||
|
@ -132,7 +132,7 @@ struct GlobalSettings {
|
||||||
volume: Num<i32, 8>,
|
volume: Num<i32, 8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Tracker {
|
impl<M: Mixer> Tracker<M> {
|
||||||
/// Create a new tracker playing a specified track. See the [example](crate#example) for how to use the tracker.
|
/// Create a new tracker playing a specified track. See the [example](crate#example) for how to use the tracker.
|
||||||
pub fn new(track: &'static Track) -> Self {
|
pub fn new(track: &'static Track) -> Self {
|
||||||
let mut channels = Vec::new();
|
let mut channels = Vec::new();
|
||||||
|
@ -165,7 +165,7 @@ impl Tracker {
|
||||||
|
|
||||||
/// Call this once per frame before calling [`mixer.frame`](agb::sound::mixer::Mixer::frame()).
|
/// Call this once per frame before calling [`mixer.frame`](agb::sound::mixer::Mixer::frame()).
|
||||||
/// See the [example](crate#example) for how to use the tracker.
|
/// See the [example](crate#example) for how to use the tracker.
|
||||||
pub fn step(&mut self, mixer: &mut Mixer) {
|
pub fn step(&mut self, mixer: &mut M) {
|
||||||
if !self.increment_frame() {
|
if !self.increment_frame() {
|
||||||
self.update_envelopes(mixer);
|
self.update_envelopes(mixer);
|
||||||
return;
|
return;
|
||||||
|
@ -215,7 +215,7 @@ impl Tracker {
|
||||||
self.update_envelopes(mixer);
|
self.update_envelopes(mixer);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_envelopes(&mut self, mixer: &mut Mixer) {
|
fn update_envelopes(&mut self, mixer: &mut M) {
|
||||||
for (channel, envelope_state_option) in self.channels.iter_mut().zip(&mut self.envelopes) {
|
for (channel, envelope_state_option) in self.channels.iter_mut().zip(&mut self.envelopes) {
|
||||||
if let Some(envelope_state) = envelope_state_option {
|
if let Some(envelope_state) = envelope_state_option {
|
||||||
let envelope = &self.track.envelopes[envelope_state.envelope_id];
|
let envelope = &self.track.envelopes[envelope_state.envelope_id];
|
||||||
|
@ -288,13 +288,8 @@ impl Tracker {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TrackerChannel {
|
impl<M: Mixer> TrackerChannel<M> {
|
||||||
fn play_sound(
|
fn play_sound(&mut self, mixer: &mut M, sample: &Sample, global_settings: &GlobalSettings) {
|
||||||
&mut self,
|
|
||||||
mixer: &mut Mixer<'_>,
|
|
||||||
sample: &Sample,
|
|
||||||
global_settings: &GlobalSettings,
|
|
||||||
) {
|
|
||||||
if let Some(channel) = self
|
if let Some(channel) = self
|
||||||
.channel_id
|
.channel_id
|
||||||
.take()
|
.take()
|
||||||
|
@ -303,7 +298,7 @@ impl TrackerChannel {
|
||||||
channel.stop();
|
channel.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut new_channel = SoundChannel::new(match sample.data {
|
let mut new_channel = M::SoundChannel::new(match sample.data {
|
||||||
alloc::borrow::Cow::Borrowed(data) => data,
|
alloc::borrow::Cow::Borrowed(data) => data,
|
||||||
alloc::borrow::Cow::Owned(_) => {
|
alloc::borrow::Cow::Owned(_) => {
|
||||||
unimplemented!("Must use borrowed COW data for tracker")
|
unimplemented!("Must use borrowed COW data for tracker")
|
||||||
|
@ -326,7 +321,7 @@ impl TrackerChannel {
|
||||||
self.volume = sample.volume.change_base();
|
self.volume = sample.volume.change_base();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_speed(&mut self, mixer: &mut Mixer<'_>, speed: Num<u32, 8>) {
|
fn set_speed(&mut self, mixer: &mut M, speed: Num<u32, 8>) {
|
||||||
if let Some(channel) = self
|
if let Some(channel) = self
|
||||||
.channel_id
|
.channel_id
|
||||||
.as_ref()
|
.as_ref()
|
||||||
|
@ -343,7 +338,7 @@ impl TrackerChannel {
|
||||||
|
|
||||||
fn apply_effect(
|
fn apply_effect(
|
||||||
&mut self,
|
&mut self,
|
||||||
mixer: &mut Mixer<'_>,
|
mixer: &mut M,
|
||||||
effect: &PatternEffect,
|
effect: &PatternEffect,
|
||||||
tick: u32,
|
tick: u32,
|
||||||
global_settings: &mut GlobalSettings,
|
global_settings: &mut GlobalSettings,
|
||||||
|
@ -485,7 +480,7 @@ impl TrackerChannel {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
fn update_volume_envelope(
|
fn update_volume_envelope(
|
||||||
&mut self,
|
&mut self,
|
||||||
mixer: &mut Mixer<'_>,
|
mixer: &mut M,
|
||||||
envelope_state: &EnvelopeState,
|
envelope_state: &EnvelopeState,
|
||||||
envelope: &agb_tracker_interop::Envelope,
|
envelope: &agb_tracker_interop::Envelope,
|
||||||
global_settings: &GlobalSettings,
|
global_settings: &GlobalSettings,
|
||||||
|
@ -514,8 +509,70 @@ impl TrackerChannel {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<M: Mixer> Default for TrackerChannel<M> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
channel_id: None,
|
||||||
|
original_speed: Num::default(),
|
||||||
|
base_speed: Num::default(),
|
||||||
|
volume: Num::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[agb::entry]
|
#[agb::entry]
|
||||||
fn main(gba: agb::Gba) -> ! {
|
fn main(gba: agb::Gba) -> ! {
|
||||||
loop {}
|
loop {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl SoundChannel for agb::sound::mixer::SoundChannel {
|
||||||
|
fn new(data: &'static [u8]) -> Self {
|
||||||
|
Self::new(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn stop(&mut self) {
|
||||||
|
self.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pause(&mut self) -> &mut Self {
|
||||||
|
self.pause()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resume(&mut self) -> &mut Self {
|
||||||
|
self.resume()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn should_loop(&mut self) -> &mut Self {
|
||||||
|
self.should_loop()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn volume(&mut self, value: impl Into<Num<i16, 8>>) -> &mut Self {
|
||||||
|
self.volume(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn restart_point(&mut self, value: impl Into<Num<u32, 8>>) -> &mut Self {
|
||||||
|
self.restart_point(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn playback(&mut self, playback_speed: impl Into<Num<u32, 8>>) -> &mut Self {
|
||||||
|
self.playback(playback_speed)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn panning(&mut self, panning: impl Into<Num<i16, 8>>) -> &mut Self {
|
||||||
|
self.panning(panning)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'gba> Mixer for agb::sound::mixer::Mixer<'gba> {
|
||||||
|
type ChannelId = agb::sound::mixer::ChannelId;
|
||||||
|
type SoundChannel = agb::sound::mixer::SoundChannel;
|
||||||
|
|
||||||
|
fn channel(&mut self, channel_id: &Self::ChannelId) -> Option<&mut Self::SoundChannel> {
|
||||||
|
self.channel(channel_id)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn play_sound(&mut self, channel: Self::SoundChannel) -> Option<Self::ChannelId> {
|
||||||
|
self.play_sound(channel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
25
tracker/agb-tracker/src/mixer.rs
Normal file
25
tracker/agb-tracker/src/mixer.rs
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
#![allow(missing_docs)]
|
||||||
|
|
||||||
|
use agb::fixnum::Num;
|
||||||
|
|
||||||
|
pub trait SoundChannel {
|
||||||
|
fn new(data: &'static [u8]) -> Self;
|
||||||
|
|
||||||
|
fn stop(&mut self);
|
||||||
|
fn pause(&mut self) -> &mut Self;
|
||||||
|
fn resume(&mut self) -> &mut Self;
|
||||||
|
|
||||||
|
fn should_loop(&mut self) -> &mut Self;
|
||||||
|
fn volume(&mut self, value: impl Into<Num<i16, 8>>) -> &mut Self;
|
||||||
|
fn restart_point(&mut self, value: impl Into<Num<u32, 8>>) -> &mut Self;
|
||||||
|
fn playback(&mut self, playback_speed: impl Into<Num<u32, 8>>) -> &mut Self;
|
||||||
|
fn panning(&mut self, panning: impl Into<Num<i16, 8>>) -> &mut Self;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Mixer {
|
||||||
|
type ChannelId;
|
||||||
|
type SoundChannel: SoundChannel;
|
||||||
|
|
||||||
|
fn channel(&mut self, channel_id: &Self::ChannelId) -> Option<&mut Self::SoundChannel>;
|
||||||
|
fn play_sound(&mut self, channel: Self::SoundChannel) -> Option<Self::ChannelId>;
|
||||||
|
}
|
Loading…
Reference in a new issue