Skip to main content

rustysynth_ext/
error.rs

1use std::error;
2use std::fmt;
3use std::io;
4
5use crate::four_cc::FourCC;
6
7/// Represents an error when initializing a synthesizer.
8#[derive(Debug)]
9#[non_exhaustive]
10pub enum SynthesizerError {
11    SampleRateOutOfRange(i32),
12    BlockSizeOutOfRange(usize),
13    MaximumPolyphonyOutOfRange(usize),
14}
15
16impl error::Error for SynthesizerError {}
17
18impl fmt::Display for SynthesizerError {
19    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
20        match self {
21            SynthesizerError::SampleRateOutOfRange(value) => write!(
22                f,
23                "the sample rate must be between 16000 and 192000, but was {}",
24                value
25            ),
26            SynthesizerError::BlockSizeOutOfRange(value) => write!(
27                f,
28                "the block size must be between 8 and 1024, but was {}",
29                value
30            ),
31            SynthesizerError::MaximumPolyphonyOutOfRange(value) => {
32                write!(
33                    f,
34                    "the maximum number of polyphony must be between 8 and 256, but was {}",
35                    value
36                )
37            }
38        }
39    }
40}
41
42/// Represents an error when loading a SoundFont.
43#[derive(Debug)]
44#[non_exhaustive]
45pub enum SoundFontError {
46    IoError(io::Error),
47    RiffChunkNotFound,
48    InvalidRiffChunkType {
49        expected: FourCC,
50        actual: FourCC,
51    },
52    ListChunkNotFound,
53    InvalidListChunkType {
54        expected: FourCC,
55        actual: FourCC,
56    },
57    ListContainsUnknownId(FourCC),
58    SampleDataNotFound,
59    InvalidSampleData,
60    UnsupportedSampleFormat,
61    SampleDecompressionFailed(String),
62    SubChunkNotFound(FourCC),
63    InvalidPresetList,
64    InvalidInstrumentId {
65        preset_id: usize,
66        instrument_id: usize,
67    },
68    InvalidPreset(usize),
69    PresetNotFound,
70    InvalidInstrumentList,
71    InvalidSampleId {
72        instrument_id: usize,
73        sample_id: usize,
74    },
75    InvalidInstrument(usize),
76    InstrumentNotFound,
77    InvalidSampleHeaderList,
78    InvalidZoneList,
79    ZoneNotFound,
80    InvalidGeneratorList,
81    SanityCheckFailed,
82}
83
84impl error::Error for SoundFontError {
85    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
86        match self {
87            SoundFontError::IoError(err) => Some(err),
88            _ => None,
89        }
90    }
91}
92
93impl fmt::Display for SoundFontError {
94    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
95        match self {
96            SoundFontError::IoError(err) => err.fmt(f),
97            SoundFontError::RiffChunkNotFound => write!(f, "the RIFF chunk was not found"),
98            SoundFontError::InvalidRiffChunkType { expected, actual } => write!(
99                f,
100                "the type of the RIFF chunk must be '{}', but was '{}'",
101                expected, actual
102            ),
103            SoundFontError::ListChunkNotFound => write!(f, "the LIST chunk was not found"),
104            SoundFontError::InvalidListChunkType { expected, actual } => write!(
105                f,
106                "the type of the LIST chunk must be '{}', but was '{}'",
107                expected, actual
108            ),
109            SoundFontError::ListContainsUnknownId(id) => {
110                write!(f, "the INFO list contains an unknown ID '{id}'")
111            }
112            SoundFontError::SampleDataNotFound => write!(f, "no valid sample data was found"),
113            SoundFontError::InvalidSampleData => write!(f, "the sample data chunk is invalid"),
114            SoundFontError::UnsupportedSampleFormat => write!(
115                f,
116                "SoundFont3 is not supported; rebuild rustysynth-ext with the 'sf3' feature enabled"
117            ),
118            SoundFontError::SampleDecompressionFailed(message) => {
119                write!(f, "failed to decompress the sample data: {message}")
120            }
121            SoundFontError::SubChunkNotFound(id) => {
122                write!(f, "the '{}' sub-chunk was not found", id)
123            }
124            SoundFontError::InvalidPresetList => write!(f, "the preset list is invalid"),
125            SoundFontError::InvalidInstrumentId {
126                preset_id,
127                instrument_id,
128            } => write!(
129                f,
130                "the preset with the ID '{preset_id}' contains an invalid instrument ID '{instrument_id}'"
131            ),
132            SoundFontError::InvalidPreset(preset_id) => {
133                write!(f, "the preset with the ID '{preset_id}' has no zone")
134            }
135            SoundFontError::PresetNotFound => write!(f, "no valid preset was found"),
136            SoundFontError::InvalidInstrumentList => write!(f, "the instrument list is invalid"),
137            SoundFontError::InvalidSampleId {
138                instrument_id,
139                sample_id,
140            } => write!(
141                f,
142                "the instrument with the ID '{instrument_id}' contains an invalid sample ID '{sample_id}'"
143            ),
144            SoundFontError::InvalidInstrument(instrument_id) => {
145                write!(
146                    f,
147                    "the instrument with the ID '{instrument_id}' has no zone"
148                )
149            }
150            SoundFontError::InstrumentNotFound => write!(f, "no valid instrument was found"),
151            SoundFontError::InvalidSampleHeaderList => {
152                write!(f, "the sample header list is invalid")
153            }
154            SoundFontError::InvalidZoneList => write!(f, "the zone list is invalid"),
155            SoundFontError::ZoneNotFound => write!(f, "no valid zone was found"),
156            SoundFontError::InvalidGeneratorList => write!(f, "the generator list is invalid"),
157            SoundFontError::SanityCheckFailed => write!(f, "sanity check failed"),
158        }
159    }
160}
161
162impl From<io::Error> for SoundFontError {
163    fn from(err: io::Error) -> Self {
164        SoundFontError::IoError(err)
165    }
166}
167
168/// Represents an error when loading a MIDI file.
169#[derive(Debug)]
170#[non_exhaustive]
171pub enum MidiFileError {
172    IoError(io::Error),
173    InvalidChunkType { expected: FourCC, actual: FourCC },
174    InvalidChunkData(FourCC),
175    UnsupportedFormat(i16),
176    InvalidTimeDivision(i16),
177    InvalidTempoValue,
178    InvalidEventList,
179}
180
181impl error::Error for MidiFileError {
182    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
183        match self {
184            MidiFileError::IoError(err) => Some(err),
185            _ => None,
186        }
187    }
188}
189
190impl fmt::Display for MidiFileError {
191    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
192        match self {
193            MidiFileError::IoError(err) => err.fmt(f),
194            MidiFileError::InvalidChunkType { expected, actual } => write!(
195                f,
196                "the chunk type must be '{}', but was '{}'",
197                expected, actual
198            ),
199            MidiFileError::InvalidChunkData(id) => write!(f, "the '{}' chunk has invalid data", id),
200            MidiFileError::UnsupportedFormat(format) => {
201                write!(f, "the format {} is not supported", format)
202            }
203            MidiFileError::InvalidTimeDivision(division) => write!(
204                f,
205                "the time division must be a positive number of ticks per quarter note, but was {} (SMPTE timecode divisions are not supported)",
206                division
207            ),
208            MidiFileError::InvalidTempoValue => write!(f, "failed to read the tempo value"),
209            MidiFileError::InvalidEventList => write!(
210                f,
211                "the event list is invalid: the times must be in non-decreasing order"
212            ),
213        }
214    }
215}
216
217impl From<io::Error> for MidiFileError {
218    fn from(err: io::Error) -> Self {
219        MidiFileError::IoError(err)
220    }
221}