old school music tracker audio backend
1use core::slice;
2use std::{array, ops::IndexMut};
3
4use dasp::sample::ToSample;
5
6pub(crate) mod instrument;
7pub mod playback;
8pub(crate) mod sample;
9
10#[repr(transparent)]
11#[derive(Clone, Copy, Default, Debug, PartialEq)]
12pub struct Frame([f32; 2]);
13
14impl std::ops::AddAssign for Frame {
15 fn add_assign(&mut self, rhs: Self) {
16 *self = *self + rhs;
17 }
18}
19
20impl std::ops::Add for Frame {
21 type Output = Self;
22
23 fn add(self, rhs: Self) -> Self::Output {
24 Self([self.0[0] + rhs.0[0], self.0[1] + rhs.0[1]])
25 }
26}
27
28impl std::ops::Sub for Frame {
29 type Output = Self;
30
31 fn sub(self, rhs: Self) -> Self::Output {
32 Self([self.0[0] - rhs.0[0], self.0[1] - rhs.0[1]])
33 }
34}
35
36impl std::ops::SubAssign for Frame {
37 fn sub_assign(&mut self, rhs: Self) {
38 *self = *self - rhs;
39 }
40}
41
42impl std::ops::MulAssign<f32> for Frame {
43 fn mul_assign(&mut self, rhs: f32) {
44 *self.0.index_mut(0) *= rhs;
45 *self.0.index_mut(1) *= rhs;
46 }
47}
48
49impl std::ops::Mul<f32> for Frame {
50 type Output = Self;
51
52 fn mul(self, rhs: f32) -> Self::Output {
53 Self([self.0[0] * rhs, self.0[1] * rhs])
54 }
55}
56
57impl std::iter::Sum for Frame {
58 fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
59 iter.reduce(|acc, x| acc + x).unwrap_or_default()
60 }
61}
62
63impl From<[f32; 2]> for Frame {
64 fn from(value: [f32; 2]) -> Self {
65 Self(value)
66 }
67}
68
69impl From<f32> for Frame {
70 fn from(value: f32) -> Self {
71 Self([value, value])
72 }
73}
74
75impl Frame {
76 // split into left and right.
77 pub fn split_array<const N: usize>(value: [Frame; N]) -> ([f32; N], [f32; N]) {
78 (
79 array::from_fn(|i| value[i].0[0]),
80 array::from_fn(|i| value[i].0[1]),
81 )
82 }
83
84 pub fn sum_to_mono(self) -> f32 {
85 self.0[0] + self.0[1]
86 }
87
88 pub fn from_mut<'a>(value: &'a mut [f32; 2]) -> &'a mut Self {
89 // SAFETY: lifetime is specified, both mut, Self is repr(transparent).
90 unsafe { std::mem::transmute::<&'a mut [f32; 2], &'a mut Self>(value) }
91 }
92
93 pub fn from_interleaved(value: &[f32]) -> &[Frame] {
94 debug_assert!(value.len().rem_euclid(2) == 0);
95 let len = value.len() / 2;
96 let ptr = value.as_ptr().cast();
97 // SAFETY: keeps the same lifetime and mutability.
98 // [f32; 2] is a valid frame
99 unsafe { slice::from_raw_parts(ptr, len) }
100 }
101
102 pub fn from_ref<'a>(value: &'a [f32; 2]) -> &'a Self {
103 // SAFETY: lifetime is specified, both not mut, Self is repr(transparent).
104 unsafe { std::mem::transmute::<&'a [f32; 2], &'a Self>(value) }
105 }
106
107 pub fn to_sample<S: dasp::sample::FromSample<f32>>(self) -> [S; 2] {
108 [self.0[0].to_sample_(), self.0[1].to_sample_()]
109 }
110
111 pub fn to_raw<'a>(into: &mut [Self]) -> &'a mut [[f32; 2]] {
112 unsafe { std::mem::transmute(into) }
113 }
114
115 // pan laws taken from: https://www.cs.cmu.edu/~music/icm-online/readings/panlaws/index.html
116
117 // /// angle in radians between 0 and 90°
118 // pub fn pan_linear(&mut self, angle: f32) {
119 // self.0[0] *= (std::f32::consts::FRAC_PI_2 - angle) * std::f32::consts::FRAC_2_PI;
120 // self.0[1] *= angle * std::f32::consts::FRAC_2_PI;
121 // }
122
123 /// angle in radians between 0 and 90°
124 pub fn pan_constant_power(&mut self, angle: f32) {
125 self.0[0] *= angle.cos();
126 self.0[1] *= angle.sin();
127 }
128
129 // /// angle in radians between 0 and 90°
130 // pub fn pan_compromise(&mut self, angle: f32) {
131 // self.0[0] *= f32::sqrt(
132 // (std::f32::consts::FRAC_PI_2 - angle) * std::f32::consts::FRAC_2_PI * angle.cos(),
133 // );
134 // self.0[1] *= f32::sqrt(angle * std::f32::consts::FRAC_2_PI * angle.sin());
135 // }
136}