Skip to main content

DwModule

Struct DwModule 

Source
pub struct DwModule {
Show 19 fields pub variant: DwVariant, pub period_table: PeriodTable, pub period_via_finetune: bool, pub samples: Vec<DwSample>, pub sub_song: Option<DwSubSong>, pub sub_songs: Vec<DwSubSong>, pub selected_sub_song: Option<usize>, pub position_lists: [DwPositionList; 4], pub all_position_lists: Vec<[DwPositionList; 4]>, pub tracks: Vec<DwTrack>, pub volume_envelopes: Vec<DwVolumeEnvelope>, pub arpeggios: Vec<DwArpeggio>, pub dispatcher: DwDispatcher, pub features: DwFeatures, pub volume_bracket_is_pitch: bool, pub channel_volumes: [u16; 4], pub master_volume: Option<u16>, pub use_pitch_arpeggio: bool, pub use_arpeggio_pitch_lane: bool,
}
Expand description

Parsed contents of a .dw file. Self-sufficient — once a DwModule is built the original payload bytes can be dropped.

Fields§

§variant: DwVariant

Replayer family the file targets.

§period_table: PeriodTable

Which period table the replayer uses.

§period_via_finetune: bool

true when AUDxPER is computed the new-player way — full-range note index into Self::period_table scaled by the per-sample fine-tune (table[note] × mult >> 10) — rather than the qball-era one-octave composite PERIODS1[note % 12]. Always true on the new player and on the old-stream players that carry the fine-tune idiom (leviathan, empire); false on genuine qball composite players. See super::detect::DwLayout::period_via_finetune.

§samples: Vec<DwSample>

The full sample bank. Indices match the on-disk SetSample command argument minus the per-module newSampleCmd threshold (which the runtime layer applies).

§sub_song: Option<DwSubSong>

Currently selected sub-song descriptor. None when no usable sub-song could be located.

Multi-song .dw files are common: most game soundtracks ship every track in a single binary, indexed by a small integer the host passes via D0. The replayer multiplies that index by the row width and uses the result as offset into the sub-song table. The importer parses every entry it can validate into Self::sub_songs, then picks the one most likely to be the “main” song (heuristic — see pick_main_sub_song_index). Callers that want a different sub-song can read Self::sub_songs and re-derive position lists via crate::tracker::import::dw::dw_module::position_lists_for.

§sub_songs: Vec<DwSubSong>

Every sub-song the importer could decode from the sub_song_list_offset row table. Order matches the on-disk index (sub-song 0 = first row); the chosen “primary” sub-song is exposed separately as Self::sub_song / Self::selected_sub_song. Empty when no sub-song header was located.

§selected_sub_song: Option<usize>

Index into Self::sub_songs of the sub-song mirrored into Self::sub_song / Self::position_lists. None when no sub-song is available.

§position_lists: [DwPositionList; 4]

One position list per Paula channel for the selected sub-song, in channel order. Empty when the sub-song header could not be located or when the channel is intentionally silent for this song.

§all_position_lists: Vec<[DwPositionList; 4]>

Position lists for every sub-song, indexed to match Self::sub_songs. Self::position_lists is a clone of all_position_lists[selected_sub_song]. The DAW projection walks this to render each sub-song as its own xmrs song.

§tracks: Vec<DwTrack>

Every distinct track byte stream referenced by at least one position list, parsed up to its EndOfTrack terminator. De-duplicated by file offset so a single track played by many channels / many times contributes a single entry.

§volume_envelopes: Vec<DwVolumeEnvelope>

Per-channel volume envelopes — indexed by (track_byte - volume_envelope_threshold) at runtime. Empty when the module either lacks the 0xA0.. dispatcher bracket or when the table-base probe couldn’t locate the envelope-pointer table. See DwVolumeEnvelope for the per-entry shape.

§arpeggios: Vec<DwArpeggio>

Per-channel pitch arpeggios — indexed by (track_byte - pitch_arpeggio_threshold) at runtime (the 0x90.. dispatcher bracket). Empty when the module has no 0x90.. bracket or the table-base probe failed. See DwArpeggio.

§dispatcher: DwDispatcher

Per-module command dispatcher thresholds — kept on the DwModule so the simulator can resolve raw track bytes (e.g. 0xA5) back to envelope-table indices without re-reading the binary.

§features: DwFeatures

Detect-time feature flags lifted directly from super::detect::DwLayout::features. The simulator gates module-dependent semantics (e.g. Effect8 → channel transpose vs. global volume fade) on these.

§volume_bracket_is_pitch: bool

When true, the SetVolumeEnvelope dispatcher bracket arms a pitch arpeggio (indexing Self::arpeggios) instead of a volume envelope — see super::detect::DwLayout::volume_bracket_is_pitch. tetris.dw is the canonical case.

§channel_volumes: [u16; 4]

Old-player per-channel static volume (0..=64), indexed by Paula channel. The qball-era replayer loads this into AUDxVOL on every note trigger and never scales it by a sample or envelope value — it is the channel’s loudness. Read from super::detect::DwLayout::channel_volume_offset; defaults to full scale on new-player modules (whose loudness comes from the volume envelope) and when the table couldn’t be located.

§master_volume: Option<u16>

Static global master volume (1..=64), or None when the module has no master-volume scaling. The empire-era replayer multiplies every Paula volume by this before a >> 6 (AUDxVOL = volByte × master >> 6), at both note-trigger and per-tick envelope animation; the importer reproduces it by scaling all emitted Paula volumes by master / 64. Read from super::detect::DwLayout::master_volume_offset. None leaves volumes unscaled (the common case — new players use the identity master = 64, qball writes channel volumes raw).

§use_pitch_arpeggio: bool

When true, the volume envelope is baked per-tick on a synthetic per-(sample, envelope) instrument (frame-resolution loudness), as opposed to the row-resolution cell Volume animation. Set for the jump-table (command_map) family — bubble bobble & kin. (Historically this flag also drove the pitch arpeggio via a looping pitch envelope; that role has moved to the per-frame use_arpeggio_pitch_lane Points lane, so this now governs only the volume-envelope split.)

§use_arpeggio_pitch_lane: bool

When true, per-frame pitch arpeggios are baked as a TrackPitch LaneKind::Points curve of crate::core::daw::automation::AutomationValue::Pitch points — one per arpeggio-offset change, evaluated every player tick. This is the faithful projection of the replayer’s per-channel, per-frame arpeggio pointer (Ghidra play_tick held-note branch: offset added to the period-table index = semitone space, pointer continuous across notes), as opposed to the classic three-step TrackEffect::Arpeggio (mod-3, trigger-row only) or the old per-note pitch-envelope path. Set for every DW module — the lane is the single arpeggio path for DW, so the classic effect is suppressed and the arpeggio segment-split / pitch-envelope are skipped. No-op for modules that never arm an arpeggio (no points emitted). (use_pitch_arpeggio now governs only the volume-envelope synthetic-instrument path, not the arpeggio.)

Implementations§

Source§

impl DwModule

Source

pub fn load(source: &[u8]) -> Result<Self, ImportError>

Parse a .dw payload. Returns InvalidMagic("dw_detect") when the payload does not look like a David Whittaker module.

Source

pub fn to_module(&self) -> Module

Convert into the editor-friendly Module representation.

Populates the full DAW layer: the instrument bank (one InstrDefault per parsed sample), one Track::Notes per (Paula channel, current sample) run discovered by the simulator, matching Clips placed on each channel lane, and a TimelineMap covering the song length yielded by Self::simulate. After segment creation the standard xmrs crate::tracker::import::build::dedupe_tracks_by_content pass fuses bit-identical segments so a single phrase shared by several channels lives in one Track.

Quantisation: one row = one channel speed-tick (speed frames at 50 Hz PAL). Notes whose Whittaker byte exceeds the 120-position xmrs Pitch range are clamped to the top — DW’s period tables cover at most 6 octaves while xmrs has 10, so the clamp only fires on out-of-band values.

Source§

impl DwModule

Source

pub fn simulate(&self) -> Vec<(u32, TickEvent)>

Run the full simulation and return the event trace. Convenience wrapper for Simulator::run.

Trait Implementations§

Source§

impl Clone for DwModule

Source§

fn clone(&self) -> DwModule

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for DwModule

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.