1use std::{
2 error::Error,
3 fmt::{self, Display, Formatter},
4 hash::Hash,
5 sync::mpsc::{self, Receiver, Sender},
6};
7
8use mpsc::SendError;
9use oxisynth::{MidiEvent, OxiError, SettingsError, SynthDescriptor, Tuning};
10use tune::{
11 note::Note,
12 pitch::{Pitch, Ratio},
13 tuner::{AotTuner, GroupBy, JitTuner, PoolingMode, TunableSynth},
14 tuning::KeyboardMapping,
15};
16
17pub use oxisynth;
18pub use tune;
19
20pub fn create_aot<K>(
25 desc: SynthDescriptor,
26 per_semitone_polyphony: u8,
27) -> Result<(Xenth, AotXenthControl<K>), SettingsError> {
28 let (xenth, tuners) =
29 create_internal(desc, per_semitone_polyphony, |synth| AotTuner::start(synth))?;
30 Ok((xenth, AotXenthControl { tuners }))
31}
32
33pub fn create_jit<K>(
38 desc: SynthDescriptor,
39 per_semitone_polyphony: u8,
40) -> Result<(Xenth, JitXenthControl<K>), SettingsError> {
41 let (xenth, tuners) = create_internal(desc, per_semitone_polyphony, |synth| {
42 JitTuner::start(synth, PoolingMode::Stop)
43 })?;
44 Ok((xenth, JitXenthControl { tuners }))
45}
46
47pub fn create<K>(
52 desc: SynthDescriptor,
53 per_semitone_polyphony: u8,
54) -> Result<(Xenth, Vec<TunableFluid>), SettingsError> {
55 let (xenth, tuners) = create_internal(desc, per_semitone_polyphony, |synth| synth)?;
56 Ok((xenth, tuners))
57}
58
59fn create_internal<T, C: FnMut(TunableFluid) -> T>(
60 mut desc: SynthDescriptor,
61 polyphony: u8,
62 mut xenth_control_creator: C,
63) -> Result<(Xenth, Vec<T>), SettingsError> {
64 desc.drums_channel_active = false;
65 let synth = oxisynth::Synth::new(desc)?;
66
67 let (sender, receiver) = mpsc::channel();
68
69 let tuners = (0..synth.count_midi_channels())
70 .collect::<Vec<_>>()
71 .chunks_exact(usize::from(polyphony))
72 .map(|chunk| {
73 xenth_control_creator(TunableFluid {
74 sender: sender.clone(),
75 offset: chunk[0],
76 polyphony: usize::from(polyphony),
77 })
78 })
79 .collect();
80
81 let xenth = Xenth { synth, receiver };
82
83 Ok((xenth, tuners))
84}
85
86pub struct Xenth {
88 synth: oxisynth::Synth,
89 receiver: Receiver<Command>,
90}
91
92impl Xenth {
93 pub fn synth(&self) -> &oxisynth::Synth {
95 &self.synth
96 }
97
98 pub fn synth_mut(&mut self) -> &mut oxisynth::Synth {
102 &mut self.synth
103 }
104
105 pub fn read(&mut self) -> Result<impl FnMut() -> (f32, f32) + '_, OxiError> {
107 for command in self.receiver.try_iter() {
108 command(&mut self.synth)?;
109 }
110 Ok(|| self.synth.read_next())
111 }
112
113 pub fn write(
115 &mut self,
116 len: usize,
117 mut write_callback: impl FnMut((f32, f32)),
118 ) -> Result<(), OxiError> {
119 let mut samples = self.read()?;
120 for _ in 0..len {
121 write_callback(samples());
122 }
123 Ok(())
124 }
125}
126
127pub struct AotXenthControl<K> {
129 tuners: Vec<AotTuner<K, TunableFluid>>,
130}
131
132impl<K: Copy + Eq + Hash> AotXenthControl<K> {
133 pub fn set_tuning(
135 &mut self,
136 xen_channel: u8,
137 tuning: impl KeyboardMapping<K>,
138 keys: impl IntoIterator<Item = K>,
139 ) -> Result<usize, SendCommandResult> {
140 self.get_tuner(xen_channel).set_tuning(tuning, keys)
141 }
142
143 pub fn note_on(&mut self, xen_channel: u8, key: K, velocity: u8) -> SendCommandResult {
145 self.get_tuner(xen_channel).note_on(key, velocity)
146 }
147
148 pub fn note_off(&mut self, xen_channel: u8, key: K) -> SendCommandResult {
150 self.get_tuner(xen_channel).note_off(key, 0)
151 }
152
153 pub fn key_pressure(&mut self, xen_channel: u8, key: K, pressure: u8) -> SendCommandResult {
155 self.get_tuner(xen_channel).note_attr(key, pressure)
156 }
157
158 pub fn send_command(
164 &mut self,
165 xen_channel: u8,
166 command: impl FnMut(&mut oxisynth::Synth, u8) -> Result<(), OxiError> + Send + 'static,
167 ) -> SendCommandResult {
168 self.get_tuner(xen_channel).global_attr(Box::new(command))
169 }
170
171 fn get_tuner(&mut self, xen_channel: u8) -> &mut AotTuner<K, TunableFluid> {
172 &mut self.tuners[usize::from(xen_channel)]
173 }
174}
175
176pub struct JitXenthControl<K> {
178 tuners: Vec<JitTuner<K, TunableFluid>>,
179}
180
181impl<K: Copy + Eq + Hash> JitXenthControl<K> {
182 pub fn note_on(
186 &mut self,
187 xen_channel: u8,
188 key: K,
189 pitch: Pitch,
190 velocity: u8,
191 ) -> SendCommandResult {
192 self.get_tuner(xen_channel).note_on(key, pitch, velocity)
193 }
194
195 pub fn note_off(&mut self, xen_channel: u8, key: K) -> SendCommandResult {
197 self.get_tuner(xen_channel).note_off(key, 0)
198 }
199
200 pub fn key_pressure(&mut self, xen_channel: u8, key: K, pressure: u8) -> SendCommandResult {
202 self.get_tuner(xen_channel).note_attr(key, pressure)
203 }
204
205 pub fn send_command(
211 &mut self,
212 xen_channel: u8,
213 command: impl FnMut(&mut oxisynth::Synth, u8) -> Result<(), OxiError> + Send + 'static,
214 ) -> SendCommandResult {
215 self.get_tuner(xen_channel).global_attr(Box::new(command))
216 }
217
218 fn get_tuner(&mut self, xen_channel: u8) -> &mut JitTuner<K, TunableFluid> {
219 &mut self.tuners[usize::from(xen_channel)]
220 }
221}
222
223pub struct TunableFluid {
225 sender: Sender<Command>,
226 offset: usize,
227 polyphony: usize,
228}
229
230impl TunableSynth for TunableFluid {
231 type Result = SendCommandResult;
232 type NoteAttr = u8;
233 type GlobalAttr = ChannelCommand;
234
235 fn num_channels(&self) -> usize {
236 self.polyphony
237 }
238
239 fn group_by(&self) -> GroupBy {
240 GroupBy::Note
241 }
242
243 fn notes_detune(
244 &mut self,
245 channel: usize,
246 detuned_notes: &[(Note, Ratio)],
247 ) -> SendCommandResult {
248 let channel = self.get_channel(channel);
249 let mut detunings = Vec::new();
250
251 for &(detuned_note, detuning) in detuned_notes {
252 if let Some(detuned_note) = detuned_note.checked_midi_number() {
253 detunings.push((
254 u32::from(detuned_note),
255 Ratio::from_semitones(detuned_note)
256 .stretched_by(detuning)
257 .as_cents(),
258 ));
259 }
260 }
261
262 let mut tuning = Tuning::new(0, 0); self.send_command(move |s| {
264 tuning.tune_notes(&detunings);
265 s.channel_set_tuning(channel, tuning)
266 })
267 }
268
269 fn note_on(&mut self, channel: usize, started_note: Note, velocity: u8) -> SendCommandResult {
270 if let Some(started_note) = started_note.checked_midi_number() {
271 let channel = self.get_channel(channel);
272 self.send_command(move |s| {
273 s.send_event(MidiEvent::NoteOn {
274 channel,
275 key: started_note,
276 vel: velocity,
277 })
278 })?;
279 }
280 Ok(())
281 }
282
283 fn note_off(&mut self, channel: usize, stopped_note: Note, _velocity: u8) -> SendCommandResult {
284 if let Some(stopped_note) = stopped_note.checked_midi_number() {
285 let channel = self.get_channel(channel);
286 self.send_command(move |s| {
287 s.send_event(MidiEvent::NoteOff {
288 channel,
289 key: stopped_note,
290 })
291 })?;
292 }
293 Ok(())
294 }
295
296 fn note_attr(
297 &mut self,
298 channel: usize,
299 affected_note: Note,
300 pressure: u8,
301 ) -> SendCommandResult {
302 if let Some(affected_note) = affected_note.checked_midi_number() {
303 let channel = self.get_channel(channel);
304 self.send_command(move |s| {
305 s.send_event(MidiEvent::PolyphonicKeyPressure {
306 channel,
307 key: affected_note,
308 value: pressure,
309 })
310 })?;
311 }
312 Ok(())
313 }
314
315 fn global_attr(&mut self, mut command: ChannelCommand) -> SendCommandResult {
316 let channels = (self.get_channel(0)..).take(self.polyphony);
317 self.send_command(move |s| {
318 for channel in channels {
319 command(s, channel)?;
320 }
321 Ok(())
322 })
323 }
324}
325
326impl TunableFluid {
327 fn send_command(
328 &self,
329 command: impl FnOnce(&mut oxisynth::Synth) -> Result<(), OxiError> + Send + 'static,
330 ) -> SendCommandResult {
331 Ok(self.sender.send(Box::new(command))?)
332 }
333
334 fn get_channel(&self, channel: usize) -> u8 {
335 u8::try_from(self.offset + channel).unwrap()
336 }
337}
338
339pub type SendCommandResult = Result<(), SendCommandError>;
340
341#[derive(Copy, Clone, Debug)]
345pub struct SendCommandError;
346
347impl Display for SendCommandError {
348 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
349 write!(
350 f,
351 "The receiving Xenth instance has been torn down. Is the audio thread still alive?"
352 )
353 }
354}
355
356impl<T> From<SendError<T>> for SendCommandError {
357 fn from(_: SendError<T>) -> Self {
358 SendCommandError
359 }
360}
361
362impl Error for SendCommandError {}
363
364pub type ChannelCommand = Box<dyn FnMut(&mut oxisynth::Synth, u8) -> Result<(), OxiError> + Send>;
365
366type Command = Box<dyn FnOnce(&mut oxisynth::Synth) -> Result<(), OxiError> + Send>;