rill_lofi/emulators/
akai_s900.rs1use crate::config::LofiConfig;
2use crate::lofi_processor::LofiProcessor;
3use rill_core::prelude::*;
4
5pub struct AkaiS900Emulator<const BUF_SIZE: usize> {
8 state: NodeState<f32, BUF_SIZE>,
9 id: NodeId,
10 metadata: NodeMetadata,
11 outputs: Vec<Port<f32, BUF_SIZE>>,
12
13 buffer: Vec<f32>,
14 position: f32,
15 pitch: f32,
16 loop_enabled: bool,
17 loop_start: usize,
18 loop_end: usize,
19 lofi: LofiProcessor<BUF_SIZE>,
20}
21
22impl<const BUF_SIZE: usize> AkaiS900Emulator<BUF_SIZE> {
23 pub fn new(_sample_rate: f32) -> Self {
26 let lofi_config = LofiConfig::for_system(crate::config::ClassicSystem::AkaiS900);
27 let id = NodeId(0);
28 let state = NodeState::new(_sample_rate);
29
30 let outputs = vec![Port::output(id, 0, "signal_out")];
31
32 Self {
33 state,
34 id,
35 metadata: NodeMetadata {
36 name: "Akai S900".to_string(),
37
38 type_name: None,
39 category: NodeCategory::Source,
40 description: "Akai S900 sampler emulation".to_string(),
41 author: "Rill Lo-Fi".to_string(),
42 version: "1.0".to_string(),
43 signal_inputs: 0,
44 signal_outputs: 1,
45 control_inputs: 0,
46 control_outputs: 0,
47 clock_inputs: 0,
48 clock_outputs: 0,
49 feedback_ports: 0,
50 parameters: vec![
51 ParamMetadata::new("pitch", ParamType::Float, ParamValue::Float(1.0))
52 .with_description("Playback pitch")
53 .with_range(0.1, 4.0, 0.01)
54 .with_unit("x"),
55 ParamMetadata::new("loop_enabled", ParamType::Bool, ParamValue::Bool(false))
56 .with_description("Enable sample looping"),
57 ],
58 },
59 outputs,
60 buffer: Vec::new(),
61 position: 0.0,
62 pitch: 1.0,
63 loop_enabled: false,
64 loop_start: 0,
65 loop_end: 0,
66 lofi: LofiProcessor::new(lofi_config),
67 }
68 }
69
70 pub fn load_sample(&mut self, samples: &[f32]) {
73 self.buffer = samples.to_vec();
74 self.loop_end = samples.len();
75 }
76
77 pub fn set_pitch(&mut self, pitch: f32) {
79 self.pitch = pitch.clamp(0.1, 4.0);
80 }
81
82 fn generate_sample(&mut self) -> f32 {
83 if self.buffer.is_empty() {
84 return 0.0;
85 }
86
87 if (self.position as usize) >= self.buffer.len() {
88 return 0.0;
89 }
90
91 let sample = if (self.position as usize) < self.buffer.len() - 1 {
92 let idx = self.position.floor() as usize;
93 let frac = self.position.fract();
94 self.buffer[idx] * (1.0 - frac) + self.buffer[idx + 1] * frac
95 } else {
96 self.buffer[self.position as usize]
97 };
98
99 let processed = self.lofi.process_sample(sample);
100
101 self.position += self.pitch;
102
103 if self.loop_enabled && (self.position as usize) >= self.loop_end {
104 self.position = self.loop_start as f32 + (self.position - self.loop_end as f32);
105 }
106
107 processed
108 }
109}
110
111impl<const BUF_SIZE: usize> Node<f32, BUF_SIZE> for AkaiS900Emulator<BUF_SIZE> {
112 fn metadata(&self) -> NodeMetadata {
113 self.metadata.clone()
114 }
115
116 fn node_type_id(&self) -> NodeTypeId {
117 NodeTypeId::of::<Self>()
118 }
119
120 fn init(&mut self, sample_rate: f32) {
121 self.state = NodeState::new(sample_rate);
122 self.lofi.init(sample_rate);
123 }
124
125 fn reset(&mut self) {
126 self.state.reset();
127 self.position = 0.0;
128 self.lofi.reset();
129 }
130
131 fn get_parameter(&self, id: &ParameterId) -> Option<ParamValue> {
132 match id.as_str() {
133 "pitch" => Some(ParamValue::Float(self.pitch)),
134 "loop_enabled" => Some(ParamValue::Bool(self.loop_enabled)),
135 _ => None,
136 }
137 }
138
139 fn set_parameter(&mut self, id: &ParameterId, value: ParamValue) -> ProcessResult<()> {
140 match id.as_str() {
141 "pitch" => {
142 if let ParamValue::Float(v) = value {
143 self.pitch = v.clamp(0.1, 4.0);
144 Ok(())
145 } else {
146 Err(ProcessError::parameter("pitch must be a float"))
147 }
148 }
149 "loop_enabled" => {
150 if let ParamValue::Bool(v) = value {
151 self.loop_enabled = v;
152 Ok(())
153 } else {
154 Err(ProcessError::parameter("loop_enabled must be a bool"))
155 }
156 }
157 _ => Err(ProcessError::parameter(format!(
158 "Unknown parameter: {}",
159 id
160 ))),
161 }
162 }
163
164 fn id(&self) -> NodeId {
165 self.id
166 }
167 fn set_id(&mut self, id: NodeId) {
168 self.id = id;
169 }
170
171 fn input_port(&self, _index: usize) -> Option<&Port<f32, BUF_SIZE>> {
172 None
173 }
174 fn input_port_mut(&mut self, _index: usize) -> Option<&mut Port<f32, BUF_SIZE>> {
175 None
176 }
177
178 fn output_port(&self, index: usize) -> Option<&Port<f32, BUF_SIZE>> {
179 self.outputs.get(index)
180 }
181
182 fn output_port_mut(&mut self, index: usize) -> Option<&mut Port<f32, BUF_SIZE>> {
183 self.outputs.get_mut(index)
184 }
185
186 fn control_port(&self, _index: usize) -> Option<&Port<f32, BUF_SIZE>> {
187 None
188 }
189 fn control_port_mut(&mut self, _index: usize) -> Option<&mut Port<f32, BUF_SIZE>> {
190 None
191 }
192
193 fn state(&self) -> &NodeState<f32, BUF_SIZE> {
194 &self.state
195 }
196 fn state_mut(&mut self) -> &mut NodeState<f32, BUF_SIZE> {
197 &mut self.state
198 }
199
200 fn num_signal_inputs(&self) -> usize {
201 0
202 }
203 fn num_signal_outputs(&self) -> usize {
204 1
205 }
206}
207
208impl<const BUF_SIZE: usize> Source<f32, BUF_SIZE> for AkaiS900Emulator<BUF_SIZE> {
209 fn generate(
210 &mut self,
211 _ctx: &RenderContext,
212 _control_inputs: &[f32],
213 _clock_inputs: &[RenderContext],
214 _tick: &ClockTick,
215 ) -> ProcessResult<()> {
216 for i in 0..BUF_SIZE {
217 self.outputs[0].write()[i] = self.generate_sample();
218 }
219 Ok(())
220 }
221}