Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
stenzek
GitHub Repository: stenzek/duckstation
Path: blob/master/dep/cubeb/src/cubeb_resampler.cpp
4246 views
1
/*
2
* Copyright © 2014 Mozilla Foundation
3
*
4
* This program is made available under an ISC-style license. See the
5
* accompanying file LICENSE for details.
6
*/
7
#ifndef NOMINMAX
8
#define NOMINMAX
9
#endif // NOMINMAX
10
11
#include "cubeb_resampler.h"
12
#include "cubeb-speex-resampler.h"
13
#include "cubeb_resampler_internal.h"
14
#include "cubeb_utils.h"
15
#include <algorithm>
16
#include <cassert>
17
#include <cmath>
18
#include <cstddef>
19
#include <cstdio>
20
#include <cstring>
21
22
int
23
to_speex_quality(cubeb_resampler_quality q)
24
{
25
switch (q) {
26
case CUBEB_RESAMPLER_QUALITY_VOIP:
27
return SPEEX_RESAMPLER_QUALITY_VOIP;
28
case CUBEB_RESAMPLER_QUALITY_DEFAULT:
29
return SPEEX_RESAMPLER_QUALITY_DEFAULT;
30
case CUBEB_RESAMPLER_QUALITY_DESKTOP:
31
return SPEEX_RESAMPLER_QUALITY_DESKTOP;
32
default:
33
assert(false);
34
return 0XFFFFFFFF;
35
}
36
}
37
38
uint32_t
39
min_buffered_audio_frame(uint32_t sample_rate)
40
{
41
return sample_rate / 20;
42
}
43
44
template <typename T>
45
passthrough_resampler<T>::passthrough_resampler(cubeb_stream * s,
46
cubeb_data_callback cb,
47
void * ptr,
48
uint32_t input_channels,
49
uint32_t sample_rate)
50
: processor(input_channels), stream(s), data_callback(cb), user_ptr(ptr),
51
sample_rate(sample_rate)
52
{
53
}
54
55
template <typename T>
56
long
57
passthrough_resampler<T>::fill(void * input_buffer, long * input_frames_count,
58
void * output_buffer, long output_frames)
59
{
60
if (input_buffer) {
61
assert(input_frames_count);
62
}
63
assert((input_buffer && output_buffer) ||
64
(output_buffer && !input_buffer &&
65
(!input_frames_count || *input_frames_count == 0)) ||
66
(input_buffer && !output_buffer && output_frames == 0));
67
68
// When we have no pending input data and exactly as much input
69
// as output data, we don't need to copy it into the internal buffer
70
// and can directly forward it to the callback.
71
void * in_buf = input_buffer;
72
unsigned long pop_input_count = 0u;
73
if (input_buffer && !output_buffer) {
74
output_frames = *input_frames_count;
75
} else if (input_buffer) {
76
if (internal_input_buffer.length() != 0 ||
77
*input_frames_count < output_frames) {
78
// If we have pending input data left and have to first append the input
79
// so we can pass it as one pointer to the callback. Or this is a glitch.
80
// It can happen when system's performance is poor. Audible silence is
81
// being pushed at the end of the short input buffer. An improvement for
82
// the future is to resample to the output number of frames, when that
83
// happens.
84
internal_input_buffer.push(static_cast<T *>(input_buffer),
85
frames_to_samples(*input_frames_count));
86
if (internal_input_buffer.length() < frames_to_samples(output_frames)) {
87
// This is unxpected but it can happen when a glitch occurs. Fill the
88
// buffer with silence. First keep the actual number of input samples
89
// used without the silence.
90
pop_input_count = internal_input_buffer.length();
91
internal_input_buffer.push_silence(frames_to_samples(output_frames) -
92
internal_input_buffer.length());
93
} else {
94
pop_input_count = frames_to_samples(output_frames);
95
}
96
in_buf = internal_input_buffer.data();
97
} else if (*input_frames_count > output_frames) {
98
// In this case we have more input that we need output and
99
// fill the overflowing input into internal_input_buffer
100
// Since we have no other pending data, we can nonetheless
101
// pass the current input data directly to the callback
102
assert(pop_input_count == 0);
103
unsigned long samples_off = frames_to_samples(output_frames);
104
internal_input_buffer.push(
105
static_cast<T *>(input_buffer) + samples_off,
106
frames_to_samples(*input_frames_count - output_frames));
107
}
108
}
109
110
long rv =
111
data_callback(stream, user_ptr, in_buf, output_buffer, output_frames);
112
113
if (input_buffer) {
114
if (pop_input_count) {
115
internal_input_buffer.pop(nullptr, pop_input_count);
116
*input_frames_count = samples_to_frames(pop_input_count);
117
} else {
118
*input_frames_count = output_frames;
119
}
120
drop_audio_if_needed();
121
}
122
123
return rv;
124
}
125
126
// Explicit instantiation of template class.
127
template class passthrough_resampler<float>;
128
template class passthrough_resampler<short>;
129
130
template <typename T, typename InputProcessor, typename OutputProcessor>
131
cubeb_resampler_speex<T, InputProcessor, OutputProcessor>::
132
cubeb_resampler_speex(InputProcessor * input_processor,
133
OutputProcessor * output_processor, cubeb_stream * s,
134
cubeb_data_callback cb, void * ptr)
135
: input_processor(input_processor), output_processor(output_processor),
136
stream(s), data_callback(cb), user_ptr(ptr)
137
{
138
if (input_processor && output_processor) {
139
fill_internal = &cubeb_resampler_speex::fill_internal_duplex;
140
} else if (input_processor) {
141
fill_internal = &cubeb_resampler_speex::fill_internal_input;
142
} else if (output_processor) {
143
fill_internal = &cubeb_resampler_speex::fill_internal_output;
144
}
145
}
146
147
template <typename T, typename InputProcessor, typename OutputProcessor>
148
cubeb_resampler_speex<T, InputProcessor,
149
OutputProcessor>::~cubeb_resampler_speex()
150
{
151
}
152
153
template <typename T, typename InputProcessor, typename OutputProcessor>
154
long
155
cubeb_resampler_speex<T, InputProcessor, OutputProcessor>::fill(
156
void * input_buffer, long * input_frames_count, void * output_buffer,
157
long output_frames_needed)
158
{
159
/* Input and output buffers, typed */
160
T * in_buffer = reinterpret_cast<T *>(input_buffer);
161
T * out_buffer = reinterpret_cast<T *>(output_buffer);
162
return (this->*fill_internal)(in_buffer, input_frames_count, out_buffer,
163
output_frames_needed);
164
}
165
166
template <typename T, typename InputProcessor, typename OutputProcessor>
167
long
168
cubeb_resampler_speex<T, InputProcessor, OutputProcessor>::fill_internal_output(
169
T * input_buffer, long * input_frames_count, T * output_buffer,
170
long output_frames_needed)
171
{
172
assert(!input_buffer && (!input_frames_count || *input_frames_count == 0) &&
173
output_buffer && output_frames_needed);
174
175
if (!draining) {
176
long got = 0;
177
T * out_unprocessed = nullptr;
178
long output_frames_before_processing = 0;
179
180
/* fill directly the input buffer of the output processor to save a copy */
181
output_frames_before_processing =
182
output_processor->input_needed_for_output(output_frames_needed);
183
184
out_unprocessed =
185
output_processor->input_buffer(output_frames_before_processing);
186
187
got = data_callback(stream, user_ptr, nullptr, out_unprocessed,
188
output_frames_before_processing);
189
190
if (got < output_frames_before_processing) {
191
draining = true;
192
193
if (got < 0) {
194
return got;
195
}
196
}
197
198
output_processor->written(got);
199
}
200
201
/* Process the output. If not enough frames have been returned from the
202
* callback, drain the processors. */
203
return output_processor->output(output_buffer, output_frames_needed);
204
}
205
206
template <typename T, typename InputProcessor, typename OutputProcessor>
207
long
208
cubeb_resampler_speex<T, InputProcessor, OutputProcessor>::fill_internal_input(
209
T * input_buffer, long * input_frames_count, T * output_buffer,
210
long /*output_frames_needed*/)
211
{
212
assert(input_buffer && input_frames_count && *input_frames_count &&
213
!output_buffer);
214
215
/* The input data, after eventual resampling. This is passed to the callback.
216
*/
217
T * resampled_input = nullptr;
218
uint32_t resampled_frame_count =
219
input_processor->output_for_input(*input_frames_count);
220
221
/* process the input, and present exactly `output_frames_needed` in the
222
* callback. */
223
input_processor->input(input_buffer, *input_frames_count);
224
225
/* resampled_frame_count == 0 happens if the resampler
226
* doesn't have enough input frames buffered to produce 1 resampled frame. */
227
if (resampled_frame_count == 0) {
228
return *input_frames_count;
229
}
230
231
size_t frames_resampled = 0;
232
resampled_input =
233
input_processor->output(resampled_frame_count, &frames_resampled);
234
*input_frames_count = frames_resampled;
235
236
long got = data_callback(stream, user_ptr, resampled_input, nullptr,
237
resampled_frame_count);
238
239
/* Return the number of initial input frames or part of it.
240
* Since output_frames_needed == 0 in input scenario, the only
241
* available number outside resampler is the initial number of frames. */
242
return (*input_frames_count) * (got / resampled_frame_count);
243
}
244
245
template <typename T, typename InputProcessor, typename OutputProcessor>
246
long
247
cubeb_resampler_speex<T, InputProcessor, OutputProcessor>::fill_internal_duplex(
248
T * in_buffer, long * input_frames_count, T * out_buffer,
249
long output_frames_needed)
250
{
251
if (draining) {
252
// discard input and drain any signal remaining in the resampler.
253
return output_processor->output(out_buffer, output_frames_needed);
254
}
255
256
/* The input data, after eventual resampling. This is passed to the callback.
257
*/
258
T * resampled_input = nullptr;
259
/* The output buffer passed down in the callback, that might be resampled. */
260
T * out_unprocessed = nullptr;
261
long output_frames_before_processing = 0;
262
/* The number of frames returned from the callback. */
263
long got = 0;
264
265
/* We need to determine how much frames to present to the consumer.
266
* - If we have a two way stream, but we're only resampling input, we resample
267
* the input to the number of output frames.
268
* - If we have a two way stream, but we're only resampling the output, we
269
* resize the input buffer of the output resampler to the number of input
270
* frames, and we resample it afterwards.
271
* - If we resample both ways, we resample the input to the number of frames
272
* we would need to pass down to the consumer (before resampling the output),
273
* get the output data, and resample it to the number of frames needed by the
274
* caller. */
275
276
output_frames_before_processing =
277
output_processor->input_needed_for_output(output_frames_needed);
278
/* fill directly the input buffer of the output processor to save a copy */
279
out_unprocessed =
280
output_processor->input_buffer(output_frames_before_processing);
281
282
if (in_buffer) {
283
/* process the input, and present exactly `output_frames_needed` in the
284
* callback. */
285
input_processor->input(in_buffer, *input_frames_count);
286
287
size_t frames_resampled = 0;
288
resampled_input = input_processor->output(output_frames_before_processing,
289
&frames_resampled);
290
*input_frames_count = frames_resampled;
291
} else {
292
resampled_input = nullptr;
293
}
294
295
got = data_callback(stream, user_ptr, resampled_input, out_unprocessed,
296
output_frames_before_processing);
297
298
if (got < output_frames_before_processing) {
299
draining = true;
300
301
if (got < 0) {
302
return got;
303
}
304
}
305
306
output_processor->written(got);
307
308
input_processor->drop_audio_if_needed();
309
310
/* Process the output. If not enough frames have been returned from the
311
* callback, drain the processors. */
312
got = output_processor->output(out_buffer, output_frames_needed);
313
314
output_processor->drop_audio_if_needed();
315
316
return got;
317
}
318
319
/* Resampler C API */
320
321
cubeb_resampler *
322
cubeb_resampler_create(cubeb_stream * stream,
323
cubeb_stream_params * input_params,
324
cubeb_stream_params * output_params,
325
unsigned int target_rate, cubeb_data_callback callback,
326
void * user_ptr, cubeb_resampler_quality quality,
327
cubeb_resampler_reclock reclock)
328
{
329
cubeb_sample_format format;
330
331
assert(input_params || output_params);
332
333
if (input_params) {
334
format = input_params->format;
335
} else {
336
format = output_params->format;
337
}
338
339
switch (format) {
340
case CUBEB_SAMPLE_S16NE:
341
return cubeb_resampler_create_internal<short>(
342
stream, input_params, output_params, target_rate, callback, user_ptr,
343
quality, reclock);
344
case CUBEB_SAMPLE_FLOAT32NE:
345
return cubeb_resampler_create_internal<float>(
346
stream, input_params, output_params, target_rate, callback, user_ptr,
347
quality, reclock);
348
default:
349
assert(false);
350
return nullptr;
351
}
352
}
353
354
long
355
cubeb_resampler_fill(cubeb_resampler * resampler, void * input_buffer,
356
long * input_frames_count, void * output_buffer,
357
long output_frames_needed)
358
{
359
return resampler->fill(input_buffer, input_frames_count, output_buffer,
360
output_frames_needed);
361
}
362
363
void
364
cubeb_resampler_destroy(cubeb_resampler * resampler)
365
{
366
delete resampler;
367
}
368
369
long
370
cubeb_resampler_latency(cubeb_resampler * resampler)
371
{
372
return resampler->latency();
373
}
374
375