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
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
|
The device_sound_interface
==========================
.. contents:: :local:
1. The sound system
-------------------
The device sound interface is the entry point for devices that handle
sound input and/or output. The sound system is built on the concept
of *streams* which connect devices together with resampling and mixing
applied transparently as needed. Microphones (audio input) and
speakers (audio output) are specific known devices which use the same
interface.
2. Devices using device_sound_interface
---------------------------------------
2.1 Initialisation
~~~~~~~~~~~~~~~~~~
Sound streams must be created in the device_start (or
interface_pre_start) method.
.. code-block:: C++
sound_stream *stream_alloc(int inputs, int outputs, int sample_rate, sound_stream_flags flags = STREAM_DEFAULT_FLAGS);
A stream is created with ``stream_alloc``. It takes the number of
input and output channels, the sample rate and optionally flags.
The sample rate can be SAMPLE_RATE_INPUT_ADAPTIVE,
SAMPLE_RATE_OUTPUT_ADAPTIVE or SAMPLE_RATE_ADAPTIVE. In that case the
chosen sample rate is the highest one among the inputs, outputs or
both respectively. In case of loop, the chosen sample rate is the
configured global sample rate.
The only available non-default flag is STREAM_SYNCHRONOUS. When set,
the sound generation method will be called for every sample
individually. It's necessary for DSPs that run a program on every
sample. but on the other hand it's expensive, so only to be used when
required.
Devices can create multiple streams. It's rare though. Some Yamaha
chips should but don't. Inputs and outputs are numbered from 0 and
arrange all streams in the order they are created.
2.2 Sound input/output
~~~~~~~~~~~~~~~~~~~~~~
.. code-block:: C++
virtual void sound_stream_update(sound_stream &stream);
This method is required to be implemented to consume the input samples
and/or compute the output ones. The stream to update for is passed as
the parameter. See the streams section, specifically sample access,
to see how to write the method.
2.3 Stream information
~~~~~~~~~~~~~~~~~~~~~~
.. code-block:: C++
int inputs() const;
int outputs() const;
std::pair<sound_stream *, int> input_to_stream_input(int inputnum) const;
std::pair<sound_stream *, int> output_to_stream_output(int outputnum) const;
The method ``inputs`` returns the total number of inputs in the
streams created by the device. The method ``outputs`` similarly
counts the outputs. The other two methods allow to grab the stream
and channel number for the device corresponding to the global input or
output number.
2.4 Gain management
~~~~~~~~~~~~~~~~~~~
.. code-block:: C++
float input_gain(int inputnum) const;
float output_gain(int outputnum) const;
void set_input_gain(int inputnum, float gain);
void set_output_gain(int outputnum, float gain);
void set_route_gain(int source_channel, device_sound_interface *target, int target_channel, float gain);
float user_output_gain() const;
float user_output_gain(int outputnum) const;
void set_user_output_gain(float gain);
void set_user_output_gain(int outputnum, float gain);
Those methods allow to set the gain on every step of the routes
between streams. All gains are multipliers, with default value 1.0.
The steps are, from samples output in ``sound_stream_update`` to
samples read in the next device's ``sound_stream_update``:
* Per-channel output gain
* Per-channel user output gain
* Per-device user output gain
* Per-route gain
* Per-channel input gain
The user gains must not be set from the driver, they're for use by the
user interface (the sliders) and are saved in the game configuration.
The other gains are for driver/device use, and are saved in save
states.
2.5 Routing setup
~~~~~~~~~~~~~~~~~
.. code-block:: C++
device_sound_interface &add_route(u32 output, const device_finder<T, R> &target, double gain, u32 channel = 0)
device_sound_interface &add_route(u32 output, const char *target, double gain, u32 channel = 0);
device_sound_interface &add_route(u32 output, device_sound_interface &target, double gain, u32 channel = 0);
device_sound_interface &reset_routes();
Routes between devices, e.g. between streams, are set at configuration
time. The method ``add_route`` must be called on the source device
and gives the channel on the source device, the target device, the
gain, and optionally the channel on the target device. The constant
``ALL_OUTPUTS`` can be used to add a route from every channel of the
source to a given channel of the destination.
The method ``reset_routes`` is used to remove all the routes setup on
a given source device.
.. code-block:: C++
u32 get_sound_requested_inputs() const;
u32 get_sound_requested_outputs() const;
u64 get_sound_requested_inputs_mask() const;
u64 get_sound_requested_outputs_mask() const;
Those methods are useful for devices which want to behave differently
depending on what routes are set up on them. You get either the max
number of requested channel plus one (which is the number of channels
when all channels are routed, but is more useful when there are gaps)
or a mask of use for channels 0-63. Note that ``ALL_OUTPUTS`` does
not register any specific output or output count.
3. Streams
----------
3.1 Generalities
~~~~~~~~~~~~~~~~
Streams are endpoints associated with devices and, when connected
together, ensure the transmission of audio data between them. A
stream has a number of inputs (which can be zero) and outputs (same)
and one sample rate which is common to all inputs and outputs. The
connections are set up at the machine configuration level and the sound
system ensures mixing and resampling is done transparently.
Samples in streams are encoded as sample_t. In the current
implementation, this is a float. Nominal values are between -1 and 1,
but clamping at the device level is not recommended (unless that's
what happens in hardware of course) because the gain values, volume
and effects can easily avoid saturation.
They are implemented in the class ``sound_stream``.
3.2 Characteristics
~~~~~~~~~~~~~~~~~~~
.. code-block:: C++
device_t &device() const;
bool input_adaptive() const;
bool output_adaptive() const;
bool synchronous() const;
u32 input_count() const;
u32 output_count() const;
u32 sample_rate() const;
attotime sample_period() const;
3.3 Sample access
~~~~~~~~~~~~~~~~~
.. code-block:: C++
s32 samples() const;
void put(s32 output, s32 index, sample_t sample);
void put_clamp(s32 output, s32 index, sample_t sample, sample_t clamp = 1.0);
void put_int(s32 output, s32 index, s32 sample, s32 max);
void put_int_clamp(s32 output, s32 index, s32 sample, s32 maxclamp);
void add(s32 output, s32 index, sample_t sample);
void add_int(s32 output, s32 index, s32 sample, s32 max);
void fill(s32 output, sample_t value, s32 start, s32 count);
void fill(s32 output, sample_t value, s32 start);
void fill(s32 output, sample_t value);
void copy(s32 output, s32 input, s32 start, s32 count);
void copy(s32 output, s32 input, s32 start);
void copy(s32 output, s32 input);
sample_t get(s32 input, s32 index) const;
sample_t get_output(s32 output, s32 index) const;
Those are the methods used to implement ``sound_stream_update``.
First ``samples`` tells how many samples to consume and/or generate.
The to-generate samples, if any, are pre-cleared (e.g. set to zero).
Input samples are retrieved with ``get``, where ``input`` is the
stream channel number and ``index`` the sample number.
Generated samples are written with the put variants. ``put`` sets a
sample_t in channel ``output`` at position ``index``. ``put_clamp``
does the same but first clamps the value to +/-``clamp``. ``put_int``
does it with an integer ``sample`` but pre-divides it by ``max``.
``put_int_clamp`` does the same but also pre-clamps within
-``maxclamp`` and ``maxclamp``-1, which is the normal range for a
2-complement value.
``add`` and ``add_int`` are similar but add the value of the sample to
what's there instead of replacing. ``get_output`` gets the currently
stored output value.
``fill`` sets a range of an output channel to a given ``value``.
``start`` tells where to start (default index 0), ``count`` how many
(default up to the end of the buffer).
``copy`` does the same as fill but gets its value from the indentical
position in an input channel.
Note that clamping should not be used unless it actually happens in
hardware. Between gains and effects there is a fair chance saturation
can be avoided later in the chain.
3.4 Gain management
~~~~~~~~~~~~~~~~~~~
.. code-block:: C++
float user_output_gain() const;
void set_user_output_gain(float gain);
float user_output_gain(s32 output) const;
void set_user_output_gain(s32 output, float gain);
float input_gain(s32 input) const;
void set_input_gain(s32 input, float gain);
void apply_input_gain(s32 input, float gain);
float output_gain(s32 output) const;
void set_output_gain(s32 output, float gain);
void apply_output_gain(s32 output, float gain);
This is similar to the device gain control, with a twist: apply
multiplies the current gain by the given value.
3.5 Misc. actions
~~~~~~~~~~~~~~~~~
.. code-block:: C++
void set_sample_rate(u32 sample_rate);
void update();
The method ``set_sample_rate`` allows to change the sample rate of the
stream. The method ``update`` triggers a call of
``sound_stream_update`` on the stream and the ones it depends on to
reach the current time in terms of samples.
4. Devices using device_mixer_interface
---------------------------------------
The device mixer interface is used for devices that want to relay
sound in the device tree without acting on it. It's very useful on
for instance slot devices connectors, where the slot device may have
an audio connection with the main system. They are routed like every
other sound device, create the streams automatically and copy input to
output. Nothing needs to be done in the device.
|