bms_rs/bms/parse/
notes.rs

1//! Note objects manager.
2
3use itertools::Itertools;
4use std::{
5    cmp::Reverse,
6    collections::{BTreeMap, HashMap},
7    ops::Bound,
8};
9
10use super::{header::Header, obj::Obj, ParseError, Result};
11use crate::{
12    lex::{
13        command::{self, Channel, Key, NoteKind, ObjId},
14        token::Token,
15    },
16    time::{ObjTime, Track},
17};
18
19/// An object to change the BPM of the score.
20#[derive(Debug, Clone, Copy)]
21#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
22pub struct BpmChangeObj {
23    /// The time to begin the change of BPM.
24    pub time: ObjTime,
25    /// The BPM to be.
26    pub bpm: f64,
27}
28
29impl PartialEq for BpmChangeObj {
30    fn eq(&self, other: &Self) -> bool {
31        self.time == other.time
32    }
33}
34
35impl Eq for BpmChangeObj {}
36
37impl PartialOrd for BpmChangeObj {
38    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
39        Some(self.cmp(other))
40    }
41}
42
43impl Ord for BpmChangeObj {
44    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
45        self.time.cmp(&other.time)
46    }
47}
48
49/// An object to change its section length of the score.
50#[derive(Debug, Clone, Copy)]
51#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
52pub struct SectionLenChangeObj {
53    /// The target track to change.
54    pub track: Track,
55    /// The length to be.
56    pub length: f64,
57}
58
59impl PartialEq for SectionLenChangeObj {
60    fn eq(&self, other: &Self) -> bool {
61        self.track == other.track
62    }
63}
64
65impl Eq for SectionLenChangeObj {}
66
67impl PartialOrd for SectionLenChangeObj {
68    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
69        Some(self.cmp(other))
70    }
71}
72
73impl Ord for SectionLenChangeObj {
74    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
75        self.track.cmp(&other.track)
76    }
77}
78
79/// An object to stop scrolling of score.
80#[derive(Debug, Clone, Copy)]
81#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
82pub struct StopObj {
83    /// Time to start the stop.
84    pub time: ObjTime,
85    /// Object duration how long stops scrolling of score.
86    ///
87    /// Note that the duration of stopping will not be changed by a current measure length but BPM.
88    pub duration: u32,
89}
90
91impl PartialEq for StopObj {
92    fn eq(&self, other: &Self) -> bool {
93        self.time == other.time
94    }
95}
96
97impl Eq for StopObj {}
98
99impl PartialOrd for StopObj {
100    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
101        Some(self.cmp(other))
102    }
103}
104
105impl Ord for StopObj {
106    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
107        self.time.cmp(&other.time)
108    }
109}
110
111/// An object to change the image for BGA (background animation).
112#[derive(Debug, Clone, Copy)]
113#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
114pub struct BgaObj {
115    /// Time to start to display the image.
116    pub time: ObjTime,
117    /// Identifier represents the image/video file registered in [`Header`].
118    pub id: ObjId,
119    /// Layer to display.
120    pub layer: BgaLayer,
121}
122
123impl PartialEq for BgaObj {
124    fn eq(&self, other: &Self) -> bool {
125        self.time == other.time
126    }
127}
128
129impl Eq for BgaObj {}
130
131impl PartialOrd for BgaObj {
132    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
133        Some(self.cmp(other))
134    }
135}
136
137impl Ord for BgaObj {
138    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
139        self.time.cmp(&other.time)
140    }
141}
142
143/// A layer where the image for BGA to be displayed.
144#[derive(Debug, Clone, Copy, PartialEq, Eq)]
145#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
146#[non_exhaustive]
147pub enum BgaLayer {
148    /// The lowest layer.
149    Base,
150    /// Layer which is displayed only if a player missed to play notes.
151    Poor,
152    /// An overlaying layer.
153    Overlay,
154}
155
156/// An object to change scrolling factor of the score.
157#[derive(Debug, Clone, Copy)]
158#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
159pub struct ScrollingFactorObj {
160    /// The time to begin the change of BPM.
161    pub time: ObjTime,
162    /// The scrolling factor to be.
163    pub factor: f64,
164}
165
166impl PartialEq for ScrollingFactorObj {
167    fn eq(&self, other: &Self) -> bool {
168        self.time == other.time
169    }
170}
171
172impl Eq for ScrollingFactorObj {}
173
174impl PartialOrd for ScrollingFactorObj {
175    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
176        Some(self.cmp(other))
177    }
178}
179
180impl Ord for ScrollingFactorObj {
181    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
182        self.time.cmp(&other.time)
183    }
184}
185
186/// An object to change spacing factor between notes with linear interpolation.
187#[derive(Debug, Clone, Copy)]
188#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
189pub struct SpacingFactorObj {
190    /// The time to begin the change of BPM.
191    pub time: ObjTime,
192    /// The spacing factor to be.
193    pub factor: f64,
194}
195
196impl PartialEq for SpacingFactorObj {
197    fn eq(&self, other: &Self) -> bool {
198        self.time == other.time
199    }
200}
201
202impl Eq for SpacingFactorObj {}
203
204impl PartialOrd for SpacingFactorObj {
205    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
206        Some(self.cmp(other))
207    }
208}
209
210impl Ord for SpacingFactorObj {
211    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
212        self.time.cmp(&other.time)
213    }
214}
215
216/// An extended object on the score.
217#[derive(Debug, Clone)]
218#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
219pub struct ExtendedMessageObj {
220    /// The track which the message is on.
221    pub track: Track,
222    /// The channel which the message is on.
223    pub channel: Channel,
224    /// The extended message.
225    pub message: String,
226}
227
228impl PartialEq for ExtendedMessageObj {
229    fn eq(&self, other: &Self) -> bool {
230        self.track == other.track
231    }
232}
233
234impl Eq for ExtendedMessageObj {}
235
236impl PartialOrd for ExtendedMessageObj {
237    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
238        Some(self.cmp(other))
239    }
240}
241
242impl Ord for ExtendedMessageObj {
243    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
244        self.track.cmp(&other.track)
245    }
246}
247
248/// The objects set for querying by lane or time.
249#[derive(Debug, Default)]
250pub struct Notes {
251    // objects stored in obj is sorted, so it can be searched by bisection method
252    objs: HashMap<ObjId, Vec<Obj>>,
253    bgms: BTreeMap<ObjTime, Vec<ObjId>>,
254    ids_by_key: HashMap<Key, BTreeMap<ObjTime, ObjId>>,
255    bpm_changes: BTreeMap<ObjTime, BpmChangeObj>,
256    section_len_changes: BTreeMap<Track, SectionLenChangeObj>,
257    stops: BTreeMap<ObjTime, StopObj>,
258    bga_changes: BTreeMap<ObjTime, BgaObj>,
259    scrolling_factor_changes: BTreeMap<ObjTime, ScrollingFactorObj>,
260    spacing_factor_changes: BTreeMap<ObjTime, SpacingFactorObj>,
261    extended_messages: Vec<ExtendedMessageObj>,
262}
263
264impl Notes {
265    /// Creates a new notes dictionary.
266    pub fn new() -> Self {
267        Default::default()
268    }
269
270    /// Converts into the notes sorted by time.
271    pub fn into_all_notes(self) -> Vec<Obj> {
272        self.objs.into_values().flatten().sorted().collect()
273    }
274
275    /// Returns the iterator having all of the notes sorted by time.
276    pub fn all_notes(&self) -> impl Iterator<Item = &Obj> {
277        self.objs.values().flatten().sorted()
278    }
279
280    /// Returns all the bgms in the score.
281    pub fn bgms(&self) -> &BTreeMap<ObjTime, Vec<ObjId>> {
282        &self.bgms
283    }
284
285    /// Returns the bpm change objects.
286    pub fn bpm_changes(&self) -> &BTreeMap<ObjTime, BpmChangeObj> {
287        &self.bpm_changes
288    }
289
290    /// Returns the section len change objects.
291    pub fn section_len_changes(&self) -> &BTreeMap<Track, SectionLenChangeObj> {
292        &self.section_len_changes
293    }
294
295    /// Returns the scroll stop objects.
296    pub fn stops(&self) -> &BTreeMap<ObjTime, StopObj> {
297        &self.stops
298    }
299
300    /// Returns the bga change objects.
301    pub fn bga_changes(&self) -> &BTreeMap<ObjTime, BgaObj> {
302        &self.bga_changes
303    }
304
305    /// Finds next object on the key `Key` from the time `ObjTime`.
306    pub fn next_obj_by_key(&self, key: Key, time: ObjTime) -> Option<&Obj> {
307        self.ids_by_key
308            .get(&key)?
309            .range((Bound::Excluded(time), Bound::Unbounded))
310            .next()
311            .and_then(|(_, id)| {
312                let objs = self.objs.get(id)?;
313                let idx = objs
314                    .binary_search_by(|probe| probe.offset.cmp(&time))
315                    .unwrap_or_else(|idx| idx);
316                objs.get(idx)
317            })
318    }
319
320    /// Adds the new note object to the notes.
321    pub fn push_note(&mut self, note: Obj) {
322        self.objs.entry(note.obj).or_default().push(note.clone());
323        self.ids_by_key
324            .entry(note.key)
325            .or_default()
326            .insert(note.offset, note.obj);
327    }
328
329    /// Removes the latest note from the notes.
330    pub fn remove_latest_note(&mut self, id: ObjId) -> Option<Obj> {
331        self.objs.entry(id).or_default().pop().inspect(|removed| {
332            self.ids_by_key
333                .get_mut(&removed.key)
334                .unwrap()
335                .remove(&removed.offset)
336                .unwrap();
337        })
338    }
339
340    /// Removes the note from the notes.
341    pub fn remove_note(&mut self, id: ObjId) -> Vec<Obj> {
342        self.objs.remove(&id).map_or(vec![], |removed| {
343            for item in &removed {
344                self.ids_by_key
345                    .get_mut(&item.key)
346                    .unwrap()
347                    .remove(&item.offset)
348                    .unwrap();
349            }
350            removed
351        })
352    }
353
354    /// Adds a new BPM change object to the notes.
355    pub fn push_bpm_change(&mut self, bpm_change: BpmChangeObj) {
356        if self
357            .bpm_changes
358            .insert(bpm_change.time, bpm_change)
359            .is_some()
360        {
361            eprintln!(
362                "duplicate bpm change object detected at {:?}",
363                bpm_change.time
364            );
365        }
366    }
367
368    /// Adds a new scrolling factor change object to the notes.
369    pub fn push_scrolling_factor_change(&mut self, bpm_change: ScrollingFactorObj) {
370        if self
371            .scrolling_factor_changes
372            .insert(bpm_change.time, bpm_change)
373            .is_some()
374        {
375            eprintln!(
376                "duplicate scrolling factor change object detected at {:?}",
377                bpm_change.time
378            );
379        }
380    }
381
382    /// Adds a new spacing factor change object to the notes.
383    pub fn push_spacing_factor_change(&mut self, bpm_change: SpacingFactorObj) {
384        if self
385            .spacing_factor_changes
386            .insert(bpm_change.time, bpm_change)
387            .is_some()
388        {
389            eprintln!(
390                "duplicate spacing factor change object detected at {:?}",
391                bpm_change.time
392            );
393        }
394    }
395
396    /// Adds a new section length change object to the notes.
397    pub fn push_section_len_change(&mut self, section_len_change: SectionLenChangeObj) {
398        if self
399            .section_len_changes
400            .insert(section_len_change.track, section_len_change)
401            .is_some()
402        {
403            eprintln!(
404                "duplicate section length change object detected at {:?}",
405                section_len_change.track
406            );
407        }
408    }
409
410    /// Adds a new stop object to the notes.
411    pub fn push_stop(&mut self, stop: StopObj) {
412        self.stops
413            .entry(stop.time)
414            .and_modify(|existing| {
415                existing.duration = existing.duration.saturating_add(stop.duration)
416            })
417            .or_insert(stop);
418    }
419
420    /// Adds a new bga change object to the notes.
421    pub fn push_bga_change(&mut self, bga: BgaObj) {
422        if self.bga_changes.insert(bga.time, bga).is_some() {
423            eprintln!("duplicate bga change object detected at {:?}", bga.time);
424        }
425    }
426
427    /// Adds the new extended message object to the notes.
428    pub fn push_extended_message(&mut self, message: ExtendedMessageObj) {
429        self.extended_messages.push(message);
430    }
431
432    pub(crate) fn parse(&mut self, token: &Token, header: &Header) -> Result<()> {
433        match token {
434            Token::Message {
435                track,
436                channel: Channel::BpmChange,
437                message,
438            } => {
439                for (time, obj) in ids_from_message(*track, message) {
440                    let &bpm = header
441                        .bpm_changes
442                        .get(&obj)
443                        .ok_or(ParseError::UndefinedObject(obj))?;
444                    self.push_bpm_change(BpmChangeObj { time, bpm });
445                }
446            }
447            Token::Message {
448                track,
449                channel: Channel::BpmChangeU8,
450                message,
451            } => {
452                let denominator = message.len() as u32 / 2;
453                for (i, (c1, c2)) in message.chars().tuples().enumerate() {
454                    let bpm = c1.to_digit(16).unwrap() * 16 + c2.to_digit(16).unwrap();
455                    if bpm == 0 {
456                        continue;
457                    }
458                    let time = ObjTime::new(track.0, i as u32, denominator);
459                    self.push_bpm_change(BpmChangeObj {
460                        time,
461                        bpm: bpm as f64,
462                    });
463                }
464            }
465            Token::Message {
466                track,
467                channel: Channel::Scroll,
468                message,
469            } => {
470                for (time, obj) in ids_from_message(*track, message) {
471                    let &factor = header
472                        .scrolling_factor_changes
473                        .get(&obj)
474                        .ok_or(ParseError::UndefinedObject(obj))?;
475                    self.push_scrolling_factor_change(ScrollingFactorObj { time, factor });
476                }
477            }
478            Token::Message {
479                track,
480                channel: Channel::Speed,
481                message,
482            } => {
483                for (time, obj) in ids_from_message(*track, message) {
484                    let &factor = header
485                        .spacing_factor_changes
486                        .get(&obj)
487                        .ok_or(ParseError::UndefinedObject(obj))?;
488                    self.push_spacing_factor_change(SpacingFactorObj { time, factor });
489                }
490            }
491            Token::Message {
492                track,
493                channel: Channel::SectionLen,
494                message,
495            } => {
496                let track = Track(track.0);
497                let length = message.parse().expect("f64 as section length");
498                assert!(0.0 < length, "section length must be greater than zero");
499                self.push_section_len_change(SectionLenChangeObj { track, length });
500            }
501            Token::Message {
502                track,
503                channel: Channel::Stop,
504                message,
505            } => {
506                for (time, obj) in ids_from_message(*track, message) {
507                    let &duration = header
508                        .stops
509                        .get(&obj)
510                        .ok_or(ParseError::UndefinedObject(obj))?;
511                    self.push_stop(StopObj { time, duration })
512                }
513            }
514            Token::Message {
515                track,
516                channel: channel @ (Channel::BgaBase | Channel::BgaPoor | Channel::BgaLayer),
517                message,
518            } => {
519                for (time, obj) in ids_from_message(*track, message) {
520                    if !header.bmp_files.contains_key(&obj) {
521                        return Err(ParseError::UndefinedObject(obj));
522                    }
523                    let layer = match channel {
524                        Channel::BgaBase => BgaLayer::Base,
525                        Channel::BgaPoor => BgaLayer::Poor,
526                        Channel::BgaLayer => BgaLayer::Overlay,
527                        _ => unreachable!(),
528                    };
529                    self.push_bga_change(BgaObj {
530                        time,
531                        id: obj,
532                        layer,
533                    });
534                }
535            }
536            Token::Message {
537                track,
538                channel: Channel::Bgm,
539                message,
540            } => {
541                for (time, obj) in ids_from_message(*track, message) {
542                    self.bgms.entry(time).or_default().push(obj)
543                }
544            }
545            Token::Message {
546                track,
547                channel:
548                    Channel::Note {
549                        kind,
550                        is_player1,
551                        key,
552                    },
553                message,
554            } => {
555                for (offset, obj) in ids_from_message(*track, message) {
556                    self.push_note(Obj {
557                        offset,
558                        kind: *kind,
559                        is_player1: *is_player1,
560                        key: *key,
561                        obj,
562                    });
563                }
564            }
565            Token::ExtendedMessage {
566                track,
567                channel,
568                message,
569            } => {
570                let track = Track(track.0);
571                self.push_extended_message(ExtendedMessageObj {
572                    track,
573                    channel: channel.clone(),
574                    message: (*message).to_owned(),
575                });
576            }
577            &Token::LnObj(end_id) => {
578                let mut end_note = self
579                    .remove_latest_note(end_id)
580                    .ok_or(ParseError::UndefinedObject(end_id))?;
581                let Obj { offset, key, .. } = &end_note;
582                let (_, &begin_id) =
583                    self.ids_by_key[key].range(..offset).last().ok_or_else(|| {
584                        ParseError::SyntaxError(format!(
585                            "expected preceding object for #LNOBJ {:?}",
586                            end_id
587                        ))
588                    })?;
589                let mut begin_note = self.remove_latest_note(begin_id).unwrap();
590                begin_note.kind = NoteKind::Long;
591                end_note.kind = NoteKind::Long;
592                self.push_note(begin_note);
593                self.push_note(end_note);
594            }
595            _ => {}
596        }
597        Ok(())
598    }
599
600    /// Gets the time of last visible object.
601    pub fn last_visible_time(&self) -> Option<ObjTime> {
602        self.objs
603            .values()
604            .flatten()
605            .filter(|obj| !matches!(obj.kind, NoteKind::Invisible))
606            .map(Reverse)
607            .sorted()
608            .next()
609            .map(|Reverse(obj)| obj.offset)
610    }
611
612    /// Gets the time of last BGM object.
613    ///
614    /// You can't use this to find the length of music. Because this doesn't consider that the length of sound. And visible notes may ring after all BGMs.
615    pub fn last_bgm_time(&self) -> Option<ObjTime> {
616        self.bgms.last_key_value().map(|(time, _)| time).cloned()
617    }
618
619    /// Gets the time of last any object including visible, BGM, BPM change, section length change and so on.
620    ///
621    /// You can't use this to find the length of music. Because this doesn't consider that the length of sound.
622    pub fn last_obj_time(&self) -> Option<ObjTime> {
623        let obj_last = self
624            .objs
625            .values()
626            .flatten()
627            .map(Reverse)
628            .sorted()
629            .next()
630            .map(|Reverse(obj)| obj.offset);
631        let bpm_last = self.bpm_changes.last_key_value().map(|(&time, _)| time);
632        let section_len_last =
633            self.section_len_changes
634                .last_key_value()
635                .map(|(&time, _)| ObjTime {
636                    track: time,
637                    numerator: 0,
638                    denominator: 4,
639                });
640        let stop_last = self.stops.last_key_value().map(|(&time, _)| time);
641        let bga_last = self.bga_changes.last_key_value().map(|(&time, _)| time);
642        [obj_last, bpm_last, section_len_last, stop_last, bga_last]
643            .into_iter()
644            .max()
645            .flatten()
646    }
647
648    /// Calculates a required resolution to convert the notes time into pulses, which split one quarter note evenly.
649    pub fn resolution_for_pulses(&self) -> u32 {
650        use num::Integer;
651
652        let mut hyp_resolution = 1;
653        for obj in self.objs.values().flatten() {
654            hyp_resolution = hyp_resolution.lcm(&obj.offset.denominator);
655        }
656        for bpm_change in self.bpm_changes.values() {
657            hyp_resolution = hyp_resolution.lcm(&bpm_change.time.denominator);
658        }
659        hyp_resolution
660    }
661}
662
663fn ids_from_message(
664    track: command::Track,
665    message: &'_ str,
666) -> impl Iterator<Item = (ObjTime, ObjId)> + '_ {
667    let denominator = message.len() as u32 / 2;
668    let mut chars = message.chars().tuples().enumerate();
669    std::iter::from_fn(move || {
670        let (i, c1, c2) = loop {
671            let (i, (c1, c2)) = chars.next()?;
672            if !(c1 == '0' && c2 == '0') {
673                break (i, c1, c2);
674            }
675        };
676        let obj = ObjId::from_chars([c1, c2]).expect("invalid object id");
677        let time = ObjTime::new(track.0, i as u32, denominator);
678        Some((time, obj))
679    })
680}
681
682#[cfg(feature = "serde")]
683#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
684/// A pack of [`Notes`] for serialize/deserialize.
685pub struct NotesPack {
686    /// Note objects, ring the sound.
687    pub objs: Vec<Obj>,
688    /// BPM change events.
689    pub bpm_changes: Vec<BpmChangeObj>,
690    /// Section length change events.
691    pub section_len_changes: Vec<SectionLenChangeObj>,
692    /// Stop events.
693    pub stops: Vec<StopObj>,
694    /// BGA change events.
695    pub bga_changes: Vec<BgaObj>,
696    /// Scrolling factor change events.
697    pub scrolling_factor_changes: Vec<ScrollingFactorObj>,
698    /// Spacing factor change events.
699    pub spacing_factor_changes: Vec<SpacingFactorObj>,
700    /// Extended message events.
701    pub extended_messages: Vec<ExtendedMessageObj>,
702}
703
704#[cfg(feature = "serde")]
705impl serde::Serialize for Notes {
706    fn serialize<S>(&self, serializer: S) -> core::result::Result<S::Ok, S::Error>
707    where
708        S: serde::Serializer,
709    {
710        NotesPack {
711            objs: self.all_notes().cloned().collect(),
712            bpm_changes: self.bpm_changes.values().cloned().collect(),
713            section_len_changes: self.section_len_changes.values().cloned().collect(),
714            stops: self.stops.values().cloned().collect(),
715            bga_changes: self.bga_changes.values().cloned().collect(),
716            scrolling_factor_changes: self.scrolling_factor_changes.values().cloned().collect(),
717            spacing_factor_changes: self.spacing_factor_changes.values().cloned().collect(),
718            extended_messages: self.extended_messages.clone(),
719        }
720        .serialize(serializer)
721    }
722}
723
724#[cfg(feature = "serde")]
725impl<'de> serde::Deserialize<'de> for Notes {
726    fn deserialize<D>(deserializer: D) -> core::result::Result<Self, D::Error>
727    where
728        D: serde::Deserializer<'de>,
729    {
730        let pack = NotesPack::deserialize(deserializer)?;
731        let mut objs = HashMap::<ObjId, Vec<Obj>>::new();
732        let mut bgms: BTreeMap<ObjTime, Vec<ObjId>> = BTreeMap::new();
733        let mut ids_by_key: HashMap<Key, BTreeMap<ObjTime, ObjId>> = HashMap::new();
734        for obj in pack.objs {
735            if matches!(obj.kind, NoteKind::Invisible) {
736                bgms.entry(obj.offset)
737                    .and_modify(|ids| ids.push(obj.obj))
738                    .or_default();
739            }
740            ids_by_key
741                .entry(obj.key)
742                .and_modify(|id_map| {
743                    id_map.insert(obj.offset, obj.obj);
744                })
745                .or_default();
746            objs.entry(obj.obj).or_default().push(obj);
747        }
748        let mut bpm_changes = BTreeMap::new();
749        for bpm_change in pack.bpm_changes {
750            bpm_changes.insert(bpm_change.time, bpm_change);
751        }
752        let mut section_len_changes = BTreeMap::new();
753        for section_len_change in pack.section_len_changes {
754            section_len_changes.insert(section_len_change.track, section_len_change);
755        }
756        let mut stops = BTreeMap::new();
757        for stop in pack.stops {
758            stops.insert(stop.time, stop);
759        }
760        let mut bga_changes = BTreeMap::new();
761        for bga_change in pack.bga_changes {
762            bga_changes.insert(bga_change.time, bga_change);
763        }
764        let mut scrolling_factor_changes = BTreeMap::new();
765        for scrolling_change in pack.scrolling_factor_changes {
766            scrolling_factor_changes.insert(scrolling_change.time, scrolling_change);
767        }
768        let mut spacing_factor_changes = BTreeMap::new();
769        for spacing_change in pack.spacing_factor_changes {
770            spacing_factor_changes.insert(spacing_change.time, spacing_change);
771        }
772        let mut extended_messages = vec![];
773        for extended_message in pack.extended_messages {
774            extended_messages.push(extended_message);
775        }
776        Ok(Notes {
777            objs,
778            bgms,
779            ids_by_key,
780            bpm_changes,
781            section_len_changes,
782            stops,
783            bga_changes,
784            scrolling_factor_changes,
785            spacing_factor_changes,
786            extended_messages,
787        })
788    }
789}