Pulp 1.88.0
Cross-platform audio plugin framework
Loading...
Searching...
No Matches
smf.hpp
Go to the documentation of this file.
1#pragma once
2
5
6#include <cstddef>
7#include <cstdint>
8#include <span>
9#include <string>
10#include <vector>
11
12namespace pulp::timeline {
13
14// Import and export of Standard MIDI Files (SMF, RP-001) against the project's
15// tempo and meter maps.
16//
17// An SMF carries its own musical timebase: a header division in ticks per
18// quarter note, plus Set Tempo and Time Signature meta-events that place tempo
19// and meter changes on that grid. Conversion therefore stays entirely in the
20// musical domain — SMF ticks scale to canonical timebase::kTicksPerQuarter
21// ticks, and the file's tempo/meter meta-events become the Project's TempoMap
22// and MeterMap. No seconds-domain flattening is involved in either direction,
23// so a tempo change mid-file survives the round trip as a tempo point rather
24// than as pre-multiplied wall-clock positions.
25//
26// Tick exactness. `canonical = round(smf_tick * kTicksPerQuarter / division)`
27// is exact for every tick when `kTicksPerQuarter % division == 0` (true for
28// 96, 120, 192, 240, 480, 960, and every other divisor of 705600; false for
29// 384 and 1920). Otherwise the conversion rounds half away from zero and the
30// error is bounded by 0.5 canonical ticks; SmfImport reports both facts.
31// Export is exact by default: a canonical tick that the requested division
32// cannot represent is an error unless `allow_lossy_tick_rounding` is set.
33//
34// Documented import subset (everything outside it fails closed):
35// * MThd format 0 or 1; a metrical (non-SMPTE) division
36// * MTrk chunks only, exactly the count the header declares, no trailing bytes
37// * Note On (0x9n) and Note Off (0x8n); a Note On with velocity 0 is a
38// Note Off, per the specification
39// * Meta events Set Tempo (0x51), Time Signature (0x58),
40// Sequence/Track Name (0x03), and End of Track (0x2F)
41// Any other channel message, system-exclusive block, or meta event is rejected
42// unless the caller explicitly opts into SmfUnsupportedEventPolicy::IgnoreNonNote.
43//
44// Fields carried by supported events that have no timeline counterpart are
45// documented rather than silently reinterpreted: the Time Signature event's
46// metronome hints (MIDI clocks per click, 32nd notes per quarter) are not
47// retained, and export writes the conventional 24/8.
48//
49// Export covers note content plus the tempo and meter maps. Device chains,
50// automation lanes, takes, freezes, and media assets have no SMF
51// representation and are outside the format's scope. Per-note probability,
52// conditions, and ratchets are playback semantics rather than fixed MIDI
53// events, so exporting a clip that authors them fails with UnsupportedFeature.
57
73
92
102
104enum class SmfUnsupportedEventPolicy : std::uint8_t {
109};
110
118 std::size_t max_file_bytes = 64u * 1024u * 1024u;
120 std::size_t max_tracks = 4'096;
122 std::size_t max_events = 5'000'000;
124 std::size_t max_notes = 5'000'000;
126 std::size_t max_concurrent_notes = 65'536;
128 std::size_t max_tempo_points = 65'536;
130 std::size_t max_meter_points = 65'536;
132 std::size_t max_payload_bytes = 1u * 1024u * 1024u;
134 std::size_t max_track_name_bytes = 4'096;
136 std::int64_t max_smf_ticks = std::int64_t{1} << 34;
137};
138
146
148struct SmfImport {
152 std::uint16_t division = 0;
156 std::int64_t max_tick_rounding_error = 0;
157};
158
163runtime::Result<SmfImport, SmfError> import_smf(std::span<const std::uint8_t> file_bytes);
164
174runtime::Result<SmfImport, SmfError> import_smf(std::span<const std::uint8_t> file_bytes,
175 const SmfImportOptions& options);
176
180 std::uint16_t ticks_per_quarter = 960;
186 std::size_t max_events = 5'000'000;
187};
188
190struct SmfExport {
192 std::vector<std::uint8_t> bytes;
196 std::int64_t max_tick_rounding_error = 0;
197};
198
204
215 const SmfExportOptions& options);
216
218
219} // namespace pulp::timeline
Result<T, E> — either holds a T or an E, never both.
Definition result.hpp:76
Immutable root of a Timeline document and its monotonic identity domain.
Definition model.hpp:1079
SmfUnsupportedEventPolicy
Policy for a well-formed event outside the documented import subset.
Definition smf.hpp:104
runtime::Result< SmfExport, SmfError > export_smf(const Project &project)
Encodes a Project's root sequence as a format-1 SMF with strict defaults.
SmfErrorCode
Stable category for an SMF import or export failure.
Definition smf.hpp:75
runtime::Result< SmfImport, SmfError > import_smf(std::span< const std::uint8_t > file_bytes)
Decodes SMF bytes into a Project using default limits and strict event policy.
@ IgnoreNonNote
Discard well-formed non-note channel, system-exclusive, and meta events.
Definition smf.hpp:108
@ Reject
Reject the file so an unhandled event is never silently dropped.
Definition smf.hpp:106
@ MissingHeader
No MThd chunk starts the input.
Definition smf.hpp:77
@ MeterMapRejected
The assembled meter map is invalid.
Definition smf.hpp:89
@ UnsupportedDivision
The division is SMPTE-based or zero.
Definition smf.hpp:80
@ InexactTickConversion
A requested export tick is not exactly representable.
Definition smf.hpp:85
@ Truncated
Input ended inside a chunk, event, or payload.
Definition smf.hpp:76
@ TempoMapRejected
The assembled tempo map is invalid.
Definition smf.hpp:88
@ MalformedEvent
Running status, variable length, or meta length is invalid.
Definition smf.hpp:82
@ UnsupportedFormat
The file uses independent-sequence format 2.
Definition smf.hpp:79
@ InvalidHeader
The MThd contents are unusable.
Definition smf.hpp:78
@ UnbalancedNote
A note-on/off pair is incomplete.
Definition smf.hpp:83
@ TickRangeExceeded
A tick exceeds the SMF or canonical domain.
Definition smf.hpp:86
@ InvalidValue
An attribute is unparseable or out of range.
Definition dawproject_import.hpp:45
@ LimitExceeded
A configured import resource limit was exceeded.
Definition dawproject_import.hpp:50
@ ModelRejected
The assembled Project violates a model invariant.
Definition dawproject_import.hpp:49
@ UnsupportedFeature
A construct lies outside the accepted subset.
Definition dawproject_import.hpp:43
Definition program_compiler.hpp:18
Result<T, E> — success / failure union for fallible operations.
Model failure with the offending and, when relevant, conflicting identity.
Definition clip.hpp:95
Diagnostic returned when an SMF operation rejects the whole conversion.
Definition smf.hpp:94
std::string message
Human-readable detail naming the offending chunk, event, or value.
Definition smf.hpp:98
ModelError model_error
Underlying model failure when code is ModelRejected.
Definition smf.hpp:100
SmfErrorCode code
Failure category.
Definition smf.hpp:96
Tick-grid, loss policy, and work bound for SMF export.
Definition smf.hpp:178
bool skip_non_note_clips
Whether clips with non-note, non-empty content may be omitted.
Definition smf.hpp:184
bool allow_lossy_tick_rounding
Whether unrepresentable ticks may round half away from zero.
Definition smf.hpp:182
std::uint16_t ticks_per_quarter
MThd ticks per quarter; 960 is exact against the canonical tick grid.
Definition smf.hpp:180
std::size_t max_events
Maximum emitted MIDI events across the whole file.
Definition smf.hpp:186
Encoded SMF bytes plus tick-conversion fidelity metadata.
Definition smf.hpp:190
std::vector< std::uint8_t > bytes
Complete encoded SMF bytes.
Definition smf.hpp:192
bool exact_tick_conversion
Whether every emitted tick was represented exactly.
Definition smf.hpp:194
std::int64_t max_tick_rounding_error
Worst canonical-to-reconstructed tick error over emitted events.
Definition smf.hpp:196
Hard resource ceilings for one SMF import.
Definition smf.hpp:116
std::size_t max_meter_points
Maximum Time Signature events.
Definition smf.hpp:130
std::size_t max_track_name_bytes
Maximum decoded bytes in one track name.
Definition smf.hpp:134
std::int64_t max_smf_ticks
Absolute SMF tick ceiling for any event.
Definition smf.hpp:136
std::size_t max_tempo_points
Maximum Set Tempo events.
Definition smf.hpp:128
std::size_t max_notes
Maximum completed notes across the whole file.
Definition smf.hpp:124
std::size_t max_events
Maximum decoded events across the whole file.
Definition smf.hpp:122
std::size_t max_concurrent_notes
Maximum simultaneously open notes on one track.
Definition smf.hpp:126
std::size_t max_file_bytes
Raw file bytes, checked before decoding.
Definition smf.hpp:118
std::size_t max_payload_bytes
Maximum bytes in one meta-event or system-exclusive payload.
Definition smf.hpp:132
std::size_t max_tracks
Maximum MTrk chunks.
Definition smf.hpp:120
Caller-selected resource and unsupported-event policy for SMF import.
Definition smf.hpp:140
SmfUnsupportedEventPolicy unsupported_events
Policy for well-formed events outside the note subset.
Definition smf.hpp:144
SmfImportLimits limits
Resource ceilings.
Definition smf.hpp:142
Imported immutable Project plus tick-conversion fidelity metadata.
Definition smf.hpp:148
Project project
Atomically assembled Timeline project.
Definition smf.hpp:150
std::uint16_t division
MThd ticks-per-quarter division.
Definition smf.hpp:152
bool exact_tick_conversion
Whether every imported SMF tick has an exact canonical representation.
Definition smf.hpp:154
std::int64_t max_tick_rounding_error
Worst imported tick rounding error in canonical ticks.
Definition smf.hpp:156