midly/smf.rs
1//! Specific to the SMF packaging of MIDI streams.
2
3use crate::{
4 event::TrackEvent,
5 prelude::*,
6 primitive::{Format, Timing},
7 riff,
8};
9
10/// How many events per byte to estimate when allocating memory for events while parsing.
11///
12/// A value that is too large (ie. too few bytes/event), will overallocate, while a value that is
13/// too small (ie. too many bytes/event) will underallocate.
14///
15/// Usually, since memory is cheap it's better to overallocate, since reallocating the buffer may
16/// result in costly memory moves.
17/// This means that it's better to err on the large side (too few bytes/event).
18///
19/// Real-world tests show that without running status, the average is a little above 4 bytes/event,
20/// and with running status enabled it's a little above 3 bytes/event.
21/// This makes sense, since it's DeltaTime [+ Status] + Key + Velocity for NoteOn and NoteOff
22/// events, which should make up the bulk of most MIDI files.
23///
24/// Erring on the large side for events/byte (erring on the small side for bytes/event), we can
25/// approximate to 3 bytes/event.
26#[cfg(feature = "alloc")]
27const BYTES_TO_EVENTS: f32 = 1.0 / 3.0;
28
29/// How many bytes per event to estimate when allocating memory when writing.
30///
31/// A value that is too large will overallocate space for bytes, while a value that's too small
32/// will underallocate bytes.
33///
34/// Since the writer uses running status by default, a value a bit over `3` will allocate enough for
35/// almost all cases (Except for eg. info tracks, which usually have a high byte/event count
36/// because they contain text. However these tracks are small enough that reallocating doesn't
37/// matter too much).
38#[cfg(feature = "alloc")]
39const EVENTS_TO_BYTES: f32 = 3.4;
40
41/// How many bytes must a MIDI body have in order to enable multithreading.
42///
43/// When writing, the MIDI body size is estimated from the event count using `BYTES_PER_EVENT`.
44#[cfg(feature = "parallel")]
45const PARALLEL_ENABLE_THRESHOLD: usize = 3 * 1024;
46
47/// A single track: simply a list of track events.
48///
49/// Only available with the `alloc` feature enabled.
50#[cfg(feature = "alloc")]
51pub type Track<'a> = Vec<TrackEvent<'a>>;
52
53/// Represents a single `.mid` Standard Midi File.
54/// If you're casually looking to parse a `.mid` file, this is the type you're looking for.
55///
56/// This type is only available with the `alloc` feature enabled.
57/// If you're looking for a fully `no_std` alternative, see the [`parse`](fn.parse.html) function.
58#[cfg(feature = "alloc")]
59#[derive(Clone, PartialEq, Eq, Debug, Hash)]
60pub struct Smf<'a> {
61 /// The header of this MIDI file, indicating tempo information and track format.
62 pub header: Header,
63 /// A list of tracks within this MIDI file.
64 ///
65 /// Each track consists simply of a list of events (ie. there is no track metadata).
66 pub tracks: Vec<Track<'a>>,
67}
68#[cfg(feature = "alloc")]
69impl<'a> Smf<'a> {
70 /// Create a new empty `Smf` with zero tracks, using the given header.
71 #[inline]
72 pub fn new(header: Header) -> Smf<'a> {
73 Smf {
74 header,
75 tracks: vec![],
76 }
77 }
78
79 /// Parse a `.mid` Standard Midi File from its raw bytes.
80 /// If you casually want to parse `.mid` files, this is the function you're looking for.
81 pub fn parse(raw: &[u8]) -> Result<Smf> {
82 let (header, tracks) = parse(raw)?;
83 let track_count_hint = tracks.track_count_hint;
84 let tracks = tracks.collect_tracks()?;
85 validate_smf(&header, track_count_hint, tracks.len())?;
86 Ok(Smf { header, tracks })
87 }
88
89 /// Encodes and writes the file to the given generic writer.
90 ///
91 /// Note that this function requires a `midly::io::Write` writer, not a `std::io::Write` writer.
92 /// This makes it possible to support `no_std` environments, as well as custom writer errors.
93 /// If you're looking to write to a `File`, see the [`save`](#method.save) method.
94 /// If you're looking to write to a `std::io::Write` writer, see the
95 /// [`write_std`](#method.write_std) method.
96 ///
97 /// This function is always available, even in `no_std` environments.
98 #[inline]
99 pub fn write<W: Write>(&self, out: &mut W) -> WriteResult<W> {
100 write(&self.header, &self.tracks, out)
101 }
102
103 /// Encodes and writes the file to the given `std::io::Write` writer.
104 ///
105 /// This function is similar to the [`write`](#method.write) method, but writes to a
106 /// `std::io::Write` writer instead of a `midly::io::Write` writer.
107 ///
108 /// This function is only available with the `std` feature enabled.
109 #[cfg(feature = "std")]
110 #[inline]
111 pub fn write_std<W: io::Write>(&self, out: W) -> io::Result<()> {
112 write_std(&self.header, &self.tracks, out)
113 }
114
115 /// Encodes and writes the file to the given path.
116 ///
117 /// This function is only available with the `std` feature enabled.
118 #[cfg(feature = "std")]
119 #[inline]
120 pub fn save<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
121 /// A non-generic, non-inline function.
122 /// This means that this function will be compiled and monomorphized once, and reused for
123 /// every call to `save`.
124 fn save_impl(smf: &Smf, path: &Path) -> io::Result<()> {
125 smf.write(&mut IoWrap(File::create(path)?))
126 }
127 save_impl(self, path.as_ref())
128 }
129
130 /// Remove any lifetimed data from this event to create an `Smf` with `'static`
131 /// lifetime that can be stored and moved everywhere, solving borrow checker issues.
132 ///
133 /// This method creates a copy of the `Smf` structure. See the `make_static` method for an
134 /// in-place solution.
135 ///
136 /// WARNING: Any bytestrings, including meta messages, SysEx dumps and escape sequences will be
137 /// replaced by empty bytestrings.
138 pub fn to_static(&self) -> Smf<'static> {
139 self.clone().make_static()
140 }
141
142 /// Remove any lifetimed data from this event to create an `Smf` with `'static`
143 /// lifetime that can be stored and moved everywhere, solving borrow checker issues.
144 ///
145 /// This method consumes the `Smf` structure, reusing the backing memory.
146 ///
147 /// WARNING: Any bytestrings, including meta messages, SysEx dumps and escape sequences will be
148 /// replaced by empty bytestrings.
149 pub fn make_static(mut self) -> Smf<'static> {
150 for track in self.tracks.iter_mut() {
151 for ev in track.iter_mut() {
152 *ev = ev.to_static();
153 }
154 }
155 unsafe { mem::transmute::<Smf<'a>, Smf<'static>>(self) }
156 }
157}
158
159/// A track, represented as a `Vec` of events along with their originating bytes.
160///
161/// This type alias is only available with the `alloc` feature enabled.
162#[cfg(feature = "alloc")]
163pub type BytemappedTrack<'a> = Vec<(&'a [u8], TrackEvent<'a>)>;
164
165/// A `.mid` Standard Midi File, but keeps a mapping to the raw bytes that make up each event.
166///
167/// This type is only available with the `alloc` feature enabled.
168#[cfg(feature = "alloc")]
169#[derive(Clone, PartialEq, Eq, Debug, Hash)]
170pub struct SmfBytemap<'a> {
171 /// The header of this file.
172 pub header: Header,
173 /// A list of tracks, along with the bytemap of their events.
174 pub tracks: Vec<BytemappedTrack<'a>>,
175}
176#[cfg(feature = "alloc")]
177impl<'a> SmfBytemap<'a> {
178 /// Create a new empty `SmfBytemap` with zero tracks, using the given header.
179 #[inline]
180 pub fn new(header: Header) -> SmfBytemap<'a> {
181 SmfBytemap {
182 header,
183 tracks: vec![],
184 }
185 }
186
187 /// Parse a Standard Midi File from its raw bytes, keeping a map to the original bytes that
188 /// make up each event.
189 pub fn parse(raw: &[u8]) -> Result<SmfBytemap> {
190 let (header, tracks) = parse(raw)?;
191 let track_count_hint = tracks.track_count_hint;
192 let tracks = tracks.collect_bytemapped()?;
193 validate_smf(&header, track_count_hint, tracks.len())?;
194 Ok(SmfBytemap { header, tracks })
195 }
196
197 /// Encodes and writes the *events* (not the bytemap) to the given generic writer.
198 #[inline]
199 pub fn write<W: Write>(&self, out: &mut W) -> WriteResult<W> {
200 write(
201 &self.header,
202 self.tracks
203 .iter()
204 .map(|bytemapped| bytemapped.iter().map(|(_b, ev)| ev)),
205 out,
206 )
207 }
208
209 /// Encodes and writes the *events* (not the bytemap) to the given `std::io::Write` writer.
210 ///
211 /// This function is only available with the `std` feature enabled.
212 #[cfg(feature = "std")]
213 #[inline]
214 pub fn write_std<W: io::Write>(&self, out: W) -> io::Result<()> {
215 write_std(
216 &self.header,
217 self.tracks
218 .iter()
219 .map(|bytemapped| bytemapped.iter().map(|(_b, ev)| ev)),
220 out,
221 )
222 }
223
224 /// Creates/overwrites the file at the given path and writes the *events* (not the bytemap) to
225 /// it.
226 ///
227 /// This function is only available with the `std` feature enabled.
228 #[cfg(feature = "std")]
229 #[inline]
230 pub fn save<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
231 /// A non-generic, non-inline function.
232 /// This means that this function will be compiled and monomorphized once, and reused for
233 /// every call to `save`.
234 fn save_impl(smf: &SmfBytemap, path: &Path) -> io::Result<()> {
235 smf.write(&mut IoWrap(File::create(path)?))
236 }
237 save_impl(self, path.as_ref())
238 }
239}
240
241#[cfg(feature = "alloc")]
242fn validate_smf(header: &Header, track_count_hint: u16, track_count: usize) -> Result<()> {
243 if cfg!(feature = "strict") {
244 ensure!(
245 track_count_hint as usize == track_count,
246 err_malformed!("file has a different amount of tracks than declared")
247 );
248 ensure!(
249 header.format != Format::SingleTrack || track_count == 1,
250 err_malformed!("singletrack format file has multiple tracks")
251 );
252 }
253 Ok(())
254}
255
256/// Parse a raw MIDI file lazily, yielding its header and a lazy track iterator.
257/// No allocations are made.
258///
259/// The track iterator that is returned yields event iterators, which in turn yield concrete events.
260///
261/// This function is always available, even in `no_std` environments.
262pub fn parse(raw: &[u8]) -> Result<(Header, TrackIter)> {
263 let raw = match raw.get(..4) {
264 Some(b"RIFF") => riff::unwrap(raw)?,
265 Some(b"MThd") => raw,
266 _ => bail!(err_invalid!("not a midi file")),
267 };
268 let mut chunks = ChunkIter::new(raw);
269 let (header, track_count) = match chunks.next() {
270 Some(maybe_chunk) => match maybe_chunk.context(err_invalid!("invalid midi header"))? {
271 Chunk::Header(header, track_count) => Ok((header, track_count)),
272 Chunk::Track(_) => Err(err_invalid!("expected header, found track")),
273 },
274 None => Err(err_invalid!("no midi header chunk")),
275 }?;
276 let tracks = chunks.as_tracks(track_count);
277 Ok((header, tracks))
278}
279
280/// Encode and write a generic MIDI file into the given generic writer.
281/// The MIDI file is represented by a header and a list of tracks.
282///
283/// # Errors
284///
285/// The MIDI writer raises almost no errors by itself, it only bubbles errors from the underlying
286/// writer.
287/// The only exception to this rule are extreme cases that break the limits of the MIDI spec: if
288/// there are more than 65535 tracks, if the data for a single event is 256MB or larger, or if the
289/// total size of any track is 4GB or larger.
290///
291/// # Implementation notes
292///
293/// Currently this function will attempt to use multiple threads to encode the file if possible and
294/// the file is large enough to make it worth it.
295///
296/// Otherwise, each track will be written to an in-memory buffer before writing to disk.
297///
298/// If allocation is disabled, but the writer is seekable, the file will be written once and it
299/// will be seeked back in order to write down the chunk sizes.
300///
301/// Otherwise, encoding will happen twice: once to determine the size of the chunks and once again
302/// to actually write down the file.
303pub fn write<'a, T, E, W>(header: &Header, tracks: T, out: &mut W) -> WriteResult<W>
304where
305 T: IntoIterator<Item = E>,
306 T::IntoIter: ExactSizeIterator + Clone + Send,
307 E: IntoIterator<Item = &'a TrackEvent<'a>>,
308 E::IntoIter: Clone + Send,
309 W: Write,
310{
311 let tracks = tracks.into_iter().map(|events| events.into_iter());
312 //Write the header first
313 Chunk::write_header(header, tracks.len(), out)?;
314
315 //Try to write the file in parallel
316 #[cfg(feature = "parallel")]
317 {
318 //Figure out whether multithreading is worth it
319 let event_count = tracks
320 .clone()
321 .map(|track| track.into_iter().size_hint().0)
322 .sum::<usize>();
323 if (event_count as f32 * EVENTS_TO_BYTES) > PARALLEL_ENABLE_THRESHOLD as f32 {
324 use rayon::prelude::*;
325
326 //Write out the tracks in parallel into several different buffers
327 let mut track_chunks = Vec::new();
328 tracks
329 .collect::<Vec<_>>()
330 .into_par_iter()
331 .map(|track| {
332 let mut track_chunk = Vec::new();
333 Chunk::write_to_vec(track, &mut track_chunk)?;
334 Ok(track_chunk)
335 })
336 .collect_into_vec(&mut track_chunks);
337
338 //Write down the tracks sequentially and in order
339 for result in track_chunks {
340 let track_chunk = result.map_err(W::invalid_input)?;
341 out.write(&track_chunk)?;
342 }
343 return Ok(());
344 }
345 }
346
347 #[cfg(feature = "alloc")]
348 {
349 //Write the tracks into a buffer before writing out to the file
350 let mut buf = Vec::new();
351 for track in tracks {
352 Chunk::write_to_vec(track, &mut buf).map_err(|msg| W::invalid_input(msg))?;
353 out.write(&buf)?;
354 }
355 return Ok(());
356 }
357
358 #[allow(unreachable_code)]
359 {
360 if let Some(out) = out.make_seekable() {
361 //Write down using seeks if the writer is seekable
362 for track in tracks {
363 Chunk::write_seek(track, out)?;
364 }
365 return Ok(());
366 }
367
368 //Last resort: do probe-writing.
369 //Two passes are done: one to find out the size of the chunk and another to actually
370 //write the chunk.
371 for track in tracks {
372 Chunk::write_probe(track, out)?;
373 }
374 Ok(())
375 }
376}
377
378/// Similar to [`write`](fn.write.html), but writes to a `std::io::Write` writer instead of a
379/// `midly::io::Write` writer.
380///
381/// This function is only available with the `std` feature enabled.
382#[cfg(feature = "std")]
383#[inline]
384pub fn write_std<'a, T, E, W>(header: &Header, tracks: T, out: W) -> io::Result<()>
385where
386 T: IntoIterator<Item = E>,
387 T::IntoIter: ExactSizeIterator + Clone + Send,
388 E: IntoIterator<Item = &'a TrackEvent<'a>>,
389 E::IntoIter: Clone + Send,
390 W: io::Write,
391{
392 write(header, tracks, &mut IoWrap(out))
393}
394
395#[derive(Clone, Debug)]
396struct ChunkIter<'a> {
397 /// Starts at the current index, ends at EOF.
398 raw: &'a [u8],
399}
400impl<'a> ChunkIter<'a> {
401 #[inline]
402 fn new(raw: &'a [u8]) -> ChunkIter {
403 ChunkIter { raw }
404 }
405
406 #[inline]
407 fn as_tracks(self, track_count_hint: u16) -> TrackIter<'a> {
408 TrackIter {
409 chunks: self,
410 track_count_hint,
411 }
412 }
413}
414impl<'a> Iterator for ChunkIter<'a> {
415 type Item = Result<Chunk<'a>>;
416 #[inline]
417 fn next(&mut self) -> Option<Result<Chunk<'a>>> {
418 //Flip around option and result
419 match Chunk::read(&mut self.raw) {
420 Ok(Some(chunk)) => Some(Ok(chunk)),
421 Ok(None) => None,
422 Err(err) => {
423 //Ensure `Chunk::read` isn't called again, by setting read pointer to EOF (len 0)
424 //This is to prevent use of corrupted state (such as reading a new Chunk from the
425 //middle of a malformed message)
426 self.raw = &[];
427 Some(Err(err))
428 }
429 }
430 }
431}
432
433#[derive(Copy, Clone, Debug)]
434enum Chunk<'a> {
435 Header(Header, u16),
436 Track(&'a [u8]),
437}
438impl<'a> Chunk<'a> {
439 /// Should be called with a byte slice at least as large as the chunk (ideally until EOF).
440 /// The slice will be modified to point to the next chunk.
441 /// If we're *exactly* at EOF (slice length 0), returns a None signalling no more chunks.
442 fn read(raw: &mut &'a [u8]) -> Result<Option<Chunk<'a>>> {
443 Ok(loop {
444 if raw.is_empty() {
445 break None;
446 }
447 let id = raw
448 .split_checked(4)
449 .ok_or(err_invalid!("failed to read chunkid"))?;
450 let len = u32::read(raw).context(err_invalid!("failed to read chunklen"))?;
451 let chunkdata = match raw.split_checked(len as usize) {
452 Some(chunkdata) => chunkdata,
453 None => {
454 if cfg!(feature = "strict") {
455 bail!(err_malformed!("reached eof before chunk ended"));
456 } else {
457 //Just use the remainder of the file
458 mem::replace(raw, &[])
459 }
460 }
461 };
462 match id {
463 b"MThd" => {
464 let (header, track_count) = Header::read(chunkdata)?;
465 break Some(Chunk::Header(header, track_count));
466 }
467 b"MTrk" => {
468 break Some(Chunk::Track(chunkdata));
469 }
470 //Unknown chunk, just ignore and read the next one
471 _ => (),
472 }
473 })
474 }
475
476 /// Write a header chunk into a writer.
477 fn write_header<W: Write>(header: &Header, track_count: usize, out: &mut W) -> WriteResult<W> {
478 let mut header_chunk = [0; 4 + 4 + 6];
479 let track_count = u16::try_from(track_count)
480 .map_err(|_| W::invalid_input("track count exceeds 16 bit range"))?;
481 let header = header.encode(track_count);
482 header_chunk[0..4].copy_from_slice(&b"MThd"[..]);
483 header_chunk[4..8].copy_from_slice(&(header.len() as u32).to_be_bytes()[..]);
484 header_chunk[8..].copy_from_slice(&header[..]);
485 out.write(&header_chunk[..])?;
486 Ok(())
487 }
488
489 /// Write a single track chunk using the probe method.
490 ///
491 /// When probing, the chunk is written twice: one to find out the length of the chunk and again
492 /// to actually write the chunk contents.
493 fn write_probe<W: Write>(
494 track: impl Iterator<Item = &'a TrackEvent<'a>> + Clone,
495 out: &mut W,
496 ) -> WriteResult<W> {
497 let mut counter = WriteCounter(0);
498 Self::write_raw(track.clone(), &mut counter).map_err(W::invalid_input)?;
499 let len = Self::check_len::<W, _>(counter.0)?;
500 let mut head = [b'M', b'T', b'r', b'k', 0, 0, 0, 0];
501 head[4..8].copy_from_slice(&len);
502 out.write(&head)?;
503 Self::write_raw(track, out)?;
504 Ok(())
505 }
506
507 /// Write a single chunk using the seek method.
508 ///
509 /// The chunk is written once, then the writer is seeked back and the chunk length is written
510 /// last.
511 fn write_seek<W: Write + Seek>(
512 track: impl Iterator<Item = &'a TrackEvent<'a>>,
513 out: &mut W,
514 ) -> WriteResult<W> {
515 out.write(b"MTrk\0\0\0\0")?;
516 let start = out.tell()?;
517 Self::write_raw(track, out)?;
518 let len = Self::check_len::<W, _>(out.tell()? - start)?;
519 out.write_at(&len, start - 4)?;
520 Ok(())
521 }
522
523 /// Write a chunk to an in-memory `Vec`.
524 ///
525 /// Because the output is in-memory, the chunk can simply wind back and write the chunk length
526 /// last.
527 #[cfg(feature = "alloc")]
528 fn write_to_vec(
529 track: impl Iterator<Item = &'a TrackEvent<'a>>,
530 out: &mut Vec<u8>,
531 ) -> WriteResult<Vec<u8>> {
532 let cap = (track.size_hint().0 as f32 * EVENTS_TO_BYTES) as usize;
533 out.clear();
534 out.reserve(8 + cap);
535 out.extend_from_slice(b"MTrk\0\0\0\0");
536 Self::write_raw(track, out)?;
537 let len = Self::check_len::<Vec<u8>, _>(out.len() - 8)?;
538 out[4..8].copy_from_slice(&len);
539 Ok(())
540 }
541
542 /// Auxiliary method. Iterate over the events of a track and write them out.
543 fn write_raw<W: Write>(
544 track: impl Iterator<Item = &'a TrackEvent<'a>>,
545 out: &mut W,
546 ) -> WriteResult<W> {
547 let mut running_status = None;
548 for ev in track {
549 ev.write(&mut running_status, out)?;
550 }
551 Ok(())
552 }
553
554 /// Auxiliary method. Given an arbitrary-width length, fit it into a 32-bit big-endian integer,
555 /// reporting an error if it does not fit.
556 fn check_len<W, T>(len: T) -> StdResult<[u8; 4], W::Error>
557 where
558 u32: TryFrom<T>,
559 W: Write,
560 {
561 let len = u32::try_from(len)
562 .map_err(|_| W::invalid_input("midi chunk size exceeds 32 bit range"))?;
563 Ok(len.to_be_bytes())
564 }
565}
566
567/// A MIDI file header, indicating metadata about the file.
568#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
569pub struct Header {
570 /// Information about how should the tracks be laid out when playing them back.
571 pub format: Format,
572 /// Tempo information about the file.
573 ///
574 /// Usually it's not possible to determine the timing of a file with just this field, the first
575 /// few events of the first track must be parsed in the best case, and in the worst case the
576 /// file might have changing tempos along the song.
577 pub timing: Timing,
578}
579impl Header {
580 /// Create a new header from its raw parts.
581 #[inline]
582 pub fn new(format: Format, timing: Timing) -> Header {
583 Header { format, timing }
584 }
585
586 /// Read the contents of a header chunk, including the `Header` and the track count.
587 fn read(mut raw: &[u8]) -> Result<(Header, u16)> {
588 let format = Format::read(&mut raw)?;
589 let track_count = u16::read(&mut raw)?;
590 let timing = Timing::read(&mut raw)?;
591 Ok((Header::new(format, timing), track_count))
592 }
593 fn encode(&self, track_count: u16) -> [u8; 6] {
594 let mut bytes = [0; 6];
595 bytes[0..2].copy_from_slice(&self.format.encode()[..]);
596 bytes[2..4].copy_from_slice(&track_count.to_be_bytes()[..]);
597 bytes[4..6].copy_from_slice(&self.timing.encode()[..]);
598 bytes
599 }
600}
601
602/// An iterator over all *tracks* in a Standard Midi File.
603/// Created by the [`parse`](fn.parse.html) function.
604///
605/// This type is always available, even in `no_std` environments.
606#[derive(Clone, Debug)]
607pub struct TrackIter<'a> {
608 chunks: ChunkIter<'a>,
609 track_count_hint: u16,
610}
611impl<'a> TrackIter<'a> {
612 /// Create an event iterator from raw SMF bytes, excluding the header.
613 ///
614 /// The main way to obtain raw SMF without a header is the [`unread`](#method.unread) method.
615 #[inline]
616 pub fn new(raw: &[u8]) -> TrackIter {
617 TrackIter {
618 chunks: ChunkIter::new(raw),
619 track_count_hint: 0,
620 }
621 }
622
623 /// Peek at the remaining unparsed bytes in the file.
624 #[inline]
625 pub fn unread(&self) -> &'a [u8] {
626 self.chunks.raw
627 }
628
629 /// Parse and collect the remaining unparsed tracks into a `Vec` of tracks.
630 ///
631 /// This function is only available with the `alloc` feature enabled.
632 #[cfg(feature = "alloc")]
633 pub fn collect_tracks(self) -> Result<Vec<Track<'a>>> {
634 self.generic_collect(EventIter::into_vec)
635 }
636
637 /// Parse and collect the remaining unparsed tracks into a `Vec` of tracks, keeping a mapping
638 /// to the original bytes that make up each event.
639 ///
640 /// This function is only available with the `alloc` feature enabled.
641 #[cfg(feature = "alloc")]
642 pub fn collect_bytemapped(self) -> Result<Vec<BytemappedTrack<'a>>> {
643 self.generic_collect(|events| events.bytemapped().into_vec())
644 }
645
646 #[cfg(feature = "alloc")]
647 #[inline]
648 fn generic_collect<T: Send + 'a>(
649 self,
650 collect: impl Fn(EventIter<'a>) -> Result<Vec<T>> + Send + Sync,
651 ) -> Result<Vec<Vec<T>>> {
652 //Attempt to use multiple threads if possible and advantageous
653 #[cfg(feature = "parallel")]
654 {
655 if self.unread().len() >= PARALLEL_ENABLE_THRESHOLD {
656 use rayon::prelude::*;
657
658 let chunk_vec = self.collect::<Result<Vec<_>>>()?;
659 return chunk_vec
660 .into_par_iter()
661 .map(collect)
662 .collect::<Result<Vec<Vec<T>>>>();
663 }
664 }
665 //Fall back to single-threaded
666 self.map(|r| r.and_then(&collect))
667 .collect::<Result<Vec<Vec<T>>>>()
668 }
669}
670impl<'a> Iterator for TrackIter<'a> {
671 type Item = Result<EventIter<'a>>;
672
673 #[inline]
674 fn size_hint(&self) -> (usize, Option<usize>) {
675 (
676 self.track_count_hint as usize,
677 Some(self.track_count_hint as usize),
678 )
679 }
680
681 #[inline]
682 fn next(&mut self) -> Option<Result<EventIter<'a>>> {
683 loop {
684 if let Some(chunk) = self.chunks.next() {
685 self.track_count_hint = self.track_count_hint.saturating_sub(1);
686 match chunk {
687 Ok(Chunk::Track(track)) => break Some(Ok(EventIter::new(track))),
688 //Read another header (?)
689 Ok(Chunk::Header(..)) => {
690 if cfg!(feature = "strict") {
691 break Some(Err(err_malformed!("found duplicate header").into()));
692 } else {
693 //Ignore duplicate header
694 }
695 }
696 //Failed to read chunk
697 Err(err) => {
698 if cfg!(feature = "strict") {
699 break Some(Err(err).context(err_malformed!("invalid chunk")));
700 } else {
701 //Ignore invalid chunk
702 }
703 }
704 }
705 } else {
706 break None;
707 }
708 }
709 }
710}
711
712trait EventKind<'a> {
713 type Event: 'a;
714 fn read_ev(raw: &mut &'a [u8], running_status: &mut Option<u8>) -> Result<Self::Event>;
715}
716
717#[derive(Clone, Debug)]
718struct EventIterGeneric<'a, T> {
719 raw: &'a [u8],
720 running_status: Option<u8>,
721 _kind: PhantomData<T>,
722}
723impl<'a, T: EventKind<'a>> EventIterGeneric<'a, T> {
724 #[inline]
725 fn new(raw: &[u8]) -> EventIterGeneric<T> {
726 EventIterGeneric {
727 raw,
728 running_status: None,
729 _kind: PhantomData,
730 }
731 }
732
733 /// Get the remaining unread bytes.
734 #[inline]
735 fn unread(&self) -> &'a [u8] {
736 self.raw
737 }
738
739 /// Get the current running status of the track.
740 #[inline]
741 fn running_status(&self) -> Option<u8> {
742 self.running_status
743 }
744
745 /// Modify the current running status of the track.
746 #[inline]
747 fn running_status_mut(&mut self) -> &mut Option<u8> {
748 &mut self.running_status
749 }
750
751 #[cfg(feature = "alloc")]
752 #[inline]
753 fn estimate_events(&self) -> usize {
754 (self.raw.len() as f32 * BYTES_TO_EVENTS) as usize
755 }
756
757 #[cfg(feature = "alloc")]
758 #[inline]
759 fn into_vec(mut self) -> Result<Vec<T::Event>> {
760 let mut events = Vec::with_capacity(self.estimate_events());
761 while !self.raw.is_empty() {
762 match T::read_ev(&mut self.raw, &mut self.running_status) {
763 Ok(ev) => events.push(ev),
764 Err(err) => {
765 self.raw = &[];
766 if cfg!(feature = "strict") {
767 Err(err).context(err_malformed!("malformed event"))?;
768 } else {
769 //Stop reading track silently on failure
770 break;
771 }
772 }
773 }
774 }
775 Ok(events)
776 }
777}
778impl<'a, T: EventKind<'a>> Iterator for EventIterGeneric<'a, T> {
779 type Item = Result<T::Event>;
780 #[inline]
781 fn next(&mut self) -> Option<Self::Item> {
782 if !self.raw.is_empty() {
783 match T::read_ev(&mut self.raw, &mut self.running_status) {
784 Ok(ev) => Some(Ok(ev)),
785 Err(err) => {
786 self.raw = &[];
787 if cfg!(feature = "strict") {
788 Some(Err(err).context(err_malformed!("malformed event")))
789 } else {
790 None
791 }
792 }
793 }
794 } else {
795 None
796 }
797 }
798}
799
800/// An iterator over the events of a single track.
801/// Yielded by the [`TrackIter`](struct.TrackIter.html) iterator.
802///
803/// This iterator is lazy, it parses events as it goes, and therefore produces `Result<TrackEvent>>`
804/// rather than `TrackEvent`.
805///
806/// This type is always available, even in `no_std` environments.
807#[derive(Clone, Debug)]
808pub struct EventIter<'a> {
809 inner: EventIterGeneric<'a, Self>,
810}
811impl<'a> EventKind<'a> for EventIter<'a> {
812 type Event = TrackEvent<'a>;
813 #[inline]
814 fn read_ev(raw: &mut &'a [u8], rs: &mut Option<u8>) -> Result<TrackEvent<'a>> {
815 TrackEvent::read(raw, rs)
816 }
817}
818impl<'a> EventIter<'a> {
819 /// Create an event iterator from raw track bytes.
820 ///
821 /// It can be hard to obtain raw track bytes.
822 /// Usually these raw track bytes are obtained from the [`unread`](#method.unread) method on an
823 /// event iterator.
824 #[inline]
825 pub fn new(raw: &[u8]) -> EventIter {
826 EventIter {
827 inner: EventIterGeneric::new(raw),
828 }
829 }
830
831 /// Get the remaining unparsed event bytes.
832 #[inline]
833 pub fn unread(&self) -> &'a [u8] {
834 self.inner.unread()
835 }
836
837 /// Get the current running status of the track.
838 #[inline]
839 pub fn running_status(&self) -> Option<u8> {
840 self.inner.running_status()
841 }
842
843 /// Modify the current running status of the track.
844 #[inline]
845 pub fn running_status_mut(&mut self) -> &mut Option<u8> {
846 self.inner.running_status_mut()
847 }
848
849 /// Make this event iterator keep track of the raw bytes that make up each event.
850 #[inline]
851 pub fn bytemapped(self) -> EventBytemapIter<'a> {
852 EventBytemapIter {
853 inner: EventIterGeneric {
854 raw: self.inner.raw,
855 running_status: self.inner.running_status,
856 _kind: PhantomData,
857 },
858 }
859 }
860
861 /// Collects the remaining unparsed events into a `Track`.
862 ///
863 /// This function is a smarter version of `Iterator::collect`, as it guesses allocations and
864 /// is usually optimized better than its naive counterpart.
865 ///
866 /// This function is only available with the `alloc` feature enabled.
867 #[cfg(feature = "alloc")]
868 #[inline]
869 pub fn into_vec(self) -> Result<Track<'a>> {
870 self.inner.into_vec()
871 }
872}
873impl<'a> Iterator for EventIter<'a> {
874 type Item = Result<TrackEvent<'a>>;
875 #[inline]
876 fn next(&mut self) -> Option<Self::Item> {
877 self.inner.next()
878 }
879}
880
881/// An iterator over the events of a single track that keeps track of the raw bytes that make up
882/// each event.
883/// Created by the [`EventIter::bytemapped`](struct.EventIter.html#method.bytemapped) method.
884///
885/// This iterator is lazy, it parses events as it goes, and therefore produces
886/// `Result<(&[u8], TrackEvent)>>` rather than just `(&[u8], TrackEvent)`.
887///
888/// This type is always available, even in `no_std` environments.
889#[derive(Clone, Debug)]
890pub struct EventBytemapIter<'a> {
891 inner: EventIterGeneric<'a, Self>,
892}
893impl<'a> EventKind<'a> for EventBytemapIter<'a> {
894 type Event = (&'a [u8], TrackEvent<'a>);
895 #[inline]
896 fn read_ev(raw: &mut &'a [u8], rs: &mut Option<u8>) -> Result<Self::Event> {
897 TrackEvent::read_bytemap(raw, rs)
898 }
899}
900impl<'a> EventBytemapIter<'a> {
901 /// Create an event iterator from raw track bytes.
902 ///
903 /// It can be hard to obtain raw track bytes.
904 /// Usually these raw track bytes are obtained from the [`unread`](#method.unread) method on an
905 /// event iterator.
906 #[inline]
907 pub fn new(raw: &[u8]) -> EventBytemapIter {
908 EventBytemapIter {
909 inner: EventIterGeneric::new(raw),
910 }
911 }
912
913 /// Get the remaining unparsed event bytes.
914 #[inline]
915 pub fn unread(&self) -> &'a [u8] {
916 self.inner.unread()
917 }
918
919 /// Get the current running status of the track.
920 #[inline]
921 pub fn running_status(&self) -> Option<u8> {
922 self.inner.running_status()
923 }
924
925 /// Modify the current running status of the track.
926 #[inline]
927 pub fn running_status_mut(&mut self) -> &mut Option<u8> {
928 self.inner.running_status_mut()
929 }
930
931 /// Stop collecting bytemap information for any remaining events.
932 #[inline]
933 pub fn not_bytemapped(self) -> EventIter<'a> {
934 EventIter {
935 inner: EventIterGeneric {
936 raw: self.inner.raw,
937 running_status: self.inner.running_status,
938 _kind: PhantomData,
939 },
940 }
941 }
942
943 /// Collects the remaining unparsed events into a `Vec<(&[u8], TrackEvent)>`.
944 ///
945 /// This function is a smarter version of `Iterator::collect`, as it guesses allocations and
946 /// is usually optimized better than its naive counterpart.
947 ///
948 /// This function is only available with the `alloc` feature enabled.
949 #[cfg(feature = "alloc")]
950 #[inline]
951 pub fn into_vec(self) -> Result<Vec<(&'a [u8], TrackEvent<'a>)>> {
952 self.inner.into_vec()
953 }
954}
955impl<'a> Iterator for EventBytemapIter<'a> {
956 type Item = Result<(&'a [u8], TrackEvent<'a>)>;
957 #[inline]
958 fn next(&mut self) -> Option<Self::Item> {
959 self.inner.next()
960 }
961}