aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock7
-rw-r--r--Cargo.toml1
-rwxr-xr-xbuild2
-rw-r--r--build.rs12
-rw-r--r--greetings.txt14
-rw-r--r--src/font.rs10
-rw-r--r--src/interpolation.rs9
-rw-r--r--src/main.rs3
-rw-r--r--src/render.rs273
-rw-r--r--src/vendored/micromod-rs/ATTRIBUTION1
-rw-r--r--src/vendored/micromod-rs/COPYING37
-rw-r--r--src/vendored/micromod-rs/consts.rs11
-rw-r--r--src/vendored/micromod-rs/mod.rs860
-rw-r--r--src/vendored/mod.rs2
14 files changed, 1109 insertions, 133 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 2c7a638..0ca15e4 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -15,6 +15,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
+name = "bytemuck"
+version = "1.21.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ef657dfab802224e671f5818e9a4935f9b1957ed18e58292690cc39e7a4092a3"
+
+[[package]]
name = "byteorder"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -48,6 +54,7 @@ dependencies = [
name = "cosin-2025-invite-deck"
version = "0.1.0"
dependencies = [
+ "bytemuck",
"rand",
"rusttype",
"sdl2",
diff --git a/Cargo.toml b/Cargo.toml
index 001ba33..e52fd22 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -4,6 +4,7 @@ version = "0.1.0"
edition = "2021"
[dependencies]
+bytemuck = "1.21.0"
rand = "0.8.5"
sdl2 = { version = "0.37.0" }
diff --git a/build b/build
index 3c2d560..0dff0a9 100755
--- a/build
+++ b/build
@@ -1,4 +1,4 @@
#!/bin/sh
set -eax
-RUSTFLAGS="-Zfmt-debug=none -Zlocation-detail=none" cargo +nightly build -Z build-std=std,panic_abort -Z build-std-features="optimize_for_size" -Z build-std-features=panic_immediate_abort -r
+RUSTFLAGS="-Zlocation-detail=none" cargo +nightly build -Z build-std=std,panic_abort -Z build-std-features="optimize_for_size" -Z build-std-features=panic_immediate_abort -r
upx --brute target/release/cosin-2025-invite-deck
diff --git a/build.rs b/build.rs
index 9292e1e..438cde8 100644
--- a/build.rs
+++ b/build.rs
@@ -51,7 +51,7 @@ impl FontMetadata {
);
image[0] = (width & 0b0000_0000_1111_1111) as u8;
- image[1] = (width & 0b1111_1111_0000_0000 << 8) as u8;
+ image[1] = (width & (0b1111_1111_0000_0000 << 8)) as u8;
if (image[0] as u16) | (image[1] as u16 >> 8) != width as u16 {
panic!("Width missmatch!");
}
@@ -78,7 +78,7 @@ impl FontMetadata {
if i >= 0 {
if i as u16 == width {
i = 0;
- image2.push('\n' as u8);
+ image2.push(b'\n');
}
let v = format!("{:x?}", bit);
let v = if v.len() == 1 { format!("0{}", v) } else { v };
@@ -178,11 +178,9 @@ pub const FONT{}: {name}Struct = {name}Struct{{
fn main() -> Result<(), Box<dyn std::error::Error>> {
fs::create_dir_all("src/generated")?;
- let mut modrs = format!(
- "// Copyright is a sham this is a @generated file.
+ let mut modrs = "// Copyright is a sham this is a @generated file.
use crate::font::BakedFont;
-"
- );
+".to_string();
let fonts = [
FontMetadata {
name: "Galmuri",
@@ -205,6 +203,6 @@ use crate::font::BakedFont;
);
exec(font)?;
}
- File::create(&"src/generated/fonts.rs")?.write_all(modrs.as_bytes())?;
+ File::create("src/generated/fonts.rs")?.write_all(modrs.as_bytes())?;
Ok(())
}
diff --git a/greetings.txt b/greetings.txt
new file mode 100644
index 0000000..fbd8e30
--- /dev/null
+++ b/greetings.txt
@@ -0,0 +1,14 @@
+greetings to use, as discussed with uwuspace creatures & /dev/urandom @ mountainbytes directly:
+- Venty
+- Erdit
+- Kaede
+- Deja
+- dui
+- vimja
+- cy
+- alu
+- sashu
+- expired bread
+- gaben
+
+DELETE THIS FILE BEFORE RELEASING THE SOURCE CODE!
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 {