use alloc::format;
use alloc::{vec, vec::Vec};
use crate::prelude::*;
use crate::tracker::import::memory::{ImportMemory, MemoryType};
use crate::tracker::import::patternslot::PatternSlot;
use super::instr_helper::InstrHelper;
use super::one_sid::OneSid;
use super::sound_fx::SoundFx;
use super::voices::Voices;
#[derive(Debug)]
pub struct SidModule {
pub sid: OneSid,
pub voices: Voices,
pub instruments: Vec<InstrRobSid>,
pub soundfx: Vec<SoundFx>,
}
impl SidModule {
pub fn to_modules(&self) -> Vec<Module> {
let mut modules: Vec<Module> = vec![];
for song_number in 0..self.voices.songs.len() {
let mut module = Module::default();
module.name = format!("{} {}", self.sid.name, song_number);
module.comment = format!(
"{} - {} (song #{})",
self.sid.copyright, self.sid.author, song_number
);
module.quirks = crate::tracker::profiles::pt();
module.origin = Some(crate::tracker::format::ModuleFormat::Sid);
let resetspd = self
.sid
.resetspd_songs
.get(song_number)
.copied()
.unwrap_or(self.sid.resetspd);
module.default_tempo = 1 + resetspd;
if self.sid.frame_skip_reset > 0 {
let n = self.sid.frame_skip_reset;
module.default_bpm = (module.default_bpm * n) / (n + 1);
}
let short_gate_instrs = self.sid.short_gate_instrs();
let passes = self.voices.voice_passes(song_number, &short_gate_instrs);
let num_voices = passes.len();
let song_rows = passes.iter().map(|p| p.len()).max().unwrap_or(0);
let speed = module.default_tempo as u8;
let cadence = self.sid.tempo_frac_for(song_number);
let tick_at = move |r: u32| -> u32 {
match cadence {
Some((num, den)) => (r * num).div_ceil(den),
None => r * speed as u32,
}
};
let speed_at = move |r: u32| -> u8 {
match cadence {
Some((num, den)) => {
(((r + 1) * num).div_ceil(den) - (r * num).div_ceil(den)) as u8
}
None => speed,
}
};
let mut voices_tius: Vec<Vec<crate::tracker::import::unit::TrackImportUnit>> =
Vec::with_capacity(num_voices);
for pass in passes.iter().take(num_voices) {
let voice_pattern: Vec<Vec<PatternSlot>> = pass.iter().map(|s| vec![*s]).collect();
let mut im = ImportMemory::default();
let unpacked = im.unpack_patterns(
FrequencyType::LinearFrequencies,
MemoryType::Mod,
&[vec![0]],
&[voice_pattern],
);
let voice_rows: Vec<crate::tracker::import::unit::TrackImportUnit> = unpacked
.first()
.map(|p| p.iter().map(|r| r[0].clone()).collect())
.unwrap_or_default();
voices_tius.push(voice_rows);
}
for (voice_idx, vrows) in voices_tius.iter().enumerate() {
let len = vrows.len() as u32;
if len > 0 {
module
.channel_loops
.push(crate::core::daw::loop_region::ChannelLoop {
song: 0,
channel: voice_idx as u8,
start_tick: 0,
end_tick: tick_at(len),
});
}
}
let mut tracks: Vec<Track> = Vec::new();
let mut clips: Vec<Clip> = Vec::new();
for (voice_idx, voice_rows) in voices_tius.iter().enumerate() {
let segments = crate::tracker::import::build::split_rows_by_instrument(voice_rows);
for seg in segments {
let track_idx = tracks.len() as u32;
let seg_start_row = seg.start_row;
let materialised: Vec<Cell> =
seg.rows.into_iter().map(|tiu| tiu.prepare_cell()).collect();
let seg_len = materialised.len() as u32;
tracks.push(Track::Notes {
name: format!("voice {} seg {}", voice_idx, track_idx),
instrument: seg.instrument,
rows: materialised,
muted: false,
});
let position_tick = tick_at(seg_start_row);
clips.push(Clip {
track: track_idx,
song: 0,
target_channel: voice_idx as u8,
position_tick,
speed_at_start: speed_at(seg_start_row),
track_row_offset: 0,
source_start_row: seg_start_row,
end_tick: tick_at(seg_start_row + seg_len),
});
}
}
let bpm = module.default_bpm as u16;
let mut entries: Vec<crate::core::daw::timeline::TimelineEntry> =
Vec::with_capacity(song_rows);
for r in 0..song_rows {
entries.push(crate::core::daw::timeline::TimelineEntry {
song: 0,
order_idx: 0,
pattern_idx: 0, row_idx: r as u32,
loop_iter: 0,
tick: tick_at(r as u32),
speed_at_row: speed_at(r as u32),
bpm_at_row: bpm,
});
}
module.timeline_map = crate::core::daw::timeline::TimelineMap { entries };
module.tracks = tracks;
module.clips = crate::core::daw::sorted_clips::SortedClips::from_unsorted(clips);
let idst = InstrHelper::irss_to_instruments(&self.instruments);
module.instrument = idst;
crate::tracker::import::build::dedupe_tracks_by_content(&mut module);
let max_rows = voices_tius.iter().map(|v| v.len()).max().unwrap_or(0);
let synthetic_pattern: crate::tracker::import::build::Pattern = (0..max_rows)
.map(|r| {
voices_tius
.iter()
.map(|voice| voice.get(r).cloned().unwrap_or_default())
.collect()
})
.collect();
let lanes = crate::tracker::import::extract::extract_per_track_lanes_from_patterns(
core::slice::from_ref(&synthetic_pattern),
&module.timeline_map,
&module.clips,
&module.quirks,
);
module.automation.extend(lanes);
modules.push(module);
}
modules
}
}
impl SidModule {
pub fn get_sid_commando() -> Self {
let sid = OneSid::get_sid_commando();
sid.to_sidmodule()
}
pub fn get_sid_crazy_comets() -> Self {
let sid = OneSid::get_sid_crazy_comets();
sid.to_sidmodule()
}
pub fn get_sid_last_v8() -> Self {
let sid = OneSid::get_sid_last_v8();
sid.to_sidmodule()
}
pub fn get_sid_monty_on_the_run() -> Self {
let sid = OneSid::get_sid_monty_on_the_run();
sid.to_sidmodule()
}
pub fn get_sid_thing_on_a_spring() -> Self {
let sid = OneSid::get_sid_thing_on_a_spring();
sid.to_sidmodule()
}
pub fn get_sid_zoid() -> Self {
let sid = OneSid::get_sid_zoid();
sid.to_sidmodule()
}
pub fn get_sid_ace_2() -> Self {
let sid = OneSid::get_sid_ace_2();
sid.to_sidmodule()
}
pub fn get_sid_delta() -> Self {
let sid = OneSid::get_sid_delta();
sid.to_sidmodule()
}
pub fn get_sid_human_race() -> Self {
let sid = OneSid::get_sid_human_race();
sid.to_sidmodule()
}
pub fn get_sid_international_karate() -> Self {
let sid = OneSid::get_sid_international_karate();
sid.to_sidmodule()
}
pub fn get_sid_lightforce() -> Self {
let sid = OneSid::get_sid_lightforce();
sid.to_sidmodule()
}
pub fn get_sid_sanxion_song_1() -> Self {
let sid = OneSid::get_sid_sanxion_song_1();
sid.to_sidmodule()
}
pub fn get_sid_sanxion_song_2() -> Self {
let sid = OneSid::get_sid_sanxion_song_2();
sid.to_sidmodule()
}
pub fn get_sid_spellbound() -> Self {
let sid = OneSid::get_sid_spellbound();
sid.to_sidmodule()
}
pub fn get_sid_thrust() -> Self {
let sid = OneSid::get_sid_thrust();
sid.to_sidmodule()
}
}