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
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
use std::fmt::{self, Formatter};
use std::time::Duration;

use serde::de::{self, Deserializer, Unexpected, Visitor};
use serde::{Deserialize, Serialize, Serializer};

use crate::model::TypeAudioFeatures;

/// Information and features of a track.
///
/// See [the Spotify Web API
/// reference](https://developer.spotify.com/documentation/web-api/reference/object-model/#audio-features-object)
/// for more details on each on the items.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[allow(missing_docs)]
pub struct AudioFeatures {
    /// The [Spotify ID](https://developer.spotify.com/documentation/web-api/#spotify-uris-and-ids)
    /// for the track.
    pub id: String,
    /// The length of the track.
    #[serde(rename = "duration_ms", with = "serde_millis")]
    pub duration: Duration,
    pub acousticness: f64,
    pub danceability: f64,
    pub energy: f64,
    pub instrumentalness: f64,
    pub key: u32,
    pub liveness: f64,
    pub loudness: f64,
    pub mode: Mode,
    pub speechiness: f64,
    pub tempo: f64,
    pub time_signature: u32,
    pub valence: f64,
    /// The item type; `audio_features`.
    #[serde(rename = "type")]
    pub item_type: TypeAudioFeatures,
}

/// The mode of a track (major or minor).
#[derive(Debug, Clone, PartialEq, Eq, Copy, Hash)]
pub enum Mode {
    /// The track is major.
    Major,
    /// The track is minor.
    Minor,
}

struct ModeVisitor;

impl<'de> Visitor<'de> for ModeVisitor {
    type Value = Mode;
    fn expecting(&self, f: &mut Formatter) -> fmt::Result {
        f.write_str("a mode which is 0 (minor) or 1 (major)")
    }
    fn visit_u64<E: de::Error>(self, v: u64) -> Result<Self::Value, E> {
        match v {
            0 => Ok(Mode::Minor),
            1 => Ok(Mode::Major),
            _ => Err(E::invalid_value(Unexpected::Unsigned(v), &self)),
        }
    }
}

impl<'de> Deserialize<'de> for Mode {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        deserializer.deserialize_u64(ModeVisitor)
    }
}

impl Serialize for Mode {
    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
        match self {
            Self::Major => serializer.serialize_u64(1),
            Self::Minor => serializer.serialize_u64(0),
        }
    }
}

mod serde_mode_opt {
    use super::{Mode, ModeVisitor};
    use serde::{
        de::{self, Visitor},
        Deserializer, Serialize, Serializer,
    };
    use std::fmt::{self, Formatter};
    use std::u64;

    struct ModeOptVisitor;
    impl<'de> Visitor<'de> for ModeOptVisitor {
        type Value = Option<Mode>;
        fn expecting(&self, f: &mut Formatter) -> fmt::Result {
            f.write_str("-1 or a mode")
        }
        fn visit_i64<E: de::Error>(self, v: i64) -> Result<Self::Value, E> {
            match v {
                -1 => Ok(None),
                _ => self.visit_u64(u64::MAX),
            }
        }
        fn visit_u64<E: de::Error>(self, v: u64) -> Result<Self::Value, E> {
            ModeVisitor.visit_u64(v).map(Some)
        }
    }

    pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<Mode>, D::Error>
    where
        D: Deserializer<'de>,
    {
        deserializer.deserialize_i64(ModeOptVisitor)
    }

    #[allow(clippy::trivially_copy_pass_by_ref)]
    pub fn serialize<S: Serializer>(v: &Option<Mode>, serializer: S) -> Result<S::Ok, S::Error> {
        match v {
            Some(mode) => mode.serialize(serializer),
            None => serializer.serialize_i64(-1),
        }
    }
}

mod serde_key_opt {
    use serde::{
        de::{self, Unexpected, Visitor},
        Deserializer, Serializer,
    };
    use std::convert::TryInto;
    use std::fmt::{self, Formatter};

