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