1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
use core::fmt;
use std::error;
/// What `getopt` returns for an option it does not know, or one whose
/// value is missing.
const UNRECOGNISED_OPTION: i32 = b'?' as i32;
/// Errors returned by the Bela audio system lifecycle.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum Error {
/// `Bela_initAudio` failed with the contained return code.
///
/// The initialisation it failed partway through is not undone, so
/// this is fatal to the process rather than to the one attempt:
/// every later [`Bela::new`](crate::Bela::new) returns
/// [`AudioSystemPoisoned`](Self::AudioSystemPoisoned).
Init(i32),
/// `Bela_startAudio` failed with the contained return code.
Start(i32),
/// The run ended with the contained number of callbacks refused
/// for breaking the protocol the render states rely on.
///
/// libbela made a callback somewhere the crate could not hand out
/// the references [`BelaApplication`](crate::BelaApplication)
/// promises — several `render` calls with the same thread number,
/// or a `render_post` arriving while one was still in flight, which
/// a stop requested mid-block can produce. Each such callback was
/// skipped and a stop requested, so the audio that was rendered is
/// sound and the run ended early rather than going wrong.
///
/// Reported by [`Bela::until_stopped`](crate::Bela::until_stopped)
/// and the `run` methods built on it, so that a run which ended
/// this way is not mistaken for one that was asked to stop. See
/// [`Bela::callback_faults`](crate::Bela::callback_faults).
CallbackFaults(u32),
/// An auxiliary task name contained a NUL byte.
TaskName,
/// `Bela_createAuxiliaryTask` failed, or the crate was built for a
/// target with no audio system to create the task in.
TaskCreate,
/// An auxiliary task was created while an audio system was being
/// torn down, which would have deleted it again immediately.
///
/// This is what a `cleanup` callback gets: it runs inside that
/// teardown.
TaskCreateWhileStopping,
/// `Bela_cpuMonitoringInit` failed.
CpuMonitoring,
/// The requested CPU monitoring acquisition cycle does not fit in a
/// C `int`, which is how libbela takes it.
CpuMonitoringCycle(u32),
/// CPU monitoring was requested with a period size big enough that
/// libbela runs `render` on a different thread from the one it
/// measures.
///
/// See
/// [`MAX_MONITORED_PERIOD_SIZE`](crate::MAX_MONITORED_PERIOD_SIZE).
CpuMonitoringPeriodSize(i32),
/// Another [`Bela`](crate::Bela) audio system already exists in
/// this process.
///
/// The C API is a process-wide singleton, so a second one would
/// share — and reset — the state the first is using.
AudioSystemExists,
/// An earlier `Bela_initAudio` in this process failed partway
/// through, and no audio system can be built after that.
///
/// libbela is left believing the audio system is up and offers no
/// way to put it back: `Bela_cleanupAudio` segfaults on that path.
/// So this is refused rather than attempted — going ahead means a
/// segfault inside libbela, which is what the error replaces.
///
/// Terminal for the process, and only for the process: the board is
/// untouched, so a new one gets a working audio system straight
/// away. See [`Bela::new`](crate::Bela::new).
AudioSystemPoisoned,
/// An argument was not one of Bela's standard command-line options.
///
/// Carries what `Bela_getopt_long` returned: `'?'` for an
/// unrecognised option or one missing its value, which `getopt` has
/// already reported on standard error naming the argument, or an
/// internal option code when libbela rejected a standard option it
/// did recognise — a `--json-file` it could not read, say.
CommandLine(i32),
/// A command-line argument contained a NUL byte, which a C string
/// cannot carry.
CommandLineNul,
/// `Bela_setLineOutLevel` failed with the contained return code,
/// e.g. for a channel the codec does not have.
LineOutLevel(i32),
/// `Bela_setHpLevel` failed with the contained return code, e.g.
/// for a channel the codec does not have.
HeadphoneLevel(i32),
/// `Bela_setAudioInputGain` failed with the contained return code.
AudioInputGain(i32),
/// `Bela_muteSpeakers` failed with the contained return code.
MuteSpeakers(i32),
/// A level or gain was not a number of decibels libbela can convert
/// into register values: not finite, or larger in magnitude than
/// [`MAX_DECIBELS`](crate::MAX_DECIBELS).
///
/// The conversion on the C side is a cast to `int`, which is
/// undefined behaviour for those values, so they are refused before
/// the call rather than passed on.
Decibels,
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Init(code) => write!(f, "Bela_initAudio failed with code {code}"),
Self::Start(code) => write!(f, "Bela_startAudio failed with code {code}"),
Self::CallbackFaults(faults) => write!(
f,
"{faults} callback(s) were refused for breaking the protocol the render states \
rely on, and the audio system was asked to stop"
),
Self::TaskName => write!(f, "the auxiliary task name contains a NUL byte"),
Self::TaskCreate => write!(f, "Bela_createAuxiliaryTask failed"),
Self::TaskCreateWhileStopping => write!(
f,
"auxiliary tasks cannot be created while the audio system is stopping"
),
Self::CpuMonitoring => write!(f, "Bela_cpuMonitoringInit failed"),
Self::CpuMonitoringCycle(count) => write!(
f,
"the CPU monitoring cycle is {count} measurements, \
which does not fit in the C int libbela takes"
),
Self::CpuMonitoringPeriodSize(frames) => write!(
f,
"CPU monitoring needs a period size of at most {max} frames, not {frames}: \
above that libbela renders on a separate thread from the one it measures",
max = crate::MAX_MONITORED_PERIOD_SIZE
),
Self::AudioSystemExists => write!(
f,
"a Bela audio system already exists in this process; the C API is a singleton"
),
Self::AudioSystemPoisoned => write!(
f,
"an earlier Bela_initAudio failed in this process, leaving libbela with an audio \
system it will not give back; start a new process"
),
Self::CommandLine(code) if *code == UNRECOGNISED_OPTION => write!(
f,
"an argument is not one of Bela's standard options, or is missing its value"
),
Self::CommandLine(code) => write!(
f,
"the command line was rejected by Bela_getopt_long, which returned {code}"
),
Self::CommandLineNul => {
write!(f, "a command-line argument contains a NUL byte")
}
Self::LineOutLevel(code) => {
write!(f, "Bela_setLineOutLevel failed with code {code}")
}
Self::HeadphoneLevel(code) => write!(f, "Bela_setHpLevel failed with code {code}"),
Self::AudioInputGain(code) => {
write!(f, "Bela_setAudioInputGain failed with code {code}")
}
Self::MuteSpeakers(code) => write!(f, "Bela_muteSpeakers failed with code {code}"),
Self::Decibels => write!(
f,
"a level must be a finite number of decibels of at most {max} in magnitude, \
which is what libbela can convert into register values",
max = crate::MAX_DECIBELS
),
}
}
}
impl error::Error for Error {}