diff options
feat: Marquee, Lower dither opacity at top, DVD Logo should be separate function, Release Builds should work, advance_width should be f64 to save bytes, Vendor Micromod-RS, Add Timing Steps
Diffstat (limited to 'src')
| -rw-r--r-- | src/font.rs | 10 | ||||
| -rw-r--r-- | src/interpolation.rs | 9 | ||||
| -rw-r--r-- | src/main.rs | 3 | ||||
| -rw-r--r-- | src/render.rs | 273 | ||||
| -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 | ||||
| -rw-r--r-- | src/vendored/mod.rs | 2 | 
9 files changed, 1081 insertions, 125 deletions
| diff --git a/src/font.rs b/src/font.rs index 1dddd20..7041634 100644 --- a/src/font.rs +++ b/src/font.rs @@ -1,5 +1,3 @@ -use std::sync::LazyLock; -  use sdl2::{    pixels::{Color, PixelFormatEnum},    rect::{Point, Rect}, @@ -17,8 +15,8 @@ pub struct RenderableCharacter {    pub data: &'static [u8],    /** The offset to draw the character at */    pub offset: Point, -  /** The amount to advance the x position of the cursor when drawing */ -  pub advance_width: f32, +  /** The amount to advance the x position of the cursor when drawing - f64 due to internally being used as a f64, despite being stored as f32 (only f64 to save some cpu cycles converting f32->f64) */ +  pub advance_width: f64,  }  /**   A trait describing a generated font. @@ -34,9 +32,9 @@ pub trait BakedFont {      let width = u16::from_le_bytes(bytes[0..2].try_into().unwrap());      let offset_x = i32::from_le_bytes(bytes[2..6].try_into().unwrap());      let offset_y = i32::from_le_bytes(bytes[6..10].try_into().unwrap()); -    let advance_width = f32::from_le_bytes(bytes[10..14].try_into().unwrap()); +    let advance_width = f64::from(f32::from_le_bytes(bytes[10..14].try_into().unwrap()));      let data = &bytes[14..]; -    let height = if data.len() == 0 { +    let height = if data.is_empty() {        0      } else {        data.len() as u16 / width diff --git a/src/interpolation.rs b/src/interpolation.rs index ecb2c85..448ef43 100644 --- a/src/interpolation.rs +++ b/src/interpolation.rs @@ -90,7 +90,7 @@ impl<ValueType: Copy> AnimationTrack<ValueType> {    }    fn get_first_last(&self) -> Option<(KeyFrame<ValueType>, KeyFrame<ValueType>)> {      let okf = self.get_sorted_keyframes(); -    if okf.len() == 0 { +    if okf.is_empty() {        None      } else {        Some((*okf.first().unwrap(), *okf.last().unwrap())) @@ -175,14 +175,11 @@ impl<      timing_function: TwoValueTimingFunction,    ) -> Option<ValueType> {      let frames = self.get_current_keyframes(sorted_keyframes, time); -    match frames { -      Some(frames) => Some(KeyFrame::raw_value_at( +    frames.map(|frames| KeyFrame::raw_value_at(          &frames.0,          &frames.1,          time,          timing_function, -      )), -      None => None, -    } +      ))    }  } diff --git a/src/main.rs b/src/main.rs index 9c65333..7ed8df1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,6 +5,7 @@ mod font;  pub mod generated;  mod interpolation;  mod render; +pub mod vendored;  use std::time::{Duration, SystemTime}; @@ -17,6 +18,8 @@ pub fn main() {    let window = &mut video_subsystem      .window("Cosin25 Invite", 512, 256)      .position_centered() +    // works lol: +    // .resizable()      .build()      .unwrap(); diff --git a/src/render.rs b/src/render.rs index dbd3761..bd17e2b 100644 --- a/src/render.rs +++ b/src/render.rs @@ -8,6 +8,31 @@ use sdl2::surface::Surface;  use sdl2::video::{Window, WindowContext};  use std::ops::DerefMut; +fn dvd_logo_offset(t: f64, screen_size_x: f64, screen_size_y: f64) -> (f64, f64) { +  let offset_x = t % (screen_size_x * 2.0); +  let offset_x = if offset_x > screen_size_x { +    screen_size_x * 2.0 - offset_x +  } else { +    offset_x +  }; + +  let offset_y = t % (screen_size_y * 2.0); +  let offset_y = if offset_y > screen_size_y { +    screen_size_y * 2.0 - offset_y +  } else { +    offset_y +  }; + +  (offset_x, offset_y) +} + +const START_UWUSPACE: f64 = 0.0; +const START_BOUNCE: f64 = 0.5; +const START_SIN: f64 = 1.5; +const START_COMETOCOSIN: f64 = 2.5; +const SCENE_GREETINGS: f64 = 20.0; +const JUST_DVD: f64 = 70.0; +  pub fn render(    canvas: &mut Canvas<Window>,    texture_creator: &TextureCreator<WindowContext>, @@ -19,147 +44,169 @@ pub fn render(    } else {      i as u8    }; -  let sin_offset = time_seconds * 0.1;    let win_size = canvas.window().drawable_size();    canvas.set_draw_color(Color::RGB(12, 12, 12));    canvas.clear(); -  let mut offset: f32 = 0.0; +  let is_dvd = time_seconds < SCENE_GREETINGS || time_seconds >= JUST_DVD; -  { -    let bounce_time_offset = 1.5; -    let bounce_speed = 90.0; -    let padding_x = 16.0; -    let padding_y = 16.0; -    let (offset_x, offset_y) = if time_seconds > bounce_time_offset { -      let mut uwu_width = padding_x; -      let mut uwu_height: f32 = padding_y * 2.0; -      for c in "UwU-Space".chars() { -        let char = FONT_CHERRY_BOMB_ONE.get_char(c); -        uwu_width += char.advance_width; -        if f32::from(char.height) > uwu_height { -          uwu_height = char.height.into(); +  if is_dvd { +    let time_seconds = if time_seconds >= JUST_DVD { +      time_seconds - JUST_DVD + 15.0 +    } else { +      time_seconds +    }; +    if time_seconds >= START_UWUSPACE { +      let bounce_speed = 90.0; +      let padding_x = 16.0; +      let padding_y = 16.0; +      let (offset_x, offset_y) = if time_seconds > START_BOUNCE { +        let mut uwu_width = padding_x; +        let mut uwu_height: f32 = padding_y; +        for c in "UwU-Space".chars() { +          let char = FONT_CHERRY_BOMB_ONE.get_char(c); +          uwu_width += char.advance_width; +          let nh = f32::from(char.height) + padding_y; +          if nh > uwu_height { +            uwu_height = nh; +          }          } -      } -      let virtual_screen_size = ( -        f64::from(win_size.0) - f64::from(uwu_width + padding_x), -        f64::from(win_size.1) - f64::from(uwu_height + padding_y), -      ); -      let t = (time_seconds - bounce_time_offset) * bounce_speed; -      let offset_x = t % (virtual_screen_size.0 * 2.0); -      let offset_x = if offset_x > virtual_screen_size.0 { -        virtual_screen_size.0 * 2.0 - offset_x +        let virtual_screen_size = ( +          f64::from(win_size.0) - (uwu_width + padding_x), +          f64::from(win_size.1) - f64::from(uwu_height + padding_y), +        ); +        let t = (time_seconds - START_BOUNCE) * bounce_speed; +        let (offset_x, offset_y) = dvd_logo_offset(t, virtual_screen_size.0, virtual_screen_size.1); +        ( +          (padding_x + offset_x).round() as i32, +          (f64::from(padding_y) + offset_y).round() as i32, +        )        } else { -        offset_x +        (padding_x.round() as i32, padding_y.round() as i32)        }; +      let mut offset: f64 = 0.0; +      for c in "UwU-Space".chars() { +        let char = FONT_CHERRY_BOMB_ONE.get_char(c); +        canvas +          .copy( +            &char +              .to_texture(texture_creator, Color::RGB(i, 64, 255 - i)) +              .unwrap(), +            None, +            char.to_rect(offset as i32 + offset_x, offset_y), +          ) +          .unwrap(); +        offset += char.advance_width; +      } +    } +  } -      let offset_y = t % (virtual_screen_size.1 * 2.0); -      let offset_y = if offset_y > virtual_screen_size.1 { -        virtual_screen_size.1 * 2.0 - offset_y -      } else { -        offset_y -      }; +  if time_seconds >= JUST_DVD { +    // +  } else if time_seconds >= SCENE_GREETINGS { +    let mut offset = 0.0; +    let mut rng = rand::thread_rng(); -      ( -        (f64::from(padding_x) + offset_x).round() as i32, -        (f64::from(padding_y) + offset_y).round() as i32, -      ) -    } else { -      (padding_x.round() as i32, padding_y.round() as i32) -    }; -    for c in "UwU-Space".chars() { -      let char = FONT_CHERRY_BOMB_ONE.get_char(c); +    for c in "sorry for shit invite we have adhd".chars() { +      let char = FONT_GALMURI.get_char(c);        canvas          .copy(            &char -            .to_texture(&texture_creator, Color::RGB(i, 64, 255 - i)) +            .to_texture(texture_creator, Color::RGB(i, 64, 255 - i))              .unwrap(),            None, -          char.to_rect(offset as i32 + offset_x, offset_y), +          char.to_rect( +            offset as i32 + 18 + rng.gen_range(-2..2), +            win_size.1 as i32 - 24 + rng.gen_range(-2..2), +          ),          )          .unwrap();        offset += char.advance_width;      } -  } - -  offset = 0.0; -  let mut rng = rand::thread_rng(); - -  for c in "sorry for shit invite we have adhd".chars() { -    let char = FONT_GALMURI.get_char(c); -    canvas -      .copy( -        &char -          .to_texture(&texture_creator, Color::RGB(i, 64, 255 - i)) -          .unwrap(), -        None, -        char.to_rect( -          offset as i32 + 18 + rng.gen_range(-2..2), -          16 + 36 + rng.gen_range(-2..2), -        ), -      ) -      .unwrap(); -    offset += char.advance_width / 1.0; -  } - -  { -    let mut sin_surface = Surface::new(512, 256, PixelFormatEnum::RGBA32).unwrap(); - -    let w = sin_surface.width(); -    let h = sin_surface.height(); -    let f = &mut sin_surface.deref_mut().without_lock_mut().unwrap(); +  } else { +    if time_seconds >= START_SIN { +      let time_seconds = time_seconds - START_SIN; +      let base_sin_offset = time_seconds * 0.1; +      let sin_offset = base_sin_offset - 0.75; +      let mut sin_surface = Surface::new(win_size.0, win_size.1, PixelFormatEnum::RGBA32).unwrap(); -    for x in 0..w { +      let w = win_size.0; +      let h = win_size.1;        let f64_w = f64::from(w);        let f64_h = f64::from(h); -      let sin_x = { -        let mut sin_x = f64::from(x) + (sin_offset * f64_w); -        if sin_x > f64_w { -          sin_x = sin_x - f64_w; -        } -        sin_x -      }; -      let sin_y = -        ((f64::sin(sin_x * (3.141 * 2.0) / f64_w) + 1.0) * (f64_h / 2.0)).floor() as usize; -      // let sin_idx = (sin_y * w as usize + x as usize) * 4; - -      for y in 0..h { -        let idx = (y * w + x) as usize * 4; -        f[idx] = 122 - (x / 8) as u8; -        f[idx + 1] = 255 - (x / 2) as u8; -        f[idx + 2] = (x / 2) as u8; -        f[idx + 3] = if sin_y < y as usize { -          if idx % 5 == 0 { -            255 -          } else { -            122 +      let f = &mut sin_surface.deref_mut().without_lock_mut().unwrap(); + +      let min_x_pos = (if base_sin_offset > 1.0 { +        0.0 +      } else { +        1.0 - base_sin_offset +      }) * f64_w; + +      for x in 0..w { +        let f64_x = f64::from(x); +        let out_of_frame = f64_x < min_x_pos; +        let sin_x = { +          let mut sin_x = f64_x + (sin_offset * f64_w); +          if sin_x > f64_w { +            sin_x -= f64_w;            } -        } else { -          0 +          sin_x          }; +        let sin_y = +          ((f64::sin(sin_x * (3.141 * 2.0) / f64_w) + 1.0) * (f64_h / 2.0)).floor() as usize; +        // let sin_idx = (sin_y * w as usize + x as usize) * 4; + +        for y in 0..h { +          let idx = (y * w + x) as usize * 4; +          let cx = x * 512 / w; +          f[idx] = (122 - (cx / 8)) as u8; +          f[idx + 1] = (255 - (cx / 2)) as u8; +          f[idx + 2] = (cx / 2) as u8; +          f[idx + 3] = if out_of_frame { +            0 +          } else if sin_y < y as usize { +            let v: u16 = if idx % 5 == 0 { 255 } else { 122 }; +            let v = if (sin_y + 3) < (y as usize) { +              v +            } else { +              v * 2 / 3 +            }; +            v as u8 +          } else { +            0 +          }; +        }        } -    } -    let sin_texture = Texture::from_surface(&sin_surface, &texture_creator).unwrap(); +      let sin_texture = Texture::from_surface(&sin_surface, texture_creator).unwrap(); -    canvas -      .copy(&sin_texture, None, Rect::new(0, 0, win_size.0, win_size.1)) -      .unwrap(); -  } -  offset = 0.0; -  for c in "Come to Cosin25 :3".chars() { -    let char = FONT_GALMURI.get_char(c); -    canvas -      .copy( -        &char -          .to_texture(&texture_creator, Color::RGB(i, 64, 255 - i)) -          .unwrap(), -        None, -        char.to_rect(offset as i32 + 18, win_size.1 as i32 - 32), -      ) -      .unwrap(); -    offset += char.advance_width / 1.1; +      canvas +        .copy(&sin_texture, None, Rect::new(0, 0, win_size.0, win_size.1)) +        .unwrap(); +    } +    if time_seconds >= START_COMETOCOSIN { +      let time_seconds = time_seconds - START_COMETOCOSIN; +      let wrap_width = f64::from(win_size.0); +      let mut offset = (18.0 + (time_seconds * 32.0)) % wrap_width; +      // WARNING: we wrap this! if the text is wider than the window, this whole thing falls apart +      for c in "Come to Cosin25 :3".chars() { +        let char = FONT_GALMURI.get_char(c); +        canvas +          .copy( +            &char +              .to_texture(texture_creator, Color::RGB(i, 64, 255 - i)) +              .unwrap(), +            None, +            char.to_rect(offset as i32, win_size.1 as i32 - 32), +          ) +          .unwrap(); +        offset += char.advance_width; +        if offset > wrap_width { +          offset -= wrap_width; +        } +      } +    }    }  } 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 { |