osu_file_parser/osu_file/events/audio_sample/
mod.rs1pub mod error;
2
3pub use error::*;
4use nom::{
5 branch::alt,
6 bytes::complete::tag,
7 combinator::{complete, eof, map_res},
8 error::context,
9 sequence::{preceded, tuple},
10 Parser,
11};
12
13use crate::{
14 osu_file::{
15 FilePath, Integer, InvalidRepr, Version, VersionedDefault, VersionedFrom,
16 VersionedFromRepr, VersionedFromStr, VersionedToString, VersionedTryFrom,
17 },
18 parsers::{comma, comma_field, comma_field_type},
19};
20
21#[derive(Clone, Debug, PartialEq, Eq, Hash)]
22pub struct AudioSample {
23 pub time: Integer,
24 pub layer: Layer,
25 pub filepath: FilePath,
26 pub volume: Volume,
27}
28
29impl VersionedFromStr for AudioSample {
30 type Err = ParseAudioSampleError;
31
32 fn from_str(s: &str, version: Version) -> Result<Option<Self>, Self::Err> {
33 if s.is_empty() {
34 return Err(ParseAudioSampleError::WrongEvent);
35 }
36
37 let header = context(ParseAudioSampleError::WrongEvent.into(), tag("Sample"));
38 let time = context(
39 ParseAudioSampleError::InvalidTime.into(),
40 comma_field_type(),
41 );
42 let layer = context(
43 ParseAudioSampleError::InvalidLayer.into(),
44 map_res(
45 context(
46 ParseAudioSampleError::InvalidLayer.into(),
47 comma_field_type(),
48 ),
49 |layer| Layer::from_repr(layer, version).map(|layer| layer.unwrap()),
51 ),
52 );
53 let filepath = comma_field().map(|p| p.into());
54 let volume = alt((
55 eof.map(|_| Volume::default(version).unwrap()),
56 preceded(
57 context(ParseAudioSampleError::MissingVolume.into(), comma()),
58 context(
59 ParseAudioSampleError::InvalidVolume.into(),
60 map_res::<_, _, _, _, ParseVolumeError, _, _>(comma_field(), |s| {
61 Ok(Volume::from_str(s, version)?.unwrap())
62 }),
63 ),
64 ),
65 ));
66
67 let (_, (time, layer, filepath, volume)) = tuple((
68 preceded(
69 tuple((
70 complete(header),
71 context(ParseAudioSampleError::MissingTime.into(), comma()),
72 )),
73 time,
74 ),
75 preceded(
76 context(ParseAudioSampleError::MissingLayer.into(), comma()),
77 layer,
78 ),
79 preceded(
80 context(ParseAudioSampleError::MissingFilepath.into(), comma()),
81 filepath,
82 ),
83 volume,
84 ))(s)?;
85
86 Ok(Some(AudioSample {
87 time,
88 layer,
89 filepath,
90 volume,
91 }))
92 }
93}
94
95impl VersionedToString for AudioSample {
96 fn to_string(&self, version: Version) -> Option<String> {
97 Some(format!(
98 "Sample,{},{},{},{}",
99 self.time,
100 self.layer as usize,
101 self.filepath.to_string(version).unwrap(),
102 self.volume.to_string(version).unwrap()
103 ))
104 }
105}
106
107#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
108pub struct Volume(u8);
109
110impl VersionedDefault for Volume {
111 fn default(_: Version) -> Option<Self> {
112 Some(Volume(100))
113 }
114}
115
116impl Volume {
117 pub fn new(volume: u8, version: Version) -> Result<Volume, VolumeSetError> {
118 <Volume as VersionedTryFrom<u8>>::try_from(volume, version).map(|volume| volume.unwrap())
119 }
120
121 pub fn get(&self) -> u8 {
122 self.0
123 }
124
125 pub fn set(&mut self, value: u8, version: Version) -> Result<(), VolumeSetError> {
126 *self = <Volume as VersionedTryFrom<u8>>::try_from(value, version)
127 .map(|volume| volume.unwrap())?;
128
129 Ok(())
130 }
131}
132
133impl VersionedTryFrom<u8> for Volume {
134 type Error = VolumeSetError;
135
136 fn try_from(value: u8, _: Version) -> Result<Option<Self>, Self::Error> {
137 if value > 100 {
138 Err(VolumeSetError::VolumeTooHigh)
139 } else {
140 Ok(Some(Volume(value)))
141 }
142 }
143}
144
145impl VersionedFrom<Volume> for u8 {
146 fn from(volume: Volume, _: Version) -> Option<Self> {
147 Some(volume.get())
148 }
149}
150
151impl VersionedFromStr for Volume {
152 type Err = ParseVolumeError;
153
154 fn from_str(s: &str, version: Version) -> Result<Option<Self>, Self::Err> {
155 <Volume as VersionedTryFrom<u8>>::try_from(s.parse::<u8>()?, version)
156 .map_err(|err| err.into())
157 }
158}
159
160impl VersionedToString for Volume {
161 fn to_string(&self, version: Version) -> Option<String> {
162 <u8 as VersionedFrom<Volume>>::from(*self, version).map(|volume| volume.to_string())
163 }
164}
165
166#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
167#[non_exhaustive]
168pub enum Layer {
169 Background,
170 Fail,
171 Pass,
172 Foreground,
173}
174
175impl VersionedFromRepr for Layer {
176 fn from_repr(repr: usize, _: Version) -> Result<Option<Self>, InvalidRepr> {
177 match repr {
178 0 => Ok(Some(Layer::Background)),
179 1 => Ok(Some(Layer::Fail)),
180 2 => Ok(Some(Layer::Pass)),
181 3 => Ok(Some(Layer::Foreground)),
182 _ => Err(InvalidRepr),
183 }
184 }
185}