1use serde::{Deserialize, Serialize};
2
3#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq)]
5pub enum ClassicSystem {
6 Nes,
8 Commodore64,
10 SegaGenesis,
12 RolandD50,
14 AkaiS900,
16 EmulatorII,
18 FairlightCMI,
20 LinnDrum,
22 Custom {
24 bit_depth: u8,
26 sample_rate: f32,
28 nonlinear: bool,
30 noise_floor: f32,
32 },
33}
34
35impl ClassicSystem {
36 pub fn get_bit_depth(&self) -> u8 {
38 match self {
39 ClassicSystem::Nes => 7,
40 ClassicSystem::Commodore64 => 8,
41 ClassicSystem::SegaGenesis => 9,
42 ClassicSystem::RolandD50 => 16,
43 ClassicSystem::AkaiS900 => 12,
44 ClassicSystem::EmulatorII => 8,
45 ClassicSystem::FairlightCMI => 8,
46 ClassicSystem::LinnDrum => 8,
47 ClassicSystem::Custom { bit_depth, .. } => *bit_depth,
48 }
49 }
50
51 pub fn get_sample_rate(&self) -> f32 {
53 match self {
54 ClassicSystem::Nes => 44_100.0,
55 ClassicSystem::Commodore64 => 44_100.0,
56 ClassicSystem::SegaGenesis => 44_100.0,
57 ClassicSystem::RolandD50 => 32_000.0,
58 ClassicSystem::AkaiS900 => 40_000.0,
59 ClassicSystem::EmulatorII => 27_700.0,
60 ClassicSystem::FairlightCMI => 16_000.0,
61 ClassicSystem::LinnDrum => 44_100.0,
62 ClassicSystem::Custom { sample_rate, .. } => *sample_rate,
63 }
64 }
65
66 pub fn has_nonlinear_encoding(&self) -> bool {
68 matches!(
69 self,
70 ClassicSystem::AkaiS900
71 | ClassicSystem::Custom {
72 nonlinear: true,
73 ..
74 }
75 )
76 }
77
78 pub fn get_noise_floor_db(&self) -> f32 {
80 match self {
81 ClassicSystem::Nes => -42.0,
82 ClassicSystem::Commodore64 => -48.0,
83 ClassicSystem::AkaiS900 => -72.0,
84 ClassicSystem::FairlightCMI => -48.0,
85 ClassicSystem::Custom { noise_floor, .. } => *noise_floor,
86 _ => -90.0,
87 }
88 }
89}
90
91#[derive(Debug, Clone, Serialize, Deserialize)]
93pub struct HardwareEmulation {
94 pub bit_depth: u8,
96 pub sample_rate: f32,
98 pub dac_nonlinearity: bool,
100 pub clock_drift: f32,
102 pub voltage_drop: f32,
104 pub crosstalk: f32,
106 pub thermal_noise: f32,
108 pub ageing_effect: f32,
110}
111
112impl Default for HardwareEmulation {
113 fn default() -> Self {
114 Self {
115 bit_depth: 8,
116 sample_rate: 44_100.0,
117 dac_nonlinearity: true,
118 clock_drift: 0.1,
119 voltage_drop: 0.02,
120 crosstalk: 0.01,
121 thermal_noise: 0.001,
122 ageing_effect: 0.05,
123 }
124 }
125}
126
127impl HardwareEmulation {
128 pub fn for_system(system: ClassicSystem) -> Self {
130 let mut emulation = Self::default();
131
132 match system {
133 ClassicSystem::Nes => {
134 emulation.bit_depth = 7;
135 emulation.clock_drift = 0.5;
136 emulation.voltage_drop = 0.05;
137 emulation.thermal_noise = 0.005;
138 }
139 ClassicSystem::Commodore64 => {
140 emulation.bit_depth = 8;
141 emulation.dac_nonlinearity = true;
142 emulation.clock_drift = 0.3;
143 emulation.crosstalk = 0.03;
144 }
145 ClassicSystem::AkaiS900 => {
146 emulation.bit_depth = 12;
147 emulation.dac_nonlinearity = true;
148 emulation.sample_rate = 40_000.0;
149 emulation.thermal_noise = 0.001;
150 }
151 ClassicSystem::FairlightCMI => {
152 emulation.bit_depth = 8;
153 emulation.sample_rate = 16_000.0;
154 emulation.clock_drift = 1.0;
155 emulation.voltage_drop = 0.1;
156 emulation.thermal_noise = 0.01;
157 }
158 _ => {
159 emulation.bit_depth = system.get_bit_depth();
160 emulation.sample_rate = system.get_sample_rate();
161 }
162 }
163
164 emulation
165 }
166}
167
168#[derive(Debug, Clone, Serialize, Deserialize)]
170pub struct LofiConfig {
171 pub system: ClassicSystem,
173 pub hardware: HardwareEmulation,
175 pub enable_bitcrush: bool,
177 pub enable_sr_reduction: bool,
179 pub enable_noise: bool,
181 pub output_gain: f32,
183 pub dc_offset: f32,
188 pub output_ceiling: f32,
193 pub dry_wet: f32,
195}
196
197impl Default for LofiConfig {
198 fn default() -> Self {
199 Self {
200 system: ClassicSystem::Custom {
201 bit_depth: 8,
202 sample_rate: 44_100.0,
203 nonlinear: false,
204 noise_floor: -48.0,
205 },
206 hardware: HardwareEmulation::default(),
207 enable_bitcrush: true,
208 enable_sr_reduction: true,
209 enable_noise: true,
210 output_gain: 1.0,
211 dc_offset: 0.0,
212 output_ceiling: 1.0,
213 dry_wet: 1.0,
214 }
215 }
216}
217
218impl LofiConfig {
219 pub fn for_system(system: ClassicSystem) -> Self {
221 Self {
222 system,
223 hardware: HardwareEmulation::for_system(system),
224 ..Default::default()
225 }
226 }
227}