-
Notifications
You must be signed in to change notification settings - Fork 9
/
resampler.c
375 lines (310 loc) · 10.1 KB
/
resampler.c
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
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
/*
* JMPXRDS, an FM MPX signal generator with RDS support on
* top of Jack Audio Connection Kit - Resampler
*
* Copyright (C) 2015 Nick Kossifidis <mickflemm@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "resampler.h"
#include "utils.h"
#include <stdlib.h> /* For NULL */
#include <string.h> /* For memset/memcpy */
#include <jack/thread.h> /* For thread handling through jack */
/*********\
* HELPERS *
\*********/
static void
resampler_thread_run(struct resampler_thread_data *rstd)
{
rstd->result = soxr_process(rstd->resampler, &rstd->in, rstd->inframes,
&rstd->frames_used, &rstd->out, rstd->outframes,
&rstd->frames_generated);
}
#ifdef JMPXRDS_MT
static void*
resampler_loop(void *arg)
{
struct resampler_thread_data *rstd = (struct resampler_thread_data*) arg;
while((*rstd->active)) {
pthread_mutex_lock(&rstd->proc_mutex);
while (pthread_cond_wait(&rstd->proc_trigger, &rstd->proc_mutex) != 0);
if(!(*rstd->active))
break;
resampler_thread_run(rstd);
pthread_mutex_unlock(&rstd->proc_mutex);
/* Let the caller know we are done */
pthread_mutex_lock(&rstd->done_mutex);
pthread_cond_signal(&rstd->done_trigger);
pthread_mutex_unlock(&rstd->done_mutex);
}
return arg;
}
#endif
static int
resampler_init_upsampler_threads(struct resampler_data *rsmpl)
{
struct resampler_thread_data *rstd_l = &rsmpl->rstd_l;
struct resampler_thread_data *rstd_r = &rsmpl->rstd_r;
#ifdef JMPXRDS_MT
int ret = 0;
#endif
rstd_l->resampler = rsmpl->audio_upsampler_l;
rstd_l->active = &rsmpl->active;
rstd_r->resampler = rsmpl->audio_upsampler_r;
rstd_r->active = &rsmpl->active;
#ifdef JMPXRDS_MT
pthread_mutex_init(&rstd_l->proc_mutex, NULL);
pthread_cond_init(&rstd_l->proc_trigger, NULL);
pthread_mutex_init(&rstd_l->done_mutex, NULL);
pthread_cond_init(&rstd_l->done_trigger, NULL);
ret = jack_client_create_thread(rsmpl->fmmod_client, &rstd_l->tid,
jack_client_real_time_priority(rsmpl->fmmod_client),
jack_is_realtime(rsmpl->fmmod_client),
resampler_loop, (void *) rstd_l);
if(ret < 0) {
utils_err("[JACKD] Could not create processing thread\n");
return -1;
}
#endif
return 0;
}
/**************\
* ENTRY POINTS *
\**************/
/*
* Since we oscilate the sound using high frequency signals from the
* main oscilator, we need to upsample the sound to the sample rate of
* the main oscilator and do our processing at that sampling rate. Same
* goes for RDS which operates at a much smaller sampling rate than audio.
* After the processing is done we again need to downsample the result
* (the MPX signal) to the sample rate of the sound card (jack's sample
* rate), so that it can go out. That's the purpose of the resampler
* implemented here.
*/
/* Upsample audio to the main oscilator's sampling rate */
int
resampler_upsample_audio(struct resampler_data *rsmpl,
const float *in_l, const float *in_r,
float *out_l, float *out_r,
uint32_t inframes, uint32_t outframes)
{
struct resampler_thread_data *rstd_l = &rsmpl->rstd_l;
struct resampler_thread_data *rstd_r = &rsmpl->rstd_r;
size_t frames_generated = 0;
/* No need to upsample anything, just copy the buffers.
* Note: This is here for debugging mostly */
if (rsmpl->audio_upsampler_bypass) {
memcpy(out_l, in_l, inframes * sizeof(float));
memcpy(out_r, in_r, inframes * sizeof(float));
frames_generated = inframes;
return frames_generated;
}
#ifdef JMPXRDS_MT
pthread_mutex_lock(&rstd_l->proc_mutex);
rstd_l->inframes = inframes;
rstd_l->in = in_l;
rstd_l->out = out_l;
rstd_l->outframes = outframes;
pthread_mutex_unlock(&rstd_l->proc_mutex);
rstd_r->inframes = inframes;
rstd_r->in = in_r;
rstd_r->out = out_r;
rstd_r->outframes = outframes;
/* Signal the left channel thread to start
* processing this chunk */
pthread_mutex_lock(&rstd_l->proc_mutex);
pthread_cond_signal(&rstd_l->proc_trigger);
pthread_mutex_unlock(&rstd_l->proc_mutex);
/* Process right channel on current thread */
resampler_thread_run(rstd_r);
/* Wait for the left channel thread to finish */
while(pthread_cond_wait(&rstd_l->done_trigger, &rstd_l->done_mutex) != 0);
#else
rstd_l->inframes = inframes;
rstd_l->in = in_l;
rstd_l->out = out_l;
rstd_l->outframes = outframes;
resampler_thread_run(rstd_l);
rstd_r->inframes = inframes;
rstd_r->in = in_r;
rstd_r->out = out_r;
rstd_r->outframes = outframes;
resampler_thread_run(rstd_r);
#endif
if(rstd_l->result || rstd_r->result) {
utils_err("[RESAMPLER] Audio upsampling failed on this period: %i (L), %i (R)\n",
rstd_l->result, rstd_r->result);
return -1;
}
if (rstd_l->frames_generated == 0)
utils_wrn("[RESAMPLER] Audio upsampler didn't generate any frames\n");
return rstd_l->frames_generated;
}
/* Upsample RDS waveform to the main oscilator's sampling rate */
int
resampler_upsample_rds(const struct resampler_data *rsmpl, const float *in, float *out,
uint32_t inframes, uint32_t outframes)
{
soxr_error_t error;
size_t frames_used = 0;
size_t frames_generated = 0;
error = soxr_process(rsmpl->rds_upsampler, in, inframes, &frames_used,
out, outframes, &frames_generated);
if (error) {
utils_err("[RESAMPLER] RDS upsampling failed on this period: %i\n",
error);
return -1;
}
if (frames_generated == 0)
utils_wrn("[RESAMPLER] RDS upsampler didn't generate any frames\n");
return frames_generated;
}
/* Downsample MPX signal to JACK's sample rate */
int
resampler_downsample_mpx(const struct resampler_data *rsmpl, const float *in, float *out,
uint32_t inframes, uint32_t outframes)
{
soxr_error_t error;
size_t frames_used = 0;
size_t frames_generated = 0;
/* No need to upsample anything, just copy the buffers.
* Note: This is here for debugging mostly */
if (rsmpl->mpx_downsampler_bypass) {
memcpy(out, in, inframes * sizeof(float));
frames_generated = inframes;
return frames_generated;
} else {
error = soxr_process(rsmpl->mpx_downsampler, in, inframes,
&frames_used, out, outframes,
&frames_generated);
}
if (error) {
utils_err("[RESAMPLER] MPX downsampling failed on this period: %i\n",
error);
return -1;
}
if (frames_generated == 0)
utils_wrn("[RESAMPLER] MPX downsampler didn't generate any frames\n");
return frames_generated;
}
/****************\
* INIT / DESTROY *
\****************/
int
resampler_init(struct resampler_data *rsmpl, uint32_t jack_samplerate,
jack_client_t *fmmod_client, uint32_t osc_samplerate,
uint32_t rds_samplerate, uint32_t output_samplerate)
{
soxr_error_t error;
soxr_io_spec_t io_spec;
soxr_runtime_spec_t runtime_spec;
soxr_quality_spec_t q_spec;
int ret = 0;
if (rsmpl == NULL)
return -1;
memset(rsmpl, 0, sizeof(struct resampler_data));
/* So that RDS encoder can calculate its buffer lengths */
rsmpl->osc_samplerate = osc_samplerate;
rsmpl->fmmod_client = fmmod_client;
/* AUDIO UPSAMPLER */
if (jack_samplerate == osc_samplerate) {
rsmpl->audio_upsampler_bypass = 1;
utils_dbg("[RESAMPLER] Audio upsampler bypass !\n");
goto audio_upsampler_bypass;
}
/* Initialize upsampler's parameters */
io_spec = soxr_io_spec(SOXR_FLOAT32_S, SOXR_FLOAT32_S);
runtime_spec = soxr_runtime_spec(1);
q_spec = soxr_quality_spec(SOXR_QQ, 0);
rsmpl->audio_upsampler_l = soxr_create(jack_samplerate, osc_samplerate, 1,
&error, &io_spec, &q_spec,
&runtime_spec);
if (error) {
utils_err("[RESAMPLER] Audio upsampler (L) init failed with code: %i\n",
error);
ret = -2;
goto cleanup;
}
rsmpl->audio_upsampler_r = soxr_create(jack_samplerate, osc_samplerate, 1,
&error, &io_spec, &q_spec,
&runtime_spec);
if (error) {
utils_err("[RESAMPLER] Audio upsampler (R) init failed with code: %i\n",
error);
ret = -3;
goto cleanup;
}
rsmpl->active = 1;
ret = resampler_init_upsampler_threads(rsmpl);
if (ret < 0) {
utils_err("[RESAMPLER] Audio upsampler threads init failed\n");
rsmpl->active = 0;
ret = -4;
goto cleanup;
}
audio_upsampler_bypass:
/* RDS UPSAMPLER */
/* Initialize upsampler's parameters */
io_spec = soxr_io_spec(SOXR_FLOAT32_I, SOXR_FLOAT32_I);
runtime_spec = soxr_runtime_spec(1);
q_spec = soxr_quality_spec(SOXR_QQ, 0);
rsmpl->rds_upsampler = soxr_create(rds_samplerate, osc_samplerate, 1,
&error, &io_spec, &q_spec,
&runtime_spec);
if (error) {
utils_err("[RESAMPLER] RDS upsampler init failed with code: %i\n",
error);
ret = -5;
goto cleanup;
}
/* DOWNSAMPLER */
if (osc_samplerate == output_samplerate) {
rsmpl->mpx_downsampler_bypass = 1;
goto cleanup;
}
/* Initialize downsampler's parameters */
io_spec = soxr_io_spec(SOXR_FLOAT32_I, SOXR_FLOAT32_I);
runtime_spec = soxr_runtime_spec(1);
q_spec = soxr_quality_spec(SOXR_HQ, 0);
q_spec.passband_end = ((double)60000 / (double)output_samplerate) * 2.0L;
q_spec.stopband_begin = ((double)62500 / (double)output_samplerate) * 2.0L;
rsmpl->mpx_downsampler = soxr_create(osc_samplerate, output_samplerate,
1, &error, &io_spec, &q_spec,
&runtime_spec);
if (error) {
utils_err("[RESAMPLER] MPX downsampler (L) init failed with code: %i\n",
error);
ret = -6;
goto cleanup;
}
cleanup:
if (ret < 0) {
utils_err("[RESAMPLER] Init failed with code: %i\n", ret);
resampler_destroy(rsmpl);
} else
utils_dbg("[RESAMPLER] Init complete\n");
return ret;
}
void
resampler_destroy(struct resampler_data *rsmpl)
{
rsmpl->active = 0;
/* SoXr checks if they are NULL or not */
soxr_delete(rsmpl->audio_upsampler_l);
soxr_delete(rsmpl->audio_upsampler_r);
soxr_delete(rsmpl->rds_upsampler);
soxr_delete(rsmpl->mpx_downsampler);
utils_dbg("[RESAMPLER] Destroyed\n");
}