1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
use std::{cmp::Ordering, num::NonZeroU32};

use crate::section::hit_objects::hit_samples::{HitSampleInfo, HitSampleInfoName, SampleBank};

#[derive(Clone, Debug, PartialEq)]
/// Audio-related info about this control point.
pub struct SamplePoint {
    pub time: f64,
    pub sample_bank: SampleBank,
    pub sample_volume: i32,
    pub custom_sample_bank: i32,
}

impl SamplePoint {
    pub const DEFAULT_SAMPLE_BANK: SampleBank = SampleBank::Normal;
    pub const DEFAULT_SAMPLE_VOLUME: i32 = 100;
    pub const DEFAULT_CUSTOM_SAMPLE_BANK: i32 = 0;

    pub fn new(
        time: f64,
        sample_bank: SampleBank,
        sample_volume: i32,
        custom_sample_bank: i32,
    ) -> Self {
        Self {
            time,
            sample_bank,
            sample_volume: sample_volume.clamp(0, 100),
            custom_sample_bank,
        }
    }

    pub fn is_redundant(&self, existing: &Self) -> bool {
        self.sample_bank == existing.sample_bank
            && self.sample_volume == existing.sample_volume
            && self.custom_sample_bank == existing.custom_sample_bank
    }

    pub fn apply(&self, sample: &mut HitSampleInfo) {
        if matches!(sample.name, HitSampleInfoName::Default(_)) {
            if sample.custom_sample_bank == 0 {
                sample.custom_sample_bank = self.custom_sample_bank;

                if sample.custom_sample_bank >= 2 {
                    // SAFETY: The value is guaranteed to be >= 2
                    sample.suffix = Some(unsafe {
                        NonZeroU32::new_unchecked(sample.custom_sample_bank as u32)
                    });
                }
            }

            if sample.volume == 0 {
                sample.volume = self.sample_volume.clamp(0, 100);
            }

            if !sample.bank_specified {
                sample.bank = self.sample_bank;
                sample.bank_specified = true;
            }
        } else {
            sample.bank = SamplePoint::DEFAULT_SAMPLE_BANK;
            sample.suffix = None;

            if sample.volume == 0 {
                sample.volume = self.sample_volume.clamp(0, 100);
            }

            sample.custom_sample_bank = 1;
            sample.bank_specified = false;
            sample.is_layered = false;
        }
    }
}

impl Default for SamplePoint {
    fn default() -> Self {
        Self {
            time: 0.0,
            sample_bank: Self::DEFAULT_SAMPLE_BANK,
            sample_volume: Self::DEFAULT_SAMPLE_VOLUME,
            custom_sample_bank: Self::DEFAULT_CUSTOM_SAMPLE_BANK,
        }
    }
}

impl PartialOrd for SamplePoint {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        self.time.partial_cmp(&other.time)
    }
}