use crate::prelude::*;
use crate::tracker::import::patternslot::PatternSlot;
use alloc::{vec, vec::Vec};
#[derive(Debug)]
pub struct Voices {
pub version: usize,
pub songs: Vec<Vec<usize>>,
pub orderings: Vec<Vec<u8>>,
pub phrases: Vec<Vec<u8>>,
}
impl Voices {
pub fn new(
version: usize,
songs: Vec<Vec<usize>>,
orderings: Vec<Vec<u8>>,
phrases: Vec<Vec<u8>>,
) -> Self {
Self {
version,
songs,
orderings,
phrases,
}
}
pub fn get_voice_grid(&self, song_number: usize) -> Vec<Vec<PatternSlot>> {
let phrases = self.decode_all_phrases();
let voice_orderings = self.voice_orderings(song_number);
let num_voices = voice_orderings.len();
let mut all_done: Vec<bool> = vec![false; num_voices];
let mut ordering_cursor: [usize; 3] = [0; 3];
let mut grid: Vec<Vec<PatternSlot>> = Vec::new();
loop {
let mut current_phrase: Vec<&Vec<PatternSlot>> = (0..num_voices)
.map(|k| pick_phrase(&phrases, voice_orderings[k][ordering_cursor[k]]))
.collect();
let mut remaining = current_phrase.iter().map(|p| p.len()).max().unwrap_or(0);
let mut row_cursor: [usize; 3] = [0; 3];
while remaining != 0 {
if grid.len() >= Self::MAX_GRID_ROWS {
return grid;
}
let mut row: Vec<PatternSlot> = Vec::with_capacity(num_voices);
for k in 0..num_voices {
if row_cursor[k] >= current_phrase[k].len() {
ordering_cursor[k] += 1;
if ordering_cursor[k] >= voice_orderings[k].len() {
ordering_cursor[k] = 0;
}
row_cursor[k] = 0;
current_phrase[k] =
pick_phrase(&phrases, voice_orderings[k][ordering_cursor[k]]);
if current_phrase[k].len() > remaining {
remaining = current_phrase[k].len();
}
}
row.push(current_phrase[k][row_cursor[k]]);
row_cursor[k] += 1;
}
remaining -= 1;
grid.push(row);
}
for k in 0..num_voices {
ordering_cursor[k] += 1;
if ordering_cursor[k] >= voice_orderings[k].len() {
ordering_cursor[k] = 0;
all_done[k] = true;
if all_done.iter().all(|&b| b) {
return grid;
}
} else if voice_orderings[k][ordering_cursor[k]] == 254 {
return grid;
}
}
}
}
pub const MAX_GRID_ROWS: usize = 1 << 16;
fn voice_orderings(&self, song_number: usize) -> Vec<&Vec<u8>> {
self.songs[song_number]
.iter()
.map(|s| &self.orderings[*s])
.collect()
}
fn decode_all_phrases(&self) -> Vec<Vec<PatternSlot>> {
self.phrases
.iter()
.map(|p| decode_phrase(self.version, p))
.collect()
}
}
fn pick_phrase(phrases: &[Vec<PatternSlot>], idx: u8) -> &Vec<PatternSlot> {
let i = idx as usize;
if i < phrases.len() {
return &phrases[i];
}
let fallback = match idx {
111 => 3,
112 => 2,
_ => 0,
};
&phrases[fallback.min(phrases.len() - 1)]
}
fn decode_phrase(version: usize, source: &[u8]) -> Vec<PatternSlot> {
let byte = |idx: usize| -> u8 { source.get(idx).copied().unwrap_or(0xFF) };
let mut phrase: Vec<PatternSlot> = vec![];
let mut index: usize = 0;
let mut last_instr: Option<usize> = None;
while byte(index) != 255 {
let mut current = PatternSlot::default();
let length = byte(index) & 0b0001_1111; let release = (byte(index) & 0b0010_0000) == 0;
let append = (byte(index) & 0b0100_0000) == 0;
let instr_or_portamento = (byte(index) & 0b1000_0000) != 0;
if append {
index += 1;
if instr_or_portamento {
match version {
10 => {
if byte(index) & 0b1000_0000 == 0 {
current.instrument = Some((byte(index) & 0b0111_1111) as usize);
last_instr = current.instrument;
} else {
let p = byte(index) & 0b0111_1110;
if p != 0 {
current.effect_parameter = p >> 1;
if byte(index) & 1 == 0 {
current.effect_type = 1; } else {
current.effect_type = 2; }
}
}
}
15 => {
if byte(index) & 0b1000_0000 == 0 {
current.instrument = Some((byte(index) & 0b0111_1111) as usize);
last_instr = current.instrument;
}
}
_ => {
if byte(index) & 0b1000_0000 == 0 {
current.instrument = Some((byte(index) & 0b0111_1111) as usize);
last_instr = current.instrument;
} else if index + 2 >= source.len() {
index = index.saturating_sub(2);
} else {
let direction_down = byte(index) & 0b0100_0000 != 0;
let p: u16 =
((byte(index) as u16 & 0b0011_1111) << 8) | byte(index + 1) as u16;
index += 1;
if p != 0 {
current.effect_parameter = (p >> 6).min(255) as u8;
if direction_down {
current.effect_type = 2; } else {
current.effect_type = 1; }
}
}
}
}
index += 1;
}
let n = byte(index) & 0b0111_1111;
let note = if n > 8 * 12 {
match n {
96 => 0,
97 => 0,
98 => 12,
100 => 3,
104 => 65,
105 => 65,
107 => 6,
127 => 0,
_ => n & 0b0011_1111, }
} else {
n
};
current.note = match Pitch::try_from(note) {
Ok(p) => CellNote::Play(p),
Err(_) => CellNote::Empty,
};
}
if release {
if matches!(current.note, CellNote::Empty) {
current.note = CellNote::KeyOff;
} else {
current.volume = 0x50; }
current.instrument = last_instr;
}
index += 1;
phrase.push(current);
if length != 0 {
let empty = PatternSlot::default();
for _ in 0..length {
phrase.push(empty);
}
}
}
phrase
}