    struct KeyOptVisitor;
    impl<'de> Visitor<'de> for KeyOptVisitor {
        type Value = Option<u32>;
        fn expecting(&self, f: &mut Formatter) -> fmt::Result {
            f.write_str("-1 or a key")
        }
        fn visit_i64<E: de::Error>(self, v: i64) -> Result<Self::Value, E> {
            match v {
                -1 => Ok(None),
                _ => Err(E::invalid_value(Unexpected::Signed(v), &self)),
            }
        }
        fn visit_u64<E: de::Error>(self, v: u64) -> Result<Self::Value, E> {
            match v {
                0..=11 => Ok(Some(v.try_into().unwrap())),
                _ => Err(E::invalid_value(Unexpected::Unsigned(v), &self)),
            }
        }
    }

    pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<u32>, D::Error>
    where
        D: Deserializer<'de>,
    {
        deserializer.deserialize_i64(KeyOptVisitor)
    }

    #[allow(clippy::trivially_copy_pass_by_ref)]
    pub fn serialize<S: Serializer>(v: &Option<u32>, serializer: S) -> Result<S::Ok, S::Error> {
        match v {
            Some(v) => serializer.serialize_u32(*v),
            None => serializer.serialize_i32(-1),
        }
    }
}

/// Audio analysis of a track.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct AudioAnalysis {
    /// The time intervals of bars throughout the track. A bar is a segment of time defined as a
    /// given number of beats. Bar offsets also indicate downbeats, the first beat of a bar.
    pub bars: Vec<TimeInterval>,
    /// The time intervals of beats throughout the track. A beat is the basic time unit of a piece
    /// of music; for example, each tick of a metronome. Beats are typically multiples of tatums.
    pub beats: Vec<TimeInterval>,
    /// A tatum represents the lowest regular pulse train that a listener intuitively infers from
    /// the timing of perceived musical events (segments). For more information about tatums, see
    /// Rhythm (below).
    pub tatums: Vec<TimeInterval>,
    /// Sections are defined by large variations in rhythm or timbre, e.g. chorus, verse, bridge,
    /// guitar solo, etc. Each section contains its own descriptions of tempo, key, mode,
    /// time_signature, and loudness.
    pub sections: Vec<Section>,
    /// Audio segments attempts to subdivide a track into many segments, with each segment
    /// containing a roughly consistent sound throughout its duration.
    pub segments: Vec<Segment>,
}

/// A time interval in a track.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct TimeInterval {
    /// The starting point of the time interval.
    #[serde(with = "crate::util::serde_duration_secs")]
    pub start: Duration,
    /// The duration of the time interval.
    #[serde(with = "crate::util::serde_duration_secs")]
    pub duration: Duration,
    /// The confidence, from 0 to 1, of the reliability of the interval.
    pub confidence: f64,
}

/// A section of a track.
///
/// See
/// [here](https://developer.spotify.com/documentation/web-api/reference/tracks/get-audio-analysis/#section-object)
/// for more information.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[allow(missing_docs)]
pub struct Section {
    /// The interval of the section.
    #[serde(flatten)]
    pub interval: TimeInterval,
    pub loudness: f64,
    pub tempo: f64,
    pub tempo_confidence: f64,
    #[serde(with = "serde_key_opt")]
    pub key: Option<u32>,
    pub key_confidence: f64,
    #[serde(with = "serde_mode_opt")]
    pub mode: Option<Mode>,
    pub mode_confidence: f64,
    pub time_signature: u32,
    pub time_signature_confidence: f64,
}

/// A segment in a track.
///
/// See
/// [here](https://developer.spotify.com/documentation/web-api/reference/tracks/get-audio-analysis/#segment-object)
/// for more information.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[allow(missing_docs)]
pub struct Segment {
    /// The interval of the segment.
    #[serde(flatten)]
    pub interval: TimeInterval,
    pub loudness_start: f64,
    pub loudness_max: f64,
    pub loudness_max_time: f64,
    pub pitches: Vec<f64>,
    pub timbre: Vec<f64>,
}