rytm_rs/object/sound/
machine.rs

1// All casts in this file are intended or safe within the context of this library.
2//
3// One can change `allow` to `warn` to review them if necessary.
4#![allow(
5    clippy::cast_lossless,
6    clippy::cast_possible_truncation,
7    clippy::cast_sign_loss
8)]
9// TODO: I'm currently really lazy to write errors doc for plock impls. Will do it later..
10#![allow(clippy::missing_errors_doc)]
11
12/// Bd Acoustic machine parameters.
13mod bd_acoustic;
14/// Bd Classic machine parameters.
15mod bd_classic;
16/// Bd Fm machine parameters.
17mod bd_fm;
18/// Bd Hard machine parameters.
19mod bd_hard;
20/// Bd Plastic machine parameters.
21mod bd_plastic;
22/// Bd Sharp machine parameters.
23mod bd_sharp;
24/// Bd Silky machine parameters.
25mod bd_silky;
26/// Bt Classic machine parameters.
27mod bt_classic;
28/// Cb Classic machine parameters.
29mod cb_classic;
30/// Cb Metallic machine parameters.
31mod cb_metallic;
32/// Ch Classic machine parameters.
33mod ch_classic;
34/// Ch Metallic machine parameters.
35mod ch_metallic;
36/// Cp Classic machine parameters.
37mod cp_classic;
38/// Cy Classic machine parameters.
39mod cy_classic;
40/// Cy Metallic machine parameters.
41mod cy_metallic;
42/// Cy Ride machine parameters.
43mod cy_ride;
44/// Hh Basic machine parameters.
45mod hh_basic;
46/// Hh Lab machine parameters.
47mod hh_lab;
48/// Oh Classic machine parameters.
49mod oh_classic;
50/// Oh Metallic machine parameters.
51mod oh_metallic;
52/// Rs Classic machine parameters.
53mod rs_classic;
54/// Rs Hard machine parameters.
55mod rs_hard;
56/// Sd Acoustic machine parameters.
57mod sd_acoustic;
58/// Sd Classic machine parameters.
59mod sd_classic;
60/// Sd Fm machine parameters.
61mod sd_fm;
62/// Sd Hard machine parameters.
63mod sd_hard;
64/// Sd Natural machine parameters.
65mod sd_natural;
66/// Sy Chip machine parameters.
67mod sy_chip;
68/// Sy Dual Vco machine parameters.
69mod sy_dual_vco;
70/// Sy Raw machine parameters.
71mod sy_raw;
72/// Ut Impulse machine parameters.
73mod ut_impulse;
74/// Ut Noise machine parameters.
75mod ut_noise;
76/// Xt Classic machine parameters.
77mod xt_classic;
78
79use super::types::MachineType;
80use crate::{
81    error::{ParameterError, RytmError},
82    object::pattern::plock::ParameterLockPool,
83};
84pub use bd_acoustic::*;
85pub use bd_classic::*;
86pub use bd_fm::*;
87pub use bd_hard::*;
88pub use bd_plastic::*;
89pub use bd_sharp::*;
90pub use bd_silky::*;
91pub use bt_classic::*;
92pub use cb_classic::*;
93pub use cb_metallic::*;
94pub use ch_classic::*;
95pub use ch_metallic::*;
96pub use cp_classic::*;
97pub use cy_classic::*;
98pub use cy_metallic::*;
99pub use cy_ride::*;
100pub use hh_basic::*;
101pub use hh_lab::*;
102pub use oh_classic::*;
103pub use oh_metallic::*;
104use parking_lot::Mutex;
105pub use rs_classic::*;
106pub use rs_hard::*;
107use rytm_rs_macro::parameter_range;
108use rytm_sys::ar_sound_t;
109pub use sd_acoustic::*;
110pub use sd_classic::*;
111pub use sd_fm::*;
112pub use sd_hard::*;
113pub use sd_natural::*;
114use serde::{Deserialize, Serialize};
115use std::sync::Arc;
116pub use sy_chip::*;
117pub use sy_dual_vco::*;
118pub use sy_raw::*;
119pub use ut_impulse::*;
120pub use ut_noise::*;
121pub use xt_classic::*;
122
123/// Machine parameters of a sound.
124///
125/// Every machine has distinct parameters and ranges for those parameters.
126///
127/// Not every machine can be assigned to every track.
128#[derive(Debug, Clone, Serialize, Deserialize)]
129pub enum MachineParameters {
130    BdHard(BdHardParameters),
131    BdClassic(BdClassicParameters),
132    SdHard(SdHardParameters),
133    SdClassic(SdClassicParameters),
134    RsHard(RsHardParameters),
135    RsClassic(RsClassicParameters),
136    CpClassic(CpClassicParameters),
137    BtClassic(BtClassicParameters),
138    XtClassic(XtClassicParameters),
139    ChClassic(ChClassicParameters),
140    OhClassic(OhClassicParameters),
141    CyClassic(CyClassicParameters),
142    CbClassic(CbClassicParameters),
143    BdFm(BdFmParameters),
144    SdFm(SdFmParameters),
145    UtNoise(UtNoiseParameters),
146    UtImpulse(UtImpulseParameters),
147    ChMetallic(ChMetallicParameters),
148    OhMetallic(OhMetallicParameters),
149    CyMetallic(CyMetallicParameters),
150    CbMetallic(CbMetallicParameters),
151    BdPlastic(BdPlasticParameters),
152    BdSilky(BdSilkyParameters),
153    SdNatural(SdNaturalParameters),
154    HhBasic(HhBasicParameters),
155    CyRide(CyRideParameters),
156    BdSharp(BdSharpParameters),
157    Disable,
158    SyDualVco(SyDualVcoParameters),
159    SyChip(SyChipParameters),
160    BdAcoustic(BdAcousticParameters),
161    SdAcoustic(SdAcousticParameters),
162    SyRaw(SyRawParameters),
163    HhLab(HhLabParameters),
164    Unset,
165}
166
167impl Default for MachineParameters {
168    fn default() -> Self {
169        Self::BdHard(BdHardParameters::default())
170    }
171}
172
173impl From<MachineType> for MachineParameters {
174    fn from(machine_type: MachineType) -> Self {
175        match machine_type {
176            MachineType::BdHard => Self::BdHard(BdHardParameters::default()),
177            MachineType::BdClassic => Self::BdClassic(BdClassicParameters::default()),
178            MachineType::SdHard => Self::SdHard(SdHardParameters::default()),
179            MachineType::SdClassic => Self::SdClassic(SdClassicParameters::default()),
180            MachineType::RsHard => Self::RsHard(RsHardParameters::default()),
181            MachineType::RsClassic => Self::RsClassic(RsClassicParameters::default()),
182            MachineType::CpClassic => Self::CpClassic(CpClassicParameters::default()),
183            MachineType::BtClassic => Self::BtClassic(BtClassicParameters::default()),
184            MachineType::XtClassic => Self::XtClassic(XtClassicParameters::default()),
185            MachineType::ChClassic => Self::ChClassic(ChClassicParameters::default()),
186            MachineType::OhClassic => Self::OhClassic(OhClassicParameters::default()),
187            MachineType::CyClassic => Self::CyClassic(CyClassicParameters::default()),
188            MachineType::CbClassic => Self::CbClassic(CbClassicParameters::default()),
189            MachineType::BdFm => Self::BdFm(BdFmParameters::default()),
190            MachineType::SdFm => Self::SdFm(SdFmParameters::default()),
191            MachineType::UtNoise => Self::UtNoise(UtNoiseParameters::default()),
192            MachineType::UtImpulse => Self::UtImpulse(UtImpulseParameters::default()),
193            MachineType::ChMetallic => Self::ChMetallic(ChMetallicParameters::default()),
194            MachineType::OhMetallic => Self::OhMetallic(OhMetallicParameters::default()),
195            MachineType::CyMetallic => Self::CyMetallic(CyMetallicParameters::default()),
196            MachineType::CbMetallic => Self::CbMetallic(CbMetallicParameters::default()),
197            MachineType::BdPlastic => Self::BdPlastic(BdPlasticParameters::default()),
198            MachineType::BdSilky => Self::BdSilky(BdSilkyParameters::default()),
199            MachineType::SdNatural => Self::SdNatural(SdNaturalParameters::default()),
200            MachineType::HhBasic => Self::HhBasic(HhBasicParameters::default()),
201            MachineType::CyRide => Self::CyRide(CyRideParameters::default()),
202            MachineType::BdSharp => Self::BdSharp(BdSharpParameters::default()),
203            MachineType::Disable => Self::Disable,
204            MachineType::SyDualVco => Self::SyDualVco(SyDualVcoParameters::default()),
205            MachineType::SyChip => Self::SyChip(SyChipParameters::default()),
206            MachineType::BdAcoustic => Self::BdAcoustic(BdAcousticParameters::default()),
207            MachineType::SdAcoustic => Self::SdAcoustic(SdAcousticParameters::default()),
208            MachineType::SyRaw => Self::SyRaw(SyRawParameters::default()),
209            MachineType::HhLab => Self::HhLab(HhLabParameters::default()),
210            MachineType::Unset => Self::Unset,
211        }
212    }
213}
214
215impl MachineParameters {
216    #[parameter_range(range = "track_index[opt]:0..=11")]
217    pub(crate) fn try_from_raw_sound(
218        raw_sound: &ar_sound_t,
219        track_index: Option<usize>,
220    ) -> Result<Self, RytmError> {
221        let machine_type: MachineType = raw_sound.machine_type.try_into()?;
222        match machine_type {
223            MachineType::BdHard => Ok(Self::BdHard(BdHardParameters::from_raw_sound(
224                raw_sound,
225                track_index,
226            )?)),
227            MachineType::BdClassic => Ok(Self::BdClassic(BdClassicParameters::from_raw_sound(
228                raw_sound,
229                track_index,
230            )?)),
231            MachineType::BdAcoustic => Ok(Self::BdAcoustic(BdAcousticParameters::from_raw_sound(
232                raw_sound,
233                track_index,
234            )?)),
235            MachineType::BdFm => Ok(Self::BdFm(BdFmParameters::from_raw_sound(
236                raw_sound,
237                track_index,
238            )?)),
239            MachineType::BdPlastic => Ok(Self::BdPlastic(BdPlasticParameters::from_raw_sound(
240                raw_sound,
241                track_index,
242            )?)),
243            MachineType::BdSilky => Ok(Self::BdSilky(BdSilkyParameters::from_raw_sound(
244                raw_sound,
245                track_index,
246            )?)),
247            MachineType::BdSharp => Ok(Self::BdSharp(BdSharpParameters::from_raw_sound(
248                raw_sound,
249                track_index,
250            )?)),
251            MachineType::BtClassic => Ok(Self::BtClassic(BtClassicParameters::from_raw_sound(
252                raw_sound,
253                track_index,
254            )?)),
255            MachineType::CbClassic => Ok(Self::CbClassic(CbClassicParameters::from_raw_sound(
256                raw_sound,
257                track_index,
258            )?)),
259            MachineType::CbMetallic => Ok(Self::CbMetallic(CbMetallicParameters::from_raw_sound(
260                raw_sound,
261                track_index,
262            )?)),
263            MachineType::ChClassic => Ok(Self::ChClassic(ChClassicParameters::from_raw_sound(
264                raw_sound,
265                track_index,
266            )?)),
267            MachineType::ChMetallic => Ok(Self::ChMetallic(ChMetallicParameters::from_raw_sound(
268                raw_sound,
269                track_index,
270            )?)),
271            MachineType::CpClassic => Ok(Self::CpClassic(CpClassicParameters::from_raw_sound(
272                raw_sound,
273                track_index,
274            )?)),
275            MachineType::CyClassic => Ok(Self::CyClassic(CyClassicParameters::from_raw_sound(
276                raw_sound,
277                track_index,
278            )?)),
279            MachineType::CyMetallic => Ok(Self::CyMetallic(CyMetallicParameters::from_raw_sound(
280                raw_sound,
281                track_index,
282            )?)),
283            MachineType::CyRide => Ok(Self::CyRide(CyRideParameters::from_raw_sound(
284                raw_sound,
285                track_index,
286            )?)),
287            MachineType::HhBasic => Ok(Self::HhBasic(HhBasicParameters::from_raw_sound(
288                raw_sound,
289                track_index,
290            )?)),
291            MachineType::HhLab => Ok(Self::HhLab(HhLabParameters::from_raw_sound(
292                raw_sound,
293                track_index,
294            )?)),
295            MachineType::OhClassic => Ok(Self::OhClassic(OhClassicParameters::from_raw_sound(
296                raw_sound,
297                track_index,
298            )?)),
299            MachineType::OhMetallic => Ok(Self::OhMetallic(OhMetallicParameters::from_raw_sound(
300                raw_sound,
301                track_index,
302            )?)),
303            MachineType::RsClassic => Ok(Self::RsClassic(RsClassicParameters::from_raw_sound(
304                raw_sound,
305                track_index,
306            )?)),
307            MachineType::RsHard => Ok(Self::RsHard(RsHardParameters::from_raw_sound(
308                raw_sound,
309                track_index,
310            )?)),
311            MachineType::Disable => Ok(Self::Disable),
312            MachineType::Unset => Ok(Self::Unset),
313            MachineType::SdAcoustic => Ok(Self::SdAcoustic(SdAcousticParameters::from_raw_sound(
314                raw_sound,
315                track_index,
316            )?)),
317            MachineType::SdClassic => Ok(Self::SdClassic(SdClassicParameters::from_raw_sound(
318                raw_sound,
319                track_index,
320            )?)),
321            MachineType::SdFm => Ok(Self::SdFm(SdFmParameters::from_raw_sound(
322                raw_sound,
323                track_index,
324            )?)),
325            MachineType::SdHard => Ok(Self::SdHard(SdHardParameters::from_raw_sound(
326                raw_sound,
327                track_index,
328            )?)),
329            MachineType::SdNatural => Ok(Self::SdNatural(SdNaturalParameters::from_raw_sound(
330                raw_sound,
331                track_index,
332            )?)),
333            MachineType::SyChip => Ok(Self::SyChip(SyChipParameters::from_raw_sound(
334                raw_sound,
335                track_index,
336            )?)),
337            MachineType::SyDualVco => Ok(Self::SyDualVco(SyDualVcoParameters::from_raw_sound(
338                raw_sound,
339                track_index,
340            )?)),
341            MachineType::SyRaw => Ok(Self::SyRaw(SyRawParameters::from_raw_sound(
342                raw_sound,
343                track_index,
344            )?)),
345            MachineType::UtImpulse => Ok(Self::UtImpulse(UtImpulseParameters::from_raw_sound(
346                raw_sound,
347                track_index,
348            )?)),
349            MachineType::UtNoise => Ok(Self::UtNoise(UtNoiseParameters::from_raw_sound(
350                raw_sound,
351                track_index,
352            )?)),
353            MachineType::XtClassic => Ok(Self::XtClassic(XtClassicParameters::from_raw_sound(
354                raw_sound,
355                track_index,
356            )?)),
357        }
358    }
359
360    /// Returns the default machine parameters for a given track.
361    ///
362    /// Range `0..=11`
363    #[parameter_range(range = "track_index:0..=11")]
364    pub fn try_default_for_track(track_index: usize) -> Result<Self, RytmError> {
365        Ok(match track_index {
366            0 => Self::BdHard(BdHardParameters::default()),
367            1 => Self::SdHard(SdHardParameters::default()),
368            2 => Self::RsHard(RsHardParameters::default()),
369            3 => Self::CpClassic(CpClassicParameters::default()),
370            4 => Self::BtClassic(BtClassicParameters::default()),
371            5 => Self::XtClassic(XtClassicParameters::default_for_lt()),
372            6 => Self::XtClassic(XtClassicParameters::default_for_mt()),
373            7 => Self::XtClassic(XtClassicParameters::default_for_ht()),
374            8 => Self::ChClassic(ChClassicParameters::default()),
375            9 => Self::OhClassic(OhClassicParameters::default()),
376            10 => Self::CyClassic(CyClassicParameters::default()),
377            11 => Self::CbClassic(CbClassicParameters::default()),
378            _ => unreachable!("This can never happened the range is pre-checked."),
379        })
380    }
381
382    pub(crate) fn apply_to_raw_sound(&self, raw_sound: &mut ar_sound_t) {
383        match self {
384            Self::BdHard(bd_hard) => bd_hard.apply_to_raw_sound(raw_sound),
385            Self::BdClassic(bd_classic) => bd_classic.apply_to_raw_sound(raw_sound),
386            Self::BdAcoustic(bd_acoustic) => bd_acoustic.apply_to_raw_sound(raw_sound),
387            Self::BdFm(bd_fm) => bd_fm.apply_to_raw_sound(raw_sound),
388            Self::BdPlastic(bd_plastic) => bd_plastic.apply_to_raw_sound(raw_sound),
389            Self::BdSilky(bd_silky) => bd_silky.apply_to_raw_sound(raw_sound),
390            Self::BdSharp(bd_sharp) => bd_sharp.apply_to_raw_sound(raw_sound),
391            Self::BtClassic(bt_classic) => bt_classic.apply_to_raw_sound(raw_sound),
392            Self::CbClassic(cb_classic) => cb_classic.apply_to_raw_sound(raw_sound),
393            Self::CbMetallic(cb_metallic) => cb_metallic.apply_to_raw_sound(raw_sound),
394            Self::ChClassic(ch_classic) => ch_classic.apply_to_raw_sound(raw_sound),
395            Self::ChMetallic(ch_metallic) => ch_metallic.apply_to_raw_sound(raw_sound),
396            Self::CpClassic(cp_classic) => cp_classic.apply_to_raw_sound(raw_sound),
397            Self::CyClassic(cy_classic) => cy_classic.apply_to_raw_sound(raw_sound),
398            Self::CyMetallic(cy_metallic) => cy_metallic.apply_to_raw_sound(raw_sound),
399            Self::CyRide(cy_ride) => cy_ride.apply_to_raw_sound(raw_sound),
400            Self::HhBasic(hh_basic) => hh_basic.apply_to_raw_sound(raw_sound),
401            Self::HhLab(hh_lab) => hh_lab.apply_to_raw_sound(raw_sound),
402            Self::OhClassic(oh_classic) => oh_classic.apply_to_raw_sound(raw_sound),
403            Self::OhMetallic(oh_metallic) => oh_metallic.apply_to_raw_sound(raw_sound),
404            Self::RsClassic(rs_classic) => rs_classic.apply_to_raw_sound(raw_sound),
405            Self::RsHard(rs_hard) => rs_hard.apply_to_raw_sound(raw_sound),
406            Self::Disable => {
407                // Empirical knowledge:
408                //
409                // These are the parameters which are sent from the rytm when a sound is queried and the machine is disabled.
410                raw_sound.synth_param_1 = crate::util::to_s_u16_t_union_a(16384);
411                raw_sound.synth_param_2 = crate::util::to_s_u16_t_union_a(0);
412                raw_sound.synth_param_3 = crate::util::to_s_u16_t_union_a(6400);
413                raw_sound.synth_param_4 = crate::util::to_s_u16_t_union_a(6400);
414                raw_sound.synth_param_5 = crate::util::to_s_u16_t_union_a(0);
415                raw_sound.synth_param_6 = crate::util::to_s_u16_t_union_a(12800);
416                raw_sound.synth_param_7 = crate::util::to_s_u16_t_union_a(0);
417                raw_sound.synth_param_8 = crate::util::to_s_u16_t_union_a(0);
418            }
419            Self::SdAcoustic(sd_acoustic) => sd_acoustic.apply_to_raw_sound(raw_sound),
420            Self::SdClassic(sd_classic) => sd_classic.apply_to_raw_sound(raw_sound),
421            Self::SdFm(sd_fm) => sd_fm.apply_to_raw_sound(raw_sound),
422            Self::SdHard(sd_hard) => sd_hard.apply_to_raw_sound(raw_sound),
423            Self::SdNatural(sd_natural) => sd_natural.apply_to_raw_sound(raw_sound),
424            Self::SyChip(sy_chip) => sy_chip.apply_to_raw_sound(raw_sound),
425            Self::SyDualVco(sy_dual_vco) => sy_dual_vco.apply_to_raw_sound(raw_sound),
426            Self::SyRaw(sy_raw) => sy_raw.apply_to_raw_sound(raw_sound),
427            Self::UtImpulse(ut_impulse) => ut_impulse.apply_to_raw_sound(raw_sound),
428            Self::UtNoise(ut_noise) => ut_noise.apply_to_raw_sound(raw_sound),
429            Self::XtClassic(xt_classic) => xt_classic.apply_to_raw_sound(raw_sound),
430            Self::Unset => unreachable!("If you encounter this error, please report it to the maintainer. It means a machine can be unset."),
431        }
432    }
433
434    pub(crate) fn link_parameter_lock_pool(
435        &mut self,
436        parameter_lock_pool: Arc<Mutex<ParameterLockPool>>,
437    ) {
438        match self {
439            Self::BdHard(bd_hard) => bd_hard.link_parameter_lock_pool(parameter_lock_pool),
440            Self::BdClassic(bd_classic) => bd_classic.link_parameter_lock_pool(parameter_lock_pool),
441            Self::BdAcoustic(bd_acoustic) => {
442                bd_acoustic.link_parameter_lock_pool(parameter_lock_pool);
443            }
444            Self::BdFm(bd_fm) => bd_fm.link_parameter_lock_pool(parameter_lock_pool),
445            Self::BdPlastic(bd_plastic) => bd_plastic.link_parameter_lock_pool(parameter_lock_pool),
446            Self::BdSilky(bd_silky) => bd_silky.link_parameter_lock_pool(parameter_lock_pool),
447            Self::BdSharp(bd_sharp) => bd_sharp.link_parameter_lock_pool(parameter_lock_pool),
448            Self::BtClassic(bt_classic) => bt_classic.link_parameter_lock_pool(parameter_lock_pool),
449            Self::CbClassic(cb_classic) => cb_classic.link_parameter_lock_pool(parameter_lock_pool),
450            Self::CbMetallic(cb_metallic) => {
451                cb_metallic.link_parameter_lock_pool(parameter_lock_pool);
452            }
453            Self::ChClassic(ch_classic) => ch_classic.link_parameter_lock_pool(parameter_lock_pool),
454            Self::ChMetallic(ch_metallic) => {
455                ch_metallic.link_parameter_lock_pool(parameter_lock_pool);
456            }
457            Self::CpClassic(cp_classic) => cp_classic.link_parameter_lock_pool(parameter_lock_pool),
458            Self::CyClassic(cy_classic) => cy_classic.link_parameter_lock_pool(parameter_lock_pool),
459            Self::CyMetallic(cy_metallic) => {
460                cy_metallic.link_parameter_lock_pool(parameter_lock_pool);
461            }
462            Self::CyRide(cy_ride) => cy_ride.link_parameter_lock_pool(parameter_lock_pool),
463            Self::HhBasic(hh_basic) => hh_basic.link_parameter_lock_pool(parameter_lock_pool),
464            Self::HhLab(hh_lab) => hh_lab.link_parameter_lock_pool(parameter_lock_pool),
465            Self::OhClassic(oh_classic) => oh_classic.link_parameter_lock_pool(parameter_lock_pool),
466            Self::OhMetallic(oh_metallic) => {
467                oh_metallic.link_parameter_lock_pool(parameter_lock_pool);
468            }
469            Self::RsClassic(rs_classic) => rs_classic.link_parameter_lock_pool(parameter_lock_pool),
470            Self::RsHard(rs_hard) => rs_hard.link_parameter_lock_pool(parameter_lock_pool),
471            Self::SdAcoustic(sd_acoustic) => {
472                sd_acoustic.link_parameter_lock_pool(parameter_lock_pool);
473            }
474            Self::SdClassic(sd_classic) => sd_classic.link_parameter_lock_pool(parameter_lock_pool),
475            Self::SdFm(sd_fm) => sd_fm.link_parameter_lock_pool(parameter_lock_pool),
476            Self::SdHard(sd_hard) => sd_hard.link_parameter_lock_pool(parameter_lock_pool),
477            Self::SdNatural(sd_natural) => sd_natural.link_parameter_lock_pool(parameter_lock_pool),
478            Self::SyChip(sy_chip) => sy_chip.link_parameter_lock_pool(parameter_lock_pool),
479            Self::SyDualVco(sy_dual_vco) => {
480                sy_dual_vco.link_parameter_lock_pool(parameter_lock_pool);
481            }
482            Self::SyRaw(sy_raw) => sy_raw.link_parameter_lock_pool(parameter_lock_pool),
483            Self::UtImpulse(ut_impulse) => ut_impulse.link_parameter_lock_pool(parameter_lock_pool),
484            Self::UtNoise(ut_noise) => ut_noise.link_parameter_lock_pool(parameter_lock_pool),
485            Self::XtClassic(xt_classic) => xt_classic.link_parameter_lock_pool(parameter_lock_pool),
486            Self::Unset | Self::Disable => {
487                // Ignore
488            }
489        }
490    }
491}