diff options
Diffstat (limited to 'src/vendored/micromod-rs')
| -rw-r--r-- | src/vendored/micromod-rs/ATTRIBUTION | 1 | ||||
| -rw-r--r-- | src/vendored/micromod-rs/COPYING | 37 | ||||
| -rw-r--r-- | src/vendored/micromod-rs/consts.rs | 11 | ||||
| -rw-r--r-- | src/vendored/micromod-rs/mod.rs | 860 | 
4 files changed, 909 insertions, 0 deletions
| diff --git a/src/vendored/micromod-rs/ATTRIBUTION b/src/vendored/micromod-rs/ATTRIBUTION new file mode 100644 index 0000000..2f5cc0f --- /dev/null +++ b/src/vendored/micromod-rs/ATTRIBUTION @@ -0,0 +1 @@ +https://github.com/aspizu/micromod-rust/tree/bcf4f62610c2a7b4b417cbde79241efdaa7ed71d
\ No newline at end of file diff --git a/src/vendored/micromod-rs/COPYING b/src/vendored/micromod-rs/COPYING new file mode 100644 index 0000000..f5dc43e --- /dev/null +++ b/src/vendored/micromod-rs/COPYING @@ -0,0 +1,37 @@ +--- +Copyright (c) 2019, Martin Cameron +All rights reserved. + +Redistribution and use in source and binary forms, with or +without modification, are permitted provided that the +following conditions are met: + + * Redistributions of source code must retain the above +   copyright notice, this list of conditions and the +   following disclaimer. + + * Redistributions in binary form must reproduce the +   above copyright notice, this list of conditions and the +   following disclaimer in the documentation and/or other +   materials provided with the distribution. +  + * Neither the name of the organization nor the names of +   its contributors may be used to endorse or promote +   products derived from this software without specific +   prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND +CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE +GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +---
\ No newline at end of file diff --git a/src/vendored/micromod-rs/consts.rs b/src/vendored/micromod-rs/consts.rs new file mode 100644 index 0000000..092766d --- /dev/null +++ b/src/vendored/micromod-rs/consts.rs @@ -0,0 +1,11 @@ +pub static MICROMOD_VERSION: &str = "Micromod Protracker replay 20180625 (c)mumart@gmail.com"; +pub static FINE_TUNING: [u16; 16] = [ +  4340, 4308, 4277, 4247, 4216, 4186, 4156, 4126, 4096, 4067, 4037, 4008, 3979, 3951, 3922, 3894, +]; +pub static ARP_TUNING: [u16; 16] = [ +  4096, 3866, 3649, 3444, 3251, 3069, 2896, 2734, 2580, 2435, 2299, 2170, 2048, 1933, 1825, 1722, +]; +pub static SINE_TABLE: [u8; 32] = [ +  0, 24, 49, 74, 97, 120, 141, 161, 180, 197, 212, 224, 235, 244, 250, 253, 255, 253, 250, 244, +  235, 224, 212, 197, 180, 161, 141, 120, 97, 74, 49, 24, +]; diff --git a/src/vendored/micromod-rs/mod.rs b/src/vendored/micromod-rs/mod.rs new file mode 100644 index 0000000..1d1934a --- /dev/null +++ b/src/vendored/micromod-rs/mod.rs @@ -0,0 +1,860 @@ +//! Experiment with converting [Micromod](https://github.com/martincameron/micromod) with +//! [c2rust](https://c2rust.com/). +//! +//! Safetyfication done manually + +#![warn(missing_docs)] + +mod consts; + +use std::cmp::Ordering; + +#[derive(Copy, Clone, Default)] +struct Channel { +  note: Note, +  period: u16, +  porta_period: u16, +  sample_offset: usize, +  sample_idx: usize, +  step: usize, +  volume: u8, +  panning: u8, +  fine_tune: u8, +  ampl: u8, +  mute: u8, +  id: u8, +  instrument: u8, +  assigned: u8, +  porta_speed: u8, +  pl_row: u8, +  fx_count: u8, +  vibrato_type: u8, +  vibrato_phase: u8, +  vibrato_speed: u8, +  vibrato_depth: u8, +  tremolo_type: u8, +  tremolo_phase: u8, +  tremolo_speed: u8, +  tremolo_depth: u8, +  tremolo_add: i8, +  vibrato_add: i8, +  arpeggio_add: i8, +} +#[derive(Copy, Clone, Default)] +struct Note { +  key: u16, +  instrument: u8, +  effect: u8, +  param: u8, +} +#[derive(Copy, Clone)] +struct Instrument<'src> { +  volume: u8, +  fine_tune: u8, +  loop_start: usize, +  loop_length: usize, +  sample_data: &'src [i8], +} + +impl Instrument<'_> { +  fn dummy() -> Self { +    Self { +      volume: 0, +      fine_tune: 0, +      loop_start: 0, +      loop_length: 0, +      sample_data: &[], +    } +  } +} + +/// Immutable source data of the module +struct ModSrc<'src> { +  instruments: Vec<Instrument<'src>>, +  module_data: &'src [i8], +  pattern_data: &'src [u8], +  sequence: &'src [u8], +  num_patterns: isize, +  num_channels: isize, +  song_length: isize, +} + +#[derive(Default)] +struct PlaybackState { +  gain: isize, +  c2_rate: isize, +  tick_len: isize, +  tick_offset: isize, +  pattern: isize, +  break_pattern: isize, +  row: isize, +  next_row: isize, +  tick: isize, +  speed: isize, +  pl_count: isize, +  pl_channel: isize, +  random_seed: isize, +} + +/// A micromod decoding instance thingy. +pub struct MmC2r<'src> { +  sample_rate: isize, +  channels: [Channel; 16], +  src: ModSrc<'src>, +  playback: PlaybackState, +} + +fn calculate_num_patterns(module_header: &[i8]) -> isize { +  let mut num_patterns_0; +  let mut order_entry; +  let mut pattern_0; +  num_patterns_0 = 0; +  pattern_0 = 0; +  while pattern_0 < 128 { +    order_entry = (module_header[(952 + pattern_0) as usize] as i32 & 0x7f_i32) as isize; +    if order_entry >= num_patterns_0 { +      num_patterns_0 = order_entry + 1; +    } +    pattern_0 += 1; +  } +  num_patterns_0 +} + +fn calculate_num_channels(module_header: &[i8]) -> Option<isize> { +  const MAX_CHANNELS: isize = 16; +  let numchan: isize = match ((module_header[1082] as isize) << 8) | module_header[1083] as isize { +    // M.K.  M!K!     N.T.     FLT4 +    0x4b2e | 0x4b21 | 0x542e | 0x5434 => 4, +    // xCHN +    0x484e => (module_header[1080] - 48) as isize, +    // xxCH +    0x4348 => (((module_header[1080] - 48) * 10) + (module_header[1081] - 48)) as isize, +    // Not recognised. +    _ => 0, +  }; +  if numchan > MAX_CHANNELS { +    None +  } else { +    Some(numchan) +  } +} +fn unsigned_short_big_endian(buf: &[i8], offset: isize) -> isize { +  ((buf[offset as usize] as i32 & 0xff_i32) << 8 | buf[(offset + 1) as usize] as i32 & 0xff_i32) +    as isize +} +fn set_tempo(tempo: isize, tick_len: &mut isize, sample_rate: isize) { +  *tick_len = ((sample_rate << 1) + (sample_rate >> 1)) / tempo; +} +fn update_frequency(chan: &mut Channel, sample_rate: isize, gain: &mut isize, c2_rate: &mut isize) { +  let mut period; +  let mut volume; + +  period = (chan.period as i32 + chan.vibrato_add as i32) as isize; +  period = (period * consts::ARP_TUNING[chan.arpeggio_add as usize] as isize) >> 11; +  period = (period >> 1) + (period & 1); +  if period < 14 { +    period = 6848; +  } +  let freq = (*c2_rate * 428 / period) as usize; +  chan.step = (freq << 14).wrapping_div(sample_rate as usize); +  volume = (chan.volume as i32 + chan.tremolo_add as i32) as isize; +  volume = volume.clamp(0, 64); +  chan.ampl = ((volume * *gain) >> 5) as u8; +} +fn tone_portamento(chan: &mut Channel) { +  let mut source; + +  source = chan.period as isize; +  let dest = chan.porta_period as isize; +  match source.cmp(&dest) { +    Ordering::Less => { +      source += chan.porta_speed as isize; +      if source > dest { +        source = dest; +      } +    } +    Ordering::Equal => { /* Do absolutely nothing */ } +    Ordering::Greater => { +      source -= chan.porta_speed as isize; +      if source < dest { +        source = dest; +      } +    } +  } +  chan.period = source as u16; +} +fn volume_slide(chan: &mut Channel, param: isize) { +  let mut volume; +  volume = chan.volume as isize + (param >> 4) - (param & 0xf); +  volume = volume.clamp(0, 64); +  chan.volume = volume as u8; +} +fn waveform(phase: isize, type_0: isize, random_seed: &mut isize) -> isize { +  let mut amplitude: isize = 0; +  match type_0 & 0x3 { +    0 => { +      amplitude = consts::SINE_TABLE[(phase & 0x1f) as usize] as isize; +      if phase & 0x20 > 0 { +        amplitude = -amplitude; +      } +    } +    1 => { +      amplitude = 255 - (((phase + 0x20) & 0x3f) << 3); +    } +    2 => { +      amplitude = 255 - ((phase & 0x20) << 4); +    } +    3 => { +      amplitude = (*random_seed >> 20) - 255; +      *random_seed = (*random_seed * 65 + 17) & 0x1fffffff; +    } +    _ => {} +  } +  amplitude +} +fn vibrato(chan: &mut Channel, random_seed: &mut isize) { +  chan.vibrato_add = ((waveform( +    chan.vibrato_phase as isize, +    chan.vibrato_type as isize, +    random_seed, +  ) * chan.vibrato_depth as isize) +    >> 7) as i8; +} +fn tremolo(chan: &mut Channel, random_seed: &mut isize) { +  chan.tremolo_add = ((waveform( +    chan.tremolo_phase as isize, +    chan.tremolo_type as isize, +    random_seed, +  ) * chan.tremolo_depth as isize) +    >> 6) as i8; +} +fn trigger(channel: &mut Channel, instruments: &[Instrument]) { +  let period; + +  let ins = channel.note.instrument as isize; +  if ins > 0 && ins < 32 { +    channel.assigned = ins as u8; +    channel.sample_offset = 0; +    channel.fine_tune = instruments[ins as usize].fine_tune; +    channel.volume = instruments[ins as usize].volume; +    if instruments[ins as usize].loop_length > 0 && channel.instrument as i32 > 0 { +      channel.instrument = ins as u8; +    } +  } +  if channel.note.effect as i32 == 0x9_i32 { +    channel.sample_offset = ((channel.note.param as i32 & 0xff_i32) << 8) as usize; +  } else if channel.note.effect as i32 == 0x15_i32 { +    channel.fine_tune = channel.note.param; +  } +  if channel.note.key as i32 > 0 { +    period = ((channel.note.key as i32 +      * consts::FINE_TUNING[(channel.fine_tune as i32 & 0xf_i32) as usize] as i32) +      >> 11) as isize; +    channel.porta_period = ((period >> 1) + (period & 1)) as u16; +    if channel.note.effect as i32 != 0x3_i32 && channel.note.effect as i32 != 0x5_i32 { +      channel.instrument = channel.assigned; +      channel.period = channel.porta_period; +      channel.sample_idx = channel.sample_offset << 14; +      if (channel.vibrato_type as i32) < 4 { +        channel.vibrato_phase = 0; +      } +      if (channel.tremolo_type as i32) < 4 { +        channel.tremolo_phase = 0; +      } +    } +  } +} +fn channel_row(chan: &mut Channel, sample_rate: isize, src: &ModSrc, playback: &mut PlaybackState) { +  let volume; +  let period; +  let effect = chan.note.effect as isize; +  let param = chan.note.param as isize; +  let fresh0 = &mut chan.fx_count; +  *fresh0 = 0; +  let fresh1 = &mut chan.arpeggio_add; +  *fresh1 = *fresh0 as i8; +  let fresh2 = &mut chan.tremolo_add; +  *fresh2 = *fresh1; +  chan.vibrato_add = *fresh2; +  if !(effect == 0x1d && param > 0) { +    trigger(chan, &src.instruments); +  } +  match effect { +    3 => { +      if param > 0 { +        chan.porta_speed = param as u8; +      } +    } +    4 => { +      if param & 0xf0 > 0 { +        chan.vibrato_speed = (param >> 4) as u8; +      } +      if param & 0xf > 0 { +        chan.vibrato_depth = (param & 0xf) as u8; +      } +      vibrato(chan, &mut playback.random_seed); +    } +    6 => { +      vibrato(chan, &mut playback.random_seed); +    } +    7 => { +      if param & 0xf0 > 0 { +        chan.tremolo_speed = (param >> 4) as u8; +      } +      if param & 0xf > 0 { +        chan.tremolo_depth = (param & 0xf) as u8; +      } +      tremolo(chan, &mut playback.random_seed); +    } +    8 => { +      if src.num_channels != 4 { +        chan.panning = (if param < 128 { param } else { 127 }) as u8; +      } +    } +    11 => { +      if playback.pl_count < 0 { +        playback.break_pattern = param; +        playback.next_row = 0; +      } +    } +    12 => { +      chan.volume = (if param > 64 { 64 } else { param }) as u8; +    } +    13 => { +      if playback.pl_count < 0 { +        if playback.break_pattern < 0 { +          playback.break_pattern = playback.pattern + 1; +        } +        playback.next_row = (param >> 4) * 10 + (param & 0xf); +        if playback.next_row >= 64 { +          playback.next_row = 0; +        } +      } +    } +    15 => { +      if param > 0 { +        if param < 32 { +          playback.speed = param; +          playback.tick = playback.speed; +        } else { +          set_tempo(param, &mut playback.tick_len, sample_rate); +        } +      } +    } +    17 => { +      period = chan.period as isize - param; +      chan.period = (if period < 0 { 0 } else { period }) as u16; +    } +    18 => { +      period = chan.period as isize + param; +      chan.period = (if period > 65535 { 65535 } else { period }) as u16; +    } +    20 => { +      if param < 8 { +        chan.vibrato_type = param as u8; +      } +    } +    22 => { +      if param == 0 { +        chan.pl_row = playback.row as u8; +      } +      if (chan.pl_row as isize) < playback.row && playback.break_pattern < 0 { +        if playback.pl_count < 0 { +          playback.pl_count = param; +          playback.pl_channel = chan.id as isize; +        } +        if playback.pl_channel == chan.id as isize { +          if playback.pl_count == 0 { +            chan.pl_row = (playback.row + 1) as u8; +          } else { +            playback.next_row = chan.pl_row as isize; +          } +          playback.pl_count -= 1; +        } +      } +    } +    23 => { +      if param < 8 { +        chan.tremolo_type = param as u8; +      } +    } +    26 => { +      volume = chan.volume as isize + param; +      chan.volume = (if volume > 64 { 64 } else { volume }) as u8; +    } +    27 => { +      volume = chan.volume as isize - param; +      chan.volume = (if volume < 0 { 0 } else { volume }) as u8; +    } +    28 => { +      if param <= 0 { +        chan.volume = 0; +      } +    } +    30 => { +      playback.tick = playback.speed + playback.speed * param; +    } +    _ => {} +  } +  update_frequency(chan, sample_rate, &mut playback.gain, &mut playback.c2_rate); +} +fn channel_tick( +  chan: &mut Channel, +  sample_rate: isize, +  gain: &mut isize, +  c2_rate: &mut isize, +  random_seed: &mut isize, +  instruments: &[Instrument], +) { +  let period; +  let effect = chan.note.effect as isize; +  let param = chan.note.param as isize; +  let fresh3 = &mut chan.fx_count; +  *fresh3 = (*fresh3).wrapping_add(1); +  match effect { +    1 => { +      period = chan.period as isize - param; +      chan.period = (if period < 0 { 0 } else { period }) as u16; +    } +    2 => { +      period = chan.period as isize + param; +      chan.period = (if period > 65535 { 65535 } else { period }) as u16; +    } +    3 => { +      tone_portamento(chan); +    } +    4 => { +      let fresh4 = &mut chan.vibrato_phase; +      *fresh4 = (*fresh4 as i32 + chan.vibrato_speed as i32) as u8; +      vibrato(chan, random_seed); +    } +    5 => { +      tone_portamento(chan); +      volume_slide(chan, param); +    } +    6 => { +      let fresh5 = &mut chan.vibrato_phase; +      *fresh5 = (*fresh5 as i32 + chan.vibrato_speed as i32) as u8; +      vibrato(chan, random_seed); +      volume_slide(chan, param); +    } +    7 => { +      let fresh6 = &mut chan.tremolo_phase; +      *fresh6 = (*fresh6 as i32 + chan.tremolo_speed as i32) as u8; +      tremolo(chan, random_seed); +    } +    10 => { +      volume_slide(chan, param); +    } +    14 => { +      if chan.fx_count as i32 > 2 { +        chan.fx_count = 0; +      } +      if chan.fx_count as i32 == 0 { +        chan.arpeggio_add = 0; +      } +      if chan.fx_count as i32 == 1 { +        chan.arpeggio_add = (param >> 4) as i8; +      } +      if chan.fx_count as i32 == 2 { +        chan.arpeggio_add = (param & 0xf) as i8; +      } +    } +    25 => { +      if chan.fx_count as isize >= param { +        chan.fx_count = 0; +        chan.sample_idx = 0; +      } +    } +    28 => { +      if param == chan.fx_count as isize { +        chan.volume = 0; +      } +    } +    29 => { +      if param == chan.fx_count as isize { +        trigger(chan, instruments); +      } +    } +    _ => {} +  } +  if effect > 0 { +    update_frequency(chan, sample_rate, gain, c2_rate); +  } +} +fn sequence_row(state: &mut MmC2r) -> bool { +  let mut song_end = false; +  let mut chan_idx; +  let mut pat_offset; +  let mut effect; +  let mut param; +  let mut note; +  if state.playback.next_row < 0 { +    state.playback.break_pattern = state.playback.pattern + 1; +    state.playback.next_row = 0; +  } +  if state.playback.break_pattern >= 0 { +    if state.playback.break_pattern >= state.src.song_length { +      state.playback.next_row = 0; +      state.playback.break_pattern = state.playback.next_row; +    } +    if state.playback.break_pattern <= state.playback.pattern { +      song_end = true; +    } +    state.playback.pattern = state.playback.break_pattern; +    chan_idx = 0; +    while chan_idx < state.src.num_channels { +      state.channels[chan_idx as usize].pl_row = 0; +      chan_idx += 1; +    } +    state.playback.break_pattern = -1; +  } +  state.playback.row = state.playback.next_row; +  state.playback.next_row = state.playback.row + 1; +  if state.playback.next_row >= 64 { +    state.playback.next_row = -1; +  } +  pat_offset = ((state.src.sequence[state.playback.pattern as usize] as i32 * 64) as isize +    + state.playback.row) +    * state.src.num_channels +    * 4; +  chan_idx = 0; +  while chan_idx < state.src.num_channels { +    note = &mut (state.channels[chan_idx as usize]).note; +    let pattern_data = state.src.pattern_data; +    note.key = ((pattern_data[pat_offset as usize] as i32 & 0xf_i32) << 8) as u16; +    let fresh7 = &mut note.key; +    *fresh7 = (*fresh7 as i32 | pattern_data[(pat_offset + 1) as usize] as i32) as u16; +    note.instrument = (pattern_data[(pat_offset + 2) as usize] as i32 >> 4) as u8; +    let fresh8 = &mut note.instrument; +    *fresh8 = (*fresh8 as i32 | pattern_data[pat_offset as usize] as i32 & 0x10_i32) as u8; +    effect = (pattern_data[(pat_offset + 2) as usize] as i32 & 0xf_i32) as isize; +    param = pattern_data[(pat_offset + 3) as usize] as isize; +    pat_offset += 4; +    if effect == 0xe { +      effect = 0x10 | param >> 4; +      param &= 0xf; +    } +    if effect == 0 && param > 0 { +      effect = 0xe; +    } +    note.effect = effect as u8; +    note.param = param as u8; +    channel_row( +      &mut state.channels[chan_idx as usize], +      state.sample_rate, +      &state.src, +      &mut state.playback, +    ); +    chan_idx += 1; +  } +  song_end +} +fn sequence_tick(state: &mut MmC2r) -> bool { +  let mut song_end = false; +  let mut chan_idx; +  state.playback.tick -= 1; +  if state.playback.tick <= 0 { +    state.playback.tick = state.playback.speed; +    song_end = sequence_row(state); +  } else { +    chan_idx = 0; +    while chan_idx < state.src.num_channels { +      channel_tick( +        &mut state.channels[chan_idx as usize], +        state.sample_rate, +        &mut state.playback.gain, +        &mut state.playback.c2_rate, +        &mut state.playback.random_seed, +        &state.src.instruments, +      ); +      chan_idx += 1; +    } +  } +  song_end +} +fn resample( +  chan: &mut Channel, +  buf: &mut [i16], +  offset: isize, +  count: isize, +  instruments: &[Instrument], +) { +  let mut epos; +  let mut buf_idx: usize = (offset << 1) as usize; +  let buf_end: usize = ((offset + count) << 1) as usize; +  let mut sidx: usize = chan.sample_idx; +  let step: usize = chan.step; +  let llen: usize = instruments[chan.instrument as usize].loop_length; +  let lep1: usize = (instruments[chan.instrument as usize].loop_start).wrapping_add(llen); +  let sdat = instruments[chan.instrument as usize].sample_data; +  let mut ampl: i16 = (if chan.mute == 0 { chan.ampl as i32 } else { 0 }) as i16; +  let lamp: i16 = ((ampl as i32 * (127_i32 - chan.panning as i32)) >> 5) as i16; +  let ramp: i16 = ((ampl as i32 * chan.panning as i32) >> 5) as i16; +  while buf_idx < buf_end { +    if sidx >= lep1 { +      if llen <= 16384 { +        sidx = lep1; +        break; +      } else { +        while sidx >= lep1 { +          sidx = sidx.wrapping_sub(llen); +        } +      } +    } +    epos = sidx.wrapping_add((buf_end.wrapping_sub(buf_idx) >> 1).wrapping_mul(step)); +    if lamp as i32 != 0 || ramp as i32 != 0 { +      if epos > lep1 { +        epos = lep1; +      } +      if lamp as i32 != 0 && ramp as i32 != 0 { +        while sidx < epos { +          ampl = sdat[(sidx >> 14) as usize] as i16; +          let fresh9 = buf_idx; +          buf_idx = buf_idx.wrapping_add(1); +          let fresh10 = &mut (buf[fresh9 as usize]); +          *fresh10 = (*fresh10 as i32 + ((ampl as i32 * lamp as i32) >> 2)) as i16; +          let fresh11 = buf_idx; +          buf_idx = buf_idx.wrapping_add(1); +          let fresh12 = &mut (buf[fresh11 as usize]); +          *fresh12 = (*fresh12 as i32 + ((ampl as i32 * ramp as i32) >> 2)) as i16; +          sidx = sidx.wrapping_add(step); +        } +      } else { +        if ramp != 0 { +          buf_idx = buf_idx.wrapping_add(1); +        } +        while sidx < epos { +          let fresh13 = &mut (buf[buf_idx as usize]); +          *fresh13 = (*fresh13 as i32 + sdat[(sidx >> 14) as usize] as i32 * ampl as i32) as i16; +          buf_idx = buf_idx.wrapping_add(2); +          sidx = sidx.wrapping_add(step); +        } +        buf_idx &= -2_i32 as usize; +      } +    } else { +      buf_idx = buf_end; +      sidx = epos; +    } +  } +  chan.sample_idx = sidx; +} + +/// Get a nice version string. I guess. +pub fn version() -> &'static str { +  consts::MICROMOD_VERSION +} + +/// An error that can happen when trying to initialize micromod +#[derive(Debug)] +pub enum InitError { +  /// Number of channels is incorrect +  ChannelNumIncorrect, +  /// Sampling rate is incorrect (below 8khz?) +  SamplingRateIncorrect, +} + +impl MmC2r<'_> { +  /// Create a new micromod decoder apparatus. +  pub fn new(data: &[u8], sample_rate: isize) -> Result<MmC2r, InitError> { +    let num_channels = match calculate_num_channels(bytemuck::cast_slice(data)) { +      Some(num_channels) => num_channels, +      None => return Err(InitError::ChannelNumIncorrect), +    }; +    if sample_rate < 8000 { +      return Err(InitError::SamplingRateIncorrect); +    } +    let song_length = (data[950] as i32 & 0x7f_i32) as isize; +    let sequence = &data[952..]; +    let pattern_data = &data[1084..]; +    let mut state = MmC2r { +      sample_rate, +      channels: Default::default(), +      src: ModSrc { +        instruments: Vec::new(), +        module_data: bytemuck::cast_slice(data), +        pattern_data, +        sequence, +        num_patterns: Default::default(), +        num_channels, +        song_length, +      }, +      playback: PlaybackState::default(), +    }; +    state.src.num_patterns = calculate_num_patterns(state.src.module_data); +    let mut sample_data_offset = 1084 + state.src.num_patterns * 64 * state.src.num_channels * 4; +    let mut inst_idx = 1; +    // First instrument is an unused dummy instrument +    state.src.instruments.push(Instrument::dummy()); +    while inst_idx < 32 { +      let sample_length = unsigned_short_big_endian(state.src.module_data, inst_idx * 30 + 12) * 2; +      let fine_tune = +        (state.src.module_data[(inst_idx * 30 + 14) as usize] as i32 & 0xf_i32) as isize; +      let fine_tune = ((fine_tune & 0x7) - (fine_tune & 0x8) + 8) as u8; +      let volume = +        (state.src.module_data[(inst_idx * 30 + 15) as usize] as i32 & 0x7f_i32) as isize; +      let volume = (if volume > 64 { 64 } else { volume }) as u8; +      let mut loop_start = unsigned_short_big_endian(state.src.module_data, inst_idx * 30 + 16) * 2; +      let mut loop_length = +        unsigned_short_big_endian(state.src.module_data, inst_idx * 30 + 18) * 2; +      if loop_start + loop_length > sample_length { +        if loop_start / 2 + loop_length <= sample_length { +          loop_start /= 2; +        } else { +          loop_length = sample_length - loop_start; +        } +      } +      if loop_length < 4 { +        loop_start = sample_length; +        loop_length = 0; +      } +      let loop_start = (loop_start << 14) as usize; +      let loop_length = (loop_length << 14) as usize; +      let sample_data = &bytemuck::cast_slice::<u8, i8>(data)[sample_data_offset as usize..]; +      sample_data_offset += sample_length; +      inst_idx += 1; +      state.src.instruments.push(Instrument { +        volume, +        fine_tune, +        loop_start, +        loop_length, +        sample_data, +      }); +    } +    state.playback.c2_rate = (if state.src.num_channels > 4 { +      8363 +    } else { +      8287 +    }) as isize; +    state.playback.gain = (if state.src.num_channels > 4 { 32 } else { 64 }) as isize; +    state.mute_channel(-1); +    micromod_set_position(0, &mut state); +    Ok(state) +  } +  /// Fill a buffer with delicious samples +  pub fn get_audio(&mut self, output_buffer: &mut [i16], mut count: isize) -> bool { +    let mut offset; +    let mut remain; +    let mut chan_idx; +    if self.src.num_channels <= 0 { +      return false; +    } +    offset = 0; +    let mut cnt = true; +    while count > 0 { +      remain = self.playback.tick_len - self.playback.tick_offset; +      if remain > count { +        remain = count; +      } +      chan_idx = 0; +      while chan_idx < self.src.num_channels { +        resample( +          &mut self.channels[chan_idx as usize], +          output_buffer, +          offset, +          remain, +          &self.src.instruments, +        ); +        chan_idx += 1; +      } +      self.playback.tick_offset += remain; +      if self.playback.tick_offset == self.playback.tick_len { +        if sequence_tick(self) { +          cnt = false; +        } +        self.playback.tick_offset = 0; +      } +      offset += remain; +      count -= remain; +    } +    cnt +  } +  /// Calculate the length of the module file... In samples. Presumably. +  pub fn calculate_mod_file_len(&self) -> Option<isize> { +    let module_header = self.src.module_data; +    let mut length; + +    let mut inst_idx; +    let numchan = calculate_num_channels(module_header)?; +    length = 1084 + 4 * numchan * 64 * calculate_num_patterns(module_header); +    inst_idx = 1; +    while inst_idx < 32 { +      length += unsigned_short_big_endian(module_header, inst_idx * 30 + 12) * 2; +      inst_idx += 1; +    } +    Some(length) +  } +  /// Calculate the song duration... Okay. +  pub fn calculate_song_duration(&mut self) -> isize { +    let mut duration; +    duration = 0; +    if self.src.num_channels > 0 { +      micromod_set_position(0, self); +      let mut song_end = false; +      while !song_end { +        duration += self.playback.tick_len; +        song_end = sequence_tick(self); +      } +      micromod_set_position(0, self); +    } +    duration +  } +  /// Mute a channel. +  pub fn mute_channel(&mut self, channel: isize) -> isize { +    let mut chan_idx; +    if channel < 0 { +      chan_idx = 0; +      while chan_idx < self.src.num_channels { +        self.channels[chan_idx as usize].mute = 0; +        chan_idx += 1; +      } +    } else if channel < self.src.num_channels { +      self.channels[channel as usize].mute = 1; +    } +    self.src.num_channels +  } +  /// Set some gainz. +  pub fn set_gain(&mut self, value: isize) { +    self.playback.gain = value; +  } +} + +fn micromod_set_position(mut pos: isize, state: &mut MmC2r) { +  let mut chan_idx; +  let mut chan; +  if state.src.num_channels <= 0 { +    return; +  } +  if pos >= state.src.song_length { +    pos = 0; +  } +  state.playback.break_pattern = pos; +  state.playback.next_row = 0; +  state.playback.tick = 1; +  state.playback.speed = 6; +  set_tempo(125, &mut state.playback.tick_len, state.sample_rate); +  state.playback.pl_channel = -1; +  state.playback.pl_count = state.playback.pl_channel; +  state.playback.random_seed = 0xabcdef; +  chan_idx = 0; +  while chan_idx < state.src.num_channels { +    chan = &mut state.channels[chan_idx as usize]; +    chan.id = chan_idx as u8; +    let fresh15 = &mut chan.assigned; +    *fresh15 = 0; +    chan.instrument = *fresh15; +    chan.volume = 0; +    match chan_idx & 0x3 { +      0 | 3 => { +        chan.panning = 0; +      } +      1 | 2 => { +        chan.panning = 127; +      } +      _ => {} +    } +    chan_idx += 1; +  } +  sequence_tick(state); +  state.playback.tick_offset = 0; +} |