use crate::{
ReadResult, SampleRate, SamplesPerTick, Timing, UnitIdx,
delay::Delay,
event::EveList,
master::Master,
noise_builder::NoiseTable,
overdrive::Overdrive,
result::WriteResult,
timing::SampleT,
unit::{Unit, VoiceIdx},
voice::Voice,
};
mod io;
use arrayvec::ArrayVec;
pub use io::Tag;
pub mod moo;
const MAX_UNITS: u16 = 50;
const MAX_TUNE_VOICE_NAME: u32 = 16;
pub const MAX_TUNE_UNIT_NAME: usize = 16;
#[derive(Default)]
pub struct Text {
pub name: String,
pub comment: String,
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
pub enum FmtVer {
V1,
V2,
V3,
V4,
V5,
}
#[derive(Clone, Copy, Debug)]
pub enum FmtKind {
Collage,
Tune,
}
#[derive(Clone, Copy, Debug)]
pub struct FmtInfo {
pub ver: FmtVer,
pub kind: FmtKind,
pub(crate) exe_ver: u16,
pub(crate) dummy: u16,
}
impl Default for FmtInfo {
fn default() -> Self {
Self {
ver: FmtVer::V5,
kind: FmtKind::Collage,
exe_ver: 0,
dummy: 0,
}
}
}
#[derive(Default)]
pub struct Song {
pub text: Text,
pub master: Master,
pub events: EveList,
pub fmt: FmtInfo,
}
impl Song {
pub fn recalculate_length(&mut self) {
self.master.adjust_meas_num(std::cmp::max(
self.master.get_last_tick(),
self.events.get_max_tick(),
));
}
}
pub struct MooInstructions {
pub out_sample_rate: SampleRate,
pub voices: Voices,
pub samples_per_tick: SamplesPerTick,
}
#[derive(Default)]
pub struct Voices(ArrayVec<Voice, 100>);
impl std::ops::Deref for Voices {
type Target = ArrayVec<Voice, 100>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl std::ops::DerefMut for Voices {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl std::ops::Index<VoiceIdx> for Voices {
type Output = Voice;
fn index(&self, index: VoiceIdx) -> &Self::Output {
&self.0[usize::from(index.0)]
}
}
impl std::ops::IndexMut<VoiceIdx> for Voices {
fn index_mut(&mut self, index: VoiceIdx) -> &mut Self::Output {
&mut self.0[usize::from(index.0)]
}
}
impl Voices {
#[must_use]
pub const fn len(&self) -> u8 {
#[expect(
clippy::cast_possible_truncation,
reason = "50 is the max unit number, so this always succeeds"
)]
(self.0.len() as u8)
}
#[must_use]
pub const fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn enumerated(&self) -> impl Iterator<Item = (VoiceIdx, &Voice)> {
#[expect(
clippy::cast_possible_truncation,
reason = "50 is the max unit number, so this always succeeds"
)]
self.iter().enumerate().map(|(idx, item)| (VoiceIdx(idx as u8), item))
}
pub fn enumerated_mut(&mut self) -> impl Iterator<Item = (VoiceIdx, &mut Voice)> {
#[expect(
clippy::cast_possible_truncation,
reason = "50 is the max unit number, so this always succeeds"
)]
self.iter_mut().enumerate().map(|(idx, item)| (VoiceIdx(idx as u8), item))
}
#[must_use]
pub fn get<'a>(&'a self, idx: VoiceIdx, extra_voices: &'a [Voice]) -> Option<&'a Voice> {
if idx.0 < 100 {
self.0.get(idx.usize())
} else {
extra_voices.get(idx.usize() - 100)
}
}
#[must_use]
pub fn get_mut(&mut self, idx: VoiceIdx) -> Option<&mut Voice> {
self.0.get_mut(idx.usize())
}
}
impl MooInstructions {
#[must_use]
pub fn new(out_sample_rate: SampleRate) -> Self {
Self {
out_sample_rate,
voices: Voices::default(),
samples_per_tick: 1.0,
}
}
}
pub fn rebuild_tones(
ins: &mut MooInstructions,
out_sample_rate: SampleRate,
delays: &mut [Delay],
overdrives: &mut [Overdrive],
master: &Master,
) {
for delay in delays {
delay.rebuild(
master.timing.beats_per_meas,
master.timing.bpm,
ins.out_sample_rate,
);
}
for ovr in overdrives {
ovr.rebuild();
}
let builder = NoiseTable::generate();
for voice in ins.voices.iter_mut() {
voice.recalculate(&builder, out_sample_rate);
}
}
#[derive(Default)]
pub struct Herd {
pub moo_end: bool,
loop_: bool,
smp_smooth: SampleRate,
pub smp_count: SampleT,
smp_start: SampleT,
pub smp_end: SampleT,
pub smp_repeat: SampleT,
smp_stride: f32,
time_pan_index: usize,
pub evt_idx: usize,
pub units: Box<Units>,
pub delays: Delays,
pub overdrives: Overdrives,
}
pub type Delays = ArrayVec<Delay, 4>;
pub type Overdrives = ArrayVec<Overdrive, 2>;
#[derive(Default)]
pub struct Units(pub(crate) ArrayVec<Unit, 50>);
impl Units {
#[must_use]
pub const fn len(&self) -> u8 {
#[expect(
clippy::cast_possible_truncation,
reason = "50 is the max unit number, so this always succeeds"
)]
(self.0.len() as u8)
}
#[must_use]
pub const fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn enumerated(&self) -> impl Iterator<Item = (UnitIdx, &Unit)> {
#[expect(
clippy::cast_possible_truncation,
reason = "50 is the max unit number, so this always succeeds"
)]
self.iter().enumerate().map(|(idx, item)| (UnitIdx(idx as u8), item))
}
pub fn enumerated_mut(&mut self) -> impl Iterator<Item = (UnitIdx, &mut Unit)> {
#[expect(
clippy::cast_possible_truncation,
reason = "50 is the max unit number, so this always succeeds"
)]
self.iter_mut().enumerate().map(|(idx, item)| (UnitIdx(idx as u8), item))
}
#[must_use]
pub fn get(&self, idx: UnitIdx) -> Option<&Unit> {
self.0.get(idx.usize())
}
#[must_use]
pub fn get_mut(&mut self, idx: UnitIdx) -> Option<&mut Unit> {
self.0.get_mut(idx.usize())
}
}
impl std::ops::Deref for Units {
type Target = ArrayVec<Unit, 50>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl std::ops::DerefMut for Units {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl<T: Into<UnitIdx>> std::ops::Index<T> for Units {
type Output = Unit;
fn index(&self, index: T) -> &Self::Output {
&self.0[usize::from(index.into().0)]
}
}
impl<T: Into<UnitIdx>> std::ops::IndexMut<T> for Units {
fn index_mut(&mut self, index: T) -> &mut Self::Output {
&mut self.0[usize::from(index.into().0)]
}
}
impl Herd {
pub const fn seek_to_sample(&mut self, sample: SampleT) {
self.smp_count = sample;
self.evt_idx = 0;
}
pub fn tune_cow_voices(
&mut self,
ins: &MooInstructions,
timing: Timing,
extra_voices: &[Voice],
) {
for unit in self.units.iter_mut() {
unit.tone_init();
unit.reset_voice(ins, VoiceIdx(0), timing, extra_voices);
}
}
}
#[expect(clippy::missing_errors_doc)]
pub fn read_song(
data: &[u8],
out_sample_rate: SampleRate,
) -> ReadResult<(Song, Herd, MooInstructions)> {
let mut song = Song {
text: Text::default(),
master: Master::default(),
events: EveList::default(),
fmt: FmtInfo {
ver: FmtVer::V5,
kind: FmtKind::Collage,
exe_ver: 0,
dummy: 0,
},
};
let mut ins = MooInstructions {
out_sample_rate,
voices: Voices::default(),
samples_per_tick: 0.0,
};
let mut herd = Herd::default();
io::read(&mut song, &mut herd, &mut ins, data)?;
song.recalculate_length();
rebuild_tones(
&mut ins,
out_sample_rate,
&mut herd.delays,
&mut herd.overdrives,
&song.master,
);
Ok((song, herd, ins))
}
pub fn serialize_project(song: &Song, herd: &Herd, ins: &MooInstructions) -> WriteResult<Vec<u8>> {
io::write(song, herd, ins)
}