1pub mod camera;
2pub mod effects;
3mod graph;
4mod ksh;
5pub mod overlaps;
6pub mod parameter;
7pub mod score_ticks;
8mod vox;
9
10use camera::CameraInfo;
11use effects::AudioEffect;
12pub use graph::*;
13pub use ksh::*;
14use serde::de::Visitor;
15use serde::{Deserialize, Serialize};
16use std::collections::HashMap;
17use std::collections::HashSet;
18use std::marker::PhantomData;
19use std::slice::Windows;
20use std::str;
21pub use vox::*;
22
23type Dict<T> = HashMap<String, T>;
24
25#[inline]
26pub fn beat_in_ms(bpm: f64) -> f64 {
27 60_000.0 / bpm
28}
29
30#[inline]
31pub fn tick_in_ms(bpm: f64, ppqn: u32) -> f64 {
32 beat_in_ms(bpm) / ppqn as f64
33}
34
35#[inline]
36pub fn ticks_from_ms(ms: f64, bpm: f64, tpqn: u32) -> f64 {
37 ms / tick_in_ms(bpm, tpqn)
38}
39
40#[inline]
41pub fn ms_from_ticks(ticks: i64, bpm: f64, tpqn: u32) -> f64 {
42 tick_in_ms(bpm, tpqn) * ticks as f64
43}
44
45#[repr(usize)]
46#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy, Serialize, Deserialize)]
47pub enum Side {
48 Left = 0,
49 Right,
50}
51
52impl Side {
53 pub fn iter() -> std::array::IntoIter<Side, 2> {
54 [Self::Left, Self::Right].into_iter()
55 }
56 pub fn opposite(&self) -> Self {
57 match self {
58 Side::Left => Self::Right,
59 Side::Right => Self::Left,
60 }
61 }
62}
63
64#[repr(usize)]
65#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy, Serialize, Deserialize)]
66pub enum BtLane {
67 A = 0,
68 B,
69 C,
70 D,
71}
72
73impl TryFrom<usize> for BtLane {
74 type Error = usize;
75
76 fn try_from(value: usize) -> Result<Self, Self::Error> {
77 match value {
78 0 => Ok(BtLane::A),
79 1 => Ok(BtLane::B),
80 2 => Ok(BtLane::C),
81 3 => Ok(BtLane::D),
82 _ => Err(value),
83 }
84 }
85}
86
87#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)]
88pub enum Track {
89 BT(BtLane),
90 FX(Side),
91 Laser(Side),
92}
93
94#[derive(Serialize, Deserialize)]
95#[serde(untagged)]
96enum SingleOrPair<T> {
97 Single(T),
98 Pair(T, T),
99}
100
101#[derive(Copy, Clone, Default)]
102pub struct GraphPoint {
103 pub y: u32,
104 pub v: f64,
105 pub vf: Option<f64>,
106 pub a: f64,
107 pub b: f64,
108}
109impl<'de> Deserialize<'de> for GraphPoint {
110 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
111 where
112 D: serde::Deserializer<'de>,
113 {
114 struct GpVisitor;
115 impl<'de> Visitor<'de> for GpVisitor {
116 type Value = GraphPoint;
117
118 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
119 formatter.write_str("[u32, f64 | [f64, f64], none | [f64, f64]]")
120 }
121
122 fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
123 where
124 A: serde::de::SeqAccess<'de>,
125 {
126 let y = seq
127 .next_element()?
128 .ok_or_else(|| serde::de::Error::custom("No element"))?;
129 let (v, vf) = match seq
130 .next_element::<SingleOrPair<f64>>()?
131 .ok_or_else(|| serde::de::Error::custom("Missing 2nd element"))?
132 {
133 SingleOrPair::Single(v) => (v, None),
134 SingleOrPair::Pair(v, vf) => (v, Some(vf)),
135 };
136 let (a, b) = if let Some((a, b)) = seq.next_element::<(f64, f64)>()? {
137 (a, b)
138 } else {
139 (0.5, 0.5)
140 };
141
142 Ok(GraphPoint { y, v, vf, a, b })
143 }
144 }
145
146 deserializer.deserialize_seq(GpVisitor)
147 }
148}
149
150impl Serialize for GraphPoint {
151 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
152 where
153 S: serde::Serializer,
154 {
155 use serde::ser::SerializeTuple;
156 let point_len = if (self.a - self.b).abs() > f64::EPSILON {
157 3
158 } else {
159 2
160 };
161
162 let mut top_tup = serializer.serialize_tuple(point_len)?;
163 top_tup.serialize_element(&self.y)?;
164 if let Some(vf) = self.vf {
165 top_tup.serialize_element(&(self.v, vf))?;
166 } else {
167 top_tup.serialize_element(&self.v)?;
168 }
169 if point_len == 3 {
170 top_tup.serialize_element(&(self.a, self.b))?;
171 }
172
173 top_tup.end()
174 }
175}
176
177#[derive(Copy, Clone)]
178pub struct GraphSectionPoint {
179 pub ry: u32,
180 pub v: f64,
181 pub vf: Option<f64>,
182 pub a: f64,
183 pub b: f64,
184}
185
186impl<'de> Deserialize<'de> for GraphSectionPoint {
187 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
188 where
189 D: serde::Deserializer<'de>,
190 {
191 struct GspVisitor;
192 impl<'de> Visitor<'de> for GspVisitor {
193 type Value = GraphSectionPoint;
194
195 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
196 formatter.write_str("[u32, f64 | [f64, f64], none | [f64, f64]]")
197 }
198
199 fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
200 where
201 A: serde::de::SeqAccess<'de>,
202 {
203 let ry = seq
204 .next_element()?
205 .ok_or_else(|| serde::de::Error::custom("No element"))?;
206 let (v, vf) = match seq
207 .next_element::<SingleOrPair<f64>>()?
208 .ok_or_else(|| serde::de::Error::custom("Missing 2nd element"))?
209 {
210 SingleOrPair::Single(v) => (v, None),
211 SingleOrPair::Pair(v, vf) => (v, Some(vf)),
212 };
213 let (a, b) = if let Some((a, b)) = seq.next_element::<(f64, f64)>()? {
214 (a, b)
215 } else {
216 (0.5, 0.5)
217 };
218
219 Ok(GraphSectionPoint { ry, v, vf, a, b })
220 }
221 }
222
223 deserializer.deserialize_seq(GspVisitor)
224 }
225}
226
227impl Serialize for GraphSectionPoint {
228 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
229 where
230 S: serde::Serializer,
231 {
232 use serde::ser::SerializeTuple;
233 let point_len = if (self.a - self.b).abs() > f64::EPSILON {
234 3
235 } else {
236 2
237 };
238
239 let mut top_tup = serializer.serialize_tuple(point_len)?;
240 top_tup.serialize_element(&self.ry)?;
241 if let Some(vf) = self.vf {
242 top_tup.serialize_element(&(self.v, vf))?;
243 } else {
244 top_tup.serialize_element(&self.v)?;
245 }
246 if point_len == 3 {
247 top_tup.serialize_element(&(self.a, self.b))?;
248 }
249
250 top_tup.end()
251 }
252}
253
254pub type ByMeasureIdx<T> = Vec<(u32, T)>;
255
256impl GraphSectionPoint {
257 pub fn new(ry: u32, v: f64) -> Self {
258 GraphSectionPoint {
259 ry,
260 v,
261 vf: None,
262 a: 0.5,
263 b: 0.5,
264 }
265 }
266}
267
268#[derive(Copy, Clone, Debug)]
269pub struct Interval {
270 pub y: u32,
271 pub l: u32,
272}
273
274impl Serialize for Interval {
275 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
276 where
277 S: serde::Serializer,
278 {
279 use serde::ser::SerializeTuple;
280 if self.l == 0 {
281 serializer.serialize_u32(self.y)
282 } else {
283 let mut tup = serializer.serialize_tuple(2)?;
284 tup.serialize_element(&self.y)?;
285 tup.serialize_element(&self.l)?;
286 tup.end()
287 }
288 }
289}
290
291struct IntervalVisitor;
292
293impl<'de> Visitor<'de> for IntervalVisitor {
294 type Value = Interval;
295
296 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
297 formatter.write_str("integer or `y` integer pair [`y`, `l`]")
298 }
299
300 fn visit_i64<E>(self, v: i64) -> Result<Self::Value, E>
301 where
302 E: serde::de::Error,
303 {
304 Ok(Interval { l: 0, y: v as u32 })
305 }
306
307 fn visit_f64<E>(self, v: f64) -> Result<Self::Value, E>
308 where
309 E: serde::de::Error,
310 {
311 Ok(Interval { l: 0, y: v as u32 })
312 }
313
314 fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E>
315 where
316 E: serde::de::Error,
317 {
318 Ok(Interval { l: 0, y: v as u32 })
319 }
320
321 fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
322 where
323 A: serde::de::SeqAccess<'de>,
324 {
325 let y = seq
326 .next_element()?
327 .ok_or_else(|| serde::de::Error::custom("Empty sequence"))?;
328 let l = seq.next_element()?.unwrap_or(0);
329 Ok(Interval { y, l })
330 }
331}
332
333impl<'de> Deserialize<'de> for Interval {
334 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
335 where
336 D: serde::Deserializer<'de>,
337 {
338 deserializer.deserialize_any(IntervalVisitor)
339 }
340}
341
342fn default_zero<T: From<u8>>() -> T {
343 T::from(0)
344}
345
346fn default_true<T: From<bool>>() -> T {
347 T::from(true)
348}
349
350fn serde_eq<T: Into<i64> + Copy, const N: i64>(v: &T) -> bool {
351 N == (*v).into()
352}
353
354#[allow(unused)]
355fn serde_def_n<T: From<u32> + Copy, const N: u32>() -> T {
356 N.into()
357}
358
359#[derive(Serialize, Deserialize, Clone)]
365pub struct LaserSection(
366 pub u32,
367 pub Vec<GraphSectionPoint>,
368 #[serde(
369 default = "default_one::<u8>",
370 skip_serializing_if = "serde_eq::<_, 1>"
371 )]
372 pub u8,
373);
374
375impl LaserSection {
376 pub fn tick(&self) -> u32 {
377 self.0
378 }
379 pub fn segments(&self) -> Windows<GraphSectionPoint> {
380 self.1.windows(2)
381 }
382
383 pub fn last(&self) -> Option<&GraphSectionPoint> {
384 self.1.last()
385 }
386
387 pub fn first(&self) -> Option<&GraphSectionPoint> {
388 self.1.first()
389 }
390
391 pub fn wide(&self) -> u8 {
392 self.2
393 }
394}
395
396pub fn do_curve(x: f64, a: f64, b: f64) -> f64 {
398 let t = if x < f64::EPSILON || a < f64::EPSILON {
399 (a - (a * a + x - 2.0 * a * x).sqrt()) / (-1.0 + 2.0 * a)
400 } else {
401 x / (a + (a * a + (1.0 - 2.0 * a) * x).sqrt())
402 };
403 2.0 * (1.0 - t) * t * b + t * t
404}
405
406fn default_one<T: From<u8>>() -> T {
407 T::from(1)
408}
409
410#[derive(Serialize, Deserialize, Clone)]
411pub struct NoteInfo {
412 pub bt: [Vec<Interval>; 4],
413 pub fx: [Vec<Interval>; 2],
414 pub laser: [Vec<LaserSection>; 2],
415}
416
417impl NoteInfo {
418 fn new() -> NoteInfo {
419 NoteInfo {
420 bt: [Vec::new(), Vec::new(), Vec::new(), Vec::new()],
421 fx: [Vec::new(), Vec::new()],
422 laser: [Vec::new(), Vec::new()],
423 }
424 }
425}
426
427#[derive(Serialize, Deserialize, Clone)]
428pub struct DifficultyInfo {
429 pub name: Option<String>,
430 pub short_name: Option<String>,
431 pub idx: u8,
432}
433
434#[derive(Serialize, Deserialize, Clone)]
435pub struct MetaInfo {
436 pub title: String,
437 #[serde(skip_serializing_if = "Option::is_none")]
438 pub title_img_filename: Option<String>,
439 #[serde(skip_serializing_if = "Option::is_none")]
440 pub subtitle: Option<String>,
441 pub artist: String,
442 pub gauge: Option<GaugeInfo>,
443 #[serde(skip_serializing_if = "Option::is_none")]
444 pub artist_img_filename: Option<String>,
445 pub chart_author: String,
446 pub difficulty: u8,
447 pub level: u8,
448 pub disp_bpm: String,
449 #[serde(skip_serializing_if = "Option::is_none")]
450 pub std_bpm: Option<f64>,
451 pub jacket_filename: String,
452 pub jacket_author: String,
453 #[serde(skip_serializing_if = "Option::is_none")]
454 pub information: Option<String>,
455}
456
457#[derive(Serialize, Deserialize, Clone)]
458pub struct GaugeInfo {
459 pub total: u32,
460}
461
462impl MetaInfo {
463 fn new() -> MetaInfo {
464 MetaInfo {
465 title: String::new(),
466 title_img_filename: None,
467 subtitle: None,
468 gauge: None,
469 artist: String::new(),
470 artist_img_filename: None,
471 chart_author: String::new(),
472 difficulty: 0,
473 level: 1,
474 disp_bpm: String::new(),
475 std_bpm: None,
476 jacket_filename: String::new(),
477 jacket_author: String::new(),
478 information: None,
479 }
480 }
481}
482
483pub type ByPulse<T> = Vec<(u32, T)>;
484#[derive(Copy, Clone, Default, PartialEq)]
485pub struct ByPulseOption<T>(u32, Option<T>);
486
487impl<T> ByPulseOption<T> {
488 pub fn tick(&self) -> u32 {
489 self.0
490 }
491
492 pub fn value(&self) -> Option<&T> {
493 self.1.as_ref()
494 }
495
496 pub fn new(y: u32, v: Option<T>) -> Self {
497 Self(y, v)
498 }
499}
500
501impl<T: Serialize> Serialize for ByPulseOption<T> {
502 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
503 where
504 S: serde::Serializer,
505 {
506 use serde::ser::SerializeTuple;
507 if let Some(v) = &self.1 {
508 let mut tup = serializer.serialize_tuple(2)?;
509 tup.serialize_element(&self.0)?;
510 tup.serialize_element(v)?;
511 tup.end()
512 } else {
513 serializer.serialize_u32(self.0)
514 }
515 }
516}
517
518impl<'de, T: Deserialize<'de>> Deserialize<'de> for ByPulseOption<T> {
519 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
520 where
521 D: serde::Deserializer<'de>,
522 {
523 struct ByPulseOptionVisitor<T>(PhantomData<T>);
524 impl<'de, T: Deserialize<'de>> Visitor<'de> for ByPulseOptionVisitor<T> {
525 type Value = ByPulseOption<T>;
526
527 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
528 formatter.write_str("[`u32`, v] or `u32`")
529 }
530
531 fn visit_i64<E>(self, v: i64) -> Result<Self::Value, E>
532 where
533 E: serde::de::Error,
534 {
535 Ok(ByPulseOption::<T>(v as u32, None))
536 }
537
538 fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E>
539 where
540 E: serde::de::Error,
541 {
542 Ok(ByPulseOption::<T>(v as u32, None))
543 }
544
545 fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
546 where
547 A: serde::de::SeqAccess<'de>,
548 {
549 Ok(ByPulseOption::<T>(
550 seq.next_element()?.unwrap_or_default(),
551 seq.next_element()?,
552 ))
553 }
554 }
555
556 deserializer.deserialize_any(ByPulseOptionVisitor(PhantomData))
557 }
558}
559
560#[derive(Serialize, Deserialize, Copy, Clone)]
561pub struct ByNote<T> {
562 pub y: u32,
563 pub v: Option<T>,
564 #[serde(default = "default_true::<bool>")]
565 pub dom: bool,
566}
567
568#[derive(Serialize, Deserialize, Clone)]
569pub struct ByNotes<T> {
570 pub bt: Option<[Vec<ByNote<T>>; 4]>,
571 pub fx: Option<[Vec<ByNote<T>>; 2]>,
572 pub laser: Option<[Vec<ByNote<T>>; 2]>,
573}
574
575impl<'a, T> IntoIterator for &'a ByNotes<T> {
576 type Item = (&'a ByNote<T>, Track);
577 type IntoIter = ByNotesIter<'a, T>;
578
579 fn into_iter(self) -> Self::IntoIter {
580 ByNotesIter {
581 by_notes: self,
582 indexes: Default::default(),
583 }
584 }
585}
586
587pub struct ByNotesIter<'a, T> {
588 by_notes: &'a ByNotes<T>,
589 indexes: HashMap<Track, usize>,
590}
591
592impl<'a, T> Iterator for ByNotesIter<'a, T> {
593 type Item = (&'a ByNote<T>, Track);
594
595 fn next(&mut self) -> Option<Self::Item> {
596 let mut current_events = HashMap::new();
597
598 if let Some(bt) = &self.by_notes.bt {
599 for (lane, bt) in bt.iter().enumerate() {
600 let bt_lane = match lane {
601 0 => BtLane::A,
602 1 => BtLane::B,
603 2 => BtLane::C,
604 3 => BtLane::D,
605 _ => unreachable!(),
606 };
607
608 let track = Track::BT(bt_lane);
609 let index = self.indexes.entry(track).or_insert(0);
610
611 if let Some(note) = bt.get(*index) {
612 current_events.insert(track, note);
613 }
614 }
615 }
616
617 if let Some(fx) = &self.by_notes.fx {
618 for (lane, fx) in fx.iter().enumerate() {
619 let fx_lane = match lane {
620 0 => Side::Left,
621 1 => Side::Right,
622 _ => unreachable!(),
623 };
624 let track = Track::FX(fx_lane);
625 let index = self.indexes.entry(track).or_insert(0);
626
627 if let Some(note) = fx.get(*index) {
628 current_events.insert(track, note);
629 }
630 }
631 }
632
633 if let Some(laser) = &self.by_notes.laser {
634 for (lane, laser) in laser.iter().enumerate() {
635 let laser_lane = match lane {
636 0 => Side::Left,
637 1 => Side::Right,
638 _ => unreachable!(),
639 };
640 let track = Track::Laser(laser_lane);
641 let index = self.indexes.entry(track).or_insert(0);
642
643 if let Some(note) = laser.get(*index) {
644 current_events.insert(track, note);
645 }
646 }
647 }
648
649 if let Some((track, event)) = current_events.iter().min_by_key(|(_, evt)| evt.y) {
650 self.indexes.entry(*track).and_modify(|i| *i += 1);
651 Some((*event, *track))
652 } else {
653 None
654 }
655 }
656}
657
658#[derive(Serialize, Deserialize, Copy, Clone)]
660pub struct TimeSignature(pub u32, pub u32);
661
662impl TimeSignature {
663 fn from_str(s: &str) -> Self {
665 let mut data = s.split('/');
666 let n: u32 = data.next().unwrap_or("4").parse().unwrap_or(4);
667 let d: u32 = data.next().unwrap_or("4").parse().unwrap_or(4);
668
669 TimeSignature(n, d)
670 }
671}
672
673#[derive(Serialize, Deserialize, Clone)]
674pub struct BeatInfo {
675 pub bpm: ByPulse<f64>,
676 pub time_sig: ByMeasureIdx<TimeSignature>,
677 pub scroll_speed: Vec<GraphPoint>,
678}
679
680pub const KSON_RESOLUTION: u32 = 240;
681
682impl BeatInfo {
683 fn new() -> Self {
684 BeatInfo {
685 bpm: Vec::new(),
686 time_sig: Vec::new(),
687 scroll_speed: Vec::new(),
688 }
689 }
690}
691
692#[derive(Serialize, Deserialize, Clone, Default)]
693pub struct BgmInfo {
694 #[serde(default, skip_serializing_if = "String::is_empty")]
695 pub filename: String,
696 #[serde(default = "default_one::<f64>")]
697 pub vol: f64,
698 #[serde(default = "default_zero::<i32>")]
699 pub offset: i32,
700 pub preview: PreviewInfo,
701 pub legacy: LegacyBgmInfo,
702}
703
704#[derive(Serialize, Deserialize, Clone, Default)]
705pub struct LegacyBgmInfo {
706 pub fp_filenames: Vec<String>,
707}
708
709#[derive(Serialize, Deserialize, Clone, Default)]
710pub struct PreviewInfo {
711 #[serde(default = "default_zero::<u32>")]
712 pub offset: u32,
713 #[serde(default = "default_zero::<u32>")]
714 pub duration: u32,
715 #[serde(skip_serializing_if = "Option::is_none")]
716 pub preview_filename: Option<String>,
717}
718
719impl BgmInfo {
720 fn new() -> Self {
721 BgmInfo {
722 filename: String::new(),
723 vol: 1.0,
724 offset: 0,
725 preview: PreviewInfo::default(),
726 legacy: LegacyBgmInfo::default(),
727 }
728 }
729}
730
731#[derive(Serialize, Deserialize, Clone, Default)]
732pub struct KeySoundInfo {
733 pub fx: KeySoundFXInfo,
734 pub laser: KeySoundLaserInfo,
735}
736
737#[derive(Serialize, Deserialize, Clone, Default)]
738pub struct KeySoundLaserInfo {
739 pub vol: ByPulse<f64>,
740}
741
742#[derive(Serialize, Deserialize, Clone, Default)]
743pub struct KeySoundFXInfo {
744 pub chip_event: HashMap<String, [Vec<ByPulse<KeySoundInvokeFX>>; 2]>,
745}
746
747#[derive(Serialize, Deserialize, Clone)]
748pub struct KeySoundInvokeFX {
749 pub vol: f64,
750}
751
752type NoteParamChange = ByPulseOption<Dict<String>>;
753
754#[derive(Serialize, Deserialize, Clone, Default, PartialEq)]
755pub struct AudioEffectFXInfo {
756 #[serde(default, skip_serializing_if = "HashMap::is_empty")]
757 pub def: Dict<AudioEffect>,
758 #[serde(default, skip_serializing_if = "HashMap::is_empty")]
759 pub param_change: Dict<Dict<ByPulse<String>>>,
760 #[serde(default, skip_serializing_if = "HashMap::is_empty")]
761 pub long_event: Dict<[Vec<NoteParamChange>; 2]>,
762}
763
764#[derive(Serialize, Deserialize, Clone, Default, PartialEq)]
765pub struct AudioEffectLaserInfo {
766 #[serde(default, skip_serializing_if = "HashMap::is_empty")]
767 def: Dict<AudioEffect>,
768 #[serde(default, skip_serializing_if = "HashMap::is_empty")]
769 pub param_change: Dict<Dict<ByPulse<String>>>,
770 #[serde(default, skip_serializing_if = "HashMap::is_empty")]
771 pub pulse_event: Dict<ByPulse<()>>,
772 #[serde(default = "default_zero::<i32>")]
773 pub peaking_filter_delay: i32,
774}
775
776#[derive(Serialize, Deserialize, Clone, Default, PartialEq)]
777pub struct AudioEffectInfo {
778 pub fx: AudioEffectFXInfo,
779 pub laser: AudioEffectLaserInfo,
780}
781
782#[derive(Serialize, Deserialize, Clone, Default)]
783#[serde(default)]
784pub struct AudioInfo {
785 pub bgm: BgmInfo,
786 #[serde(default, skip_serializing_if = "crate::IsDefault::is_default")]
787 pub audio_effect: AudioEffectInfo,
788 #[serde(skip_deserializing)]
789 pub key_sound: KeySoundInfo,
790}
791
792impl AudioInfo {
793 fn new() -> Self {
794 Self::default()
795 }
796}
797
798#[derive(Serialize, Deserialize, Clone)]
799pub struct Chart {
800 pub meta: MetaInfo,
801 pub note: NoteInfo,
802 pub beat: BeatInfo,
803 pub audio: AudioInfo,
804 #[serde(default)]
805 pub camera: camera::CameraInfo,
806 pub version: String,
807 pub bg: BgInfo,
808 #[serde(skip)]
809 pub file_hash: String,
810}
811
812#[derive(Serialize, Deserialize, Clone)]
813pub struct BgInfo {
814 pub filename: Option<String>,
815 #[serde(default)]
816 pub offset: i32,
817 pub legacy: Option<LegacyBgInfo>,
818}
819
820impl BgInfo {
821 pub fn new() -> Self {
822 Self {
823 filename: None,
824 offset: 0,
825 legacy: None,
826 }
827 }
828}
829
830impl Default for BgInfo {
831 fn default() -> Self {
832 Self::new()
833 }
834}
835
836#[derive(Serialize, Deserialize, Clone)]
837pub struct LegacyBgInfo {
838 pub bg: Option<Vec<KshBgInfo>>,
839 pub layer: Option<KshLayerInfo>,
840 pub movie: Option<KshMovieInfo>,
841}
842
843#[derive(Serialize, Deserialize, Clone)]
844pub struct KshLayerInfo {
845 pub filename: Option<String>, #[serde(default)]
852 pub duration: i32,
853 pub rotation: Option<KshLayerRotationInfo>, }
855
856#[derive(Serialize, Deserialize, Clone)]
857pub struct KshLayerRotationInfo {
858 pub tilt: bool, pub spin: bool, }
861#[derive(Serialize, Deserialize, Clone)]
862pub struct KshMovieInfo {
863 pub filename: Option<String>, pub offset: i32, }
866
867#[derive(Serialize, Deserialize, Clone)]
868pub struct KshBgInfo {
869 pub filename: String,
870}
871
872type BeatLineFn = dyn Fn(u32) -> Option<(u32, bool)>;
873pub struct MeasureBeatLines {
874 tick: u32,
875 funcs: Vec<(u32, Box<BeatLineFn>)>,
876 func_index: usize,
877}
878
879impl Iterator for MeasureBeatLines {
880 type Item = (u32, bool);
881
882 fn next(&mut self) -> Option<(u32, bool)> {
883 if let Some(func) = self.funcs.get(self.func_index) {
884 if let Some((new_tick, is_measure)) = func.1(self.tick) {
885 let old_tick = self.tick;
886 self.tick = new_tick;
887 if let Some(next_func) = self.funcs.get(self.func_index + 1) {
888 if self.tick >= next_func.0 {
889 self.func_index += 1;
890 }
891 }
892
893 return Some((old_tick, is_measure));
894 }
895 }
896
897 None
898 }
899}
900
901impl Default for Chart {
902 fn default() -> Self {
903 Self::new()
904 }
905}
906
907impl Chart {
909 pub fn new() -> Self {
910 Chart {
911 meta: MetaInfo::new(),
912 note: NoteInfo::new(),
913 beat: BeatInfo::new(),
914 audio: AudioInfo::new(),
915 camera: CameraInfo::default(),
916 version: "0.7.0".to_string(),
917 bg: BgInfo::new(),
918 file_hash: String::new(),
919 }
920 }
921
922 pub fn mode_bpm(&self) -> Option<f64> {
923 let mut last_bpm = *self.beat.bpm.first()?;
924
925 let mut durations: HashMap<u64, f64> = HashMap::new();
926
927 for ab in self.beat.bpm.windows(2) {
928 let a = ab[0];
929 let b = ab[1];
930 let l = durations.entry(a.1.to_bits()).or_default();
931 *l += self.tick_to_ms(b.0) - self.tick_to_ms(a.0);
932
933 last_bpm = b;
934 }
935
936 {
937 let x = durations.entry(last_bpm.1.to_bits()).or_default();
938 *x += self.tick_to_ms(self.get_last_tick()) - self.tick_to_ms(last_bpm.0);
939 }
940
941 durations
942 .iter()
943 .max_by(|a, b| a.1.total_cmp(b.1))
944 .map(|x| f64::from_bits(*x.0))
945 }
946
947 pub fn ms_to_tick(&self, ms: f64) -> u32 {
948 if ms <= 0.0 {
949 return 0;
950 }
951
952 let bpm = match self.beat.bpm.binary_search_by(|b| {
953 self.tick_to_ms(b.0)
954 .partial_cmp(&ms)
955 .unwrap_or(std::cmp::Ordering::Less)
956 }) {
957 Ok(i) => self.beat.bpm[i],
958 Err(i) => self.beat.bpm[i - 1],
959 };
960
961 let remaining = ms - self.tick_to_ms(bpm.0);
962 bpm.0 + ticks_from_ms(remaining, bpm.1, KSON_RESOLUTION) as u32
963 }
964
965 pub fn tick_to_ms(&self, tick: u32) -> f64 {
966 let mut ret: f64 = 0.0;
967 let mut prev = self.beat.bpm.first().unwrap_or(&(0, 120.0));
968
969 for b in &self.beat.bpm {
970 if b.0 > tick {
971 break;
972 }
973 ret += ms_from_ticks((b.0 - prev.0) as i64, prev.1, KSON_RESOLUTION);
974 prev = b;
975 }
976 ret + ms_from_ticks((tick - prev.0) as i64, prev.1, KSON_RESOLUTION)
977 }
978
979 pub fn tick_to_measure(&self, tick: u32) -> u32 {
980 let mut ret = 0;
981 let mut time_sig_iter = self.beat.time_sig.iter();
982 let mut remaining_ticks = tick;
983 if let Some(first_sig) = time_sig_iter.next() {
984 let mut prev_index = first_sig.0;
985 let mut prev_ticks_per_measure = KSON_RESOLUTION * 4 * first_sig.1 .0 / first_sig.1 .1;
986 if prev_ticks_per_measure == 0 {
987 return ret;
988 }
989 for current_sig in time_sig_iter {
990 let measure_count = current_sig.0 - prev_index;
991 let tick_count = measure_count * prev_ticks_per_measure;
992 if tick_count > remaining_ticks {
993 break;
994 }
995 ret += measure_count;
996 remaining_ticks -= tick_count;
997 prev_index = current_sig.0;
998 prev_ticks_per_measure = KSON_RESOLUTION * 4 * current_sig.1 .0 / current_sig.1 .1;
999 if prev_ticks_per_measure == 0 {
1000 return ret;
1001 }
1002 }
1003 ret += remaining_ticks / prev_ticks_per_measure;
1004 }
1005 ret
1006 }
1007
1008 pub fn measure_to_tick(&self, measure: u32) -> u32 {
1009 let mut ret = 0;
1010 let mut remaining_measures = measure;
1011 let mut time_sig_iter = self.beat.time_sig.iter();
1012
1013 if let Some(first_sig) = time_sig_iter.next() {
1014 let mut prev_index = first_sig.0;
1015 let mut prev_ticks_per_measure = KSON_RESOLUTION * 4 * first_sig.1 .0 / first_sig.1 .1;
1016 for current_sig in time_sig_iter {
1017 let measure_count = current_sig.0 - prev_index;
1018 if measure_count > remaining_measures {
1019 break;
1020 }
1021 ret += measure_count * prev_ticks_per_measure;
1022 remaining_measures -= measure_count;
1023 prev_index = current_sig.0;
1024 prev_ticks_per_measure = KSON_RESOLUTION * 4 * current_sig.1 .0 / current_sig.1 .1;
1025 }
1026 ret += remaining_measures * prev_ticks_per_measure;
1027 }
1028 ret
1029 }
1030
1031 pub fn bpm_at_tick(&self, tick: u32) -> f64 {
1032 match self.beat.bpm.binary_search_by(|b| b.0.cmp(&tick)) {
1033 Ok(i) => self.beat.bpm[i].1,
1034 Err(i) => self.beat.bpm[i - 1].1,
1035 }
1036 }
1037
1038 pub fn tick_duration_ms_at(&self, tick: u32) -> f64 {
1039 let bpm = self.bpm_at_tick(tick);
1040 beat_in_ms(bpm) / KSON_RESOLUTION as f64
1041 }
1042
1043 pub fn beat_line_iter(&self) -> MeasureBeatLines {
1044 let mut funcs: Vec<(u32, Box<BeatLineFn>)> = Vec::new();
1045 let mut prev_start = 0;
1046 let mut prev_sig = match self.beat.time_sig.first() {
1047 Some(v) => v,
1048 None => &(0, TimeSignature(4, 4)),
1049 };
1050
1051 for time_sig in &self.beat.time_sig {
1052 let ticks_per_beat = KSON_RESOLUTION * 4 / time_sig.1 .1;
1053 let ticks_per_measure = KSON_RESOLUTION * 4 * time_sig.1 .0 / time_sig.1 .1;
1054 let prev_ticks_per_measure = KSON_RESOLUTION * 4 * prev_sig.1 .0 / prev_sig.1 .1;
1055
1056 let new_start = prev_start + (time_sig.0 - prev_sig.0) * prev_ticks_per_measure;
1057 if ticks_per_measure > 0 && ticks_per_beat > 0 {
1058 funcs.push((
1059 new_start,
1060 Box::new(move |y| {
1061 let adjusted = y - new_start;
1062 Some((y + ticks_per_beat, (adjusted % ticks_per_measure) == 0))
1063 }),
1064 ));
1065 } else {
1066 funcs.push((new_start, Box::new(|_| None)));
1067 }
1068
1069 prev_start = new_start;
1070 prev_sig = time_sig;
1071 }
1072
1073 MeasureBeatLines {
1074 tick: 0,
1075 funcs,
1076 func_index: 0,
1077 }
1078 }
1079
1080 pub fn get_last_tick(&self) -> u32 {
1081 let mut last_tick = 0;
1082
1083 for i in 0..4 {
1085 if let Some(last) = &self.note.bt[i].last() {
1086 last_tick = last_tick.max(last.y + last.l);
1087 }
1088 }
1089
1090 for i in 0..2 {
1092 if let Some(last) = &self.note.fx[i].last() {
1093 last_tick = last_tick.max(last.y + last.l);
1094 }
1095 }
1096
1097 for i in 0..2 {
1099 for section in &self.note.laser[i] {
1100 let base_y = section.0;
1101 if let Some(last) = §ion.1.last() {
1102 last_tick = last_tick.max(last.ry + base_y);
1103 }
1104 }
1105 }
1106 last_tick
1107 }
1108}
1109
1110pub trait IsDefault {
1111 fn is_default(&self) -> bool;
1112}
1113
1114impl<T> IsDefault for T
1115where
1116 T: Default + PartialEq,
1117{
1118 fn is_default(&self) -> bool {
1119 self.eq(&T::default())
1120 }
1121}
1122
1123#[cfg(test)]
1124mod tests {
1125 use serde_test::Token;
1126
1127 use crate::parameter::{self, EffectFloat, EffectFreq, EffectParameterValue};
1128
1129 #[test]
1130 fn effect_param() {
1131 let mut param = parameter::EffectParameter {
1132 on: Some(EffectParameterValue::Freq(
1133 EffectFreq::Khz(10.0)..=EffectFreq::Khz(20.0),
1134 )),
1135 off: EffectParameterValue::Freq(EffectFreq::Hz(500)..=EffectFreq::Hz(500)),
1136 v: 0.0_f32,
1137 ..Default::default()
1138 };
1139
1140 serde_test::assert_tokens(¶m, &[Token::Str("500Hz>10kHz-20kHz")]);
1141
1142 param.on = None;
1143 param.off =
1144 EffectParameterValue::Filename("e9fda14b-d635-4cd8-8c7a-ca12f8d9b78a".to_string());
1145
1146 serde_test::assert_tokens(
1147 ¶m,
1148 &[Token::Str("e9fda14b-d635-4cd8-8c7a-ca12f8d9b78a")],
1149 );
1150
1151 param.off = EffectParameterValue::Sample(100..=100);
1152 serde_test::assert_tokens(¶m, &[Token::Str("100samples")]);
1153 param.off = EffectParameterValue::Sample(100..=1000);
1154 serde_test::assert_tokens(¶m, &[Token::Str("100samples-1000samples")]);
1155
1156 param.off = EffectParameterValue::Length(
1157 EffectFloat::Fraction(1, 2)..=EffectFloat::Fraction(1, 2),
1158 true,
1159 );
1160 serde_test::assert_tokens(¶m, &[Token::Str("1/2")]);
1161
1162 param.off = EffectParameterValue::Switch(false..=false);
1163 param.on = Some(EffectParameterValue::Switch(false..=true));
1164 serde_test::assert_tokens(¶m, &[Token::Str("off>off-on")]);
1165 }
1166}