1/***
2  This file is part of PulseAudio.
3
4  PulseAudio is free software; you can redistribute it and/or modify
5  it under the terms of the GNU Lesser General Public License as published
6  by the Free Software Foundation; either version 2.1 of the License,
7  or (at your option) any later version.
8
9  PulseAudio is distributed in the hope that it will be useful, but
10  WITHOUT ANY WARRANTY; without even the implied warranty of
11  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  General Public License for more details.
13
14  You should have received a copy of the GNU Lesser General Public License
15  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
16***/
17
18#ifdef HAVE_CONFIG_H
19#include <config.h>
20#endif
21
22#include <stdio.h>
23#include <getopt.h>
24#include <locale.h>
25
26#include <pulse/pulseaudio.h>
27
28#include <pulse/rtclock.h>
29#include <pulse/sample.h>
30#include <pulse/volume.h>
31
32#include <pulsecore/i18n.h>
33#include <pulsecore/log.h>
34#include <pulsecore/resampler.h>
35#include <pulsecore/macro.h>
36#include <pulsecore/endianmacros.h>
37#include <pulsecore/memblock.h>
38#include <pulsecore/sample-util.h>
39#include <pulsecore/core-util.h>
40
41static void dump_block(const char *label, const pa_sample_spec *ss, const pa_memchunk *chunk) {
42    void *d;
43    unsigned i;
44
45    if (getenv("MAKE_CHECK"))
46        return;
47    printf("%s:  \t", label);
48
49    d = pa_memblock_acquire(chunk->memblock);
50
51    switch (ss->format) {
52
53        case PA_SAMPLE_U8:
54        case PA_SAMPLE_ULAW:
55        case PA_SAMPLE_ALAW: {
56            uint8_t *u = d;
57
58            for (i = 0; i < chunk->length / pa_frame_size(ss); i++)
59                printf("      0x%02x ", *(u++));
60
61            break;
62        }
63
64        case PA_SAMPLE_S16NE:
65        case PA_SAMPLE_S16RE: {
66            uint16_t *u = d;
67
68            for (i = 0; i < chunk->length / pa_frame_size(ss); i++)
69                printf("    0x%04x ", *(u++));
70
71            break;
72        }
73
74        case PA_SAMPLE_S32NE:
75        case PA_SAMPLE_S32RE: {
76            uint32_t *u = d;
77
78            for (i = 0; i < chunk->length / pa_frame_size(ss); i++)
79                printf("0x%08x ", *(u++));
80
81            break;
82        }
83
84        case PA_SAMPLE_S24_32NE:
85        case PA_SAMPLE_S24_32RE: {
86            uint32_t *u = d;
87
88            for (i = 0; i < chunk->length / pa_frame_size(ss); i++)
89                printf("0x%08x ", *(u++));
90
91            break;
92        }
93
94        case PA_SAMPLE_FLOAT32NE:
95        case PA_SAMPLE_FLOAT32RE: {
96            float *u = d;
97
98            for (i = 0; i < chunk->length / pa_frame_size(ss); i++) {
99                printf("%4.3g ", ss->format == PA_SAMPLE_FLOAT32NE ? *u : PA_READ_FLOAT32RE(u));
100                u++;
101            }
102
103            break;
104        }
105
106        case PA_SAMPLE_S24LE:
107        case PA_SAMPLE_S24BE: {
108            uint8_t *u = d;
109
110            for (i = 0; i < chunk->length / pa_frame_size(ss); i++) {
111                printf("  0x%06x ", PA_READ24NE(u));
112                u += pa_frame_size(ss);
113            }
114
115            break;
116        }
117
118        default:
119            pa_assert_not_reached();
120    }
121
122    printf("\n");
123
124    pa_memblock_release(chunk->memblock);
125}
126
127static pa_memblock* generate_block(pa_mempool *pool, const pa_sample_spec *ss) {
128    pa_memblock *r;
129    void *d;
130    unsigned i;
131
132    pa_assert_se(r = pa_memblock_new(pool, pa_frame_size(ss) * 10));
133    d = pa_memblock_acquire(r);
134
135    switch (ss->format) {
136
137        case PA_SAMPLE_U8:
138        case PA_SAMPLE_ULAW:
139        case PA_SAMPLE_ALAW: {
140            uint8_t *u = d;
141
142            u[0] = 0x00;
143            u[1] = 0xFF;
144            u[2] = 0x7F;
145            u[3] = 0x80;
146            u[4] = 0x9f;
147            u[5] = 0x3f;
148            u[6] = 0x1;
149            u[7] = 0xF0;
150            u[8] = 0x20;
151            u[9] = 0x21;
152            break;
153        }
154
155        case PA_SAMPLE_S16NE:
156        case PA_SAMPLE_S16RE: {
157            uint16_t *u = d;
158
159            u[0] = 0x0000;
160            u[1] = 0xFFFF;
161            u[2] = 0x7FFF;
162            u[3] = 0x8000;
163            u[4] = 0x9fff;
164            u[5] = 0x3fff;
165            u[6] = 0x1;
166            u[7] = 0xF000;
167            u[8] = 0x20;
168            u[9] = 0x21;
169            break;
170        }
171
172        case PA_SAMPLE_S32NE:
173        case PA_SAMPLE_S32RE: {
174            uint32_t *u = d;
175
176            u[0] = 0x00000001;
177            u[1] = 0xFFFF0002;
178            u[2] = 0x7FFF0003;
179            u[3] = 0x80000004;
180            u[4] = 0x9fff0005;
181            u[5] = 0x3fff0006;
182            u[6] =    0x10007;
183            u[7] = 0xF0000008;
184            u[8] =   0x200009;
185            u[9] =   0x21000A;
186            break;
187        }
188
189        case PA_SAMPLE_S24_32NE:
190        case PA_SAMPLE_S24_32RE: {
191            uint32_t *u = d;
192
193            u[0] = 0x000001;
194            u[1] = 0xFF0002;
195            u[2] = 0x7F0003;
196            u[3] = 0x800004;
197            u[4] = 0x9f0005;
198            u[5] = 0x3f0006;
199            u[6] =    0x107;
200            u[7] = 0xF00008;
201            u[8] =   0x2009;
202            u[9] =   0x210A;
203            break;
204        }
205
206        case PA_SAMPLE_FLOAT32NE:
207        case PA_SAMPLE_FLOAT32RE: {
208            float *u = d;
209
210            u[0] = 0.0f;
211            u[1] = -1.0f;
212            u[2] = 1.0f;
213            u[3] = 4711.0f;
214            u[4] = 0.222f;
215            u[5] = 0.33f;
216            u[6] = -.3f;
217            u[7] = 99.0f;
218            u[8] = -0.555f;
219            u[9] = -.123f;
220
221            if (ss->format == PA_SAMPLE_FLOAT32RE)
222                for (i = 0; i < 10; i++)
223                    PA_WRITE_FLOAT32RE(&u[i], u[i]);
224
225            break;
226        }
227
228        case PA_SAMPLE_S24NE:
229        case PA_SAMPLE_S24RE: {
230            uint8_t *u = d;
231
232            PA_WRITE24NE(u,    0x000001);
233            PA_WRITE24NE(u+3,  0xFF0002);
234            PA_WRITE24NE(u+6,  0x7F0003);
235            PA_WRITE24NE(u+9,  0x800004);
236            PA_WRITE24NE(u+12, 0x9f0005);
237            PA_WRITE24NE(u+15, 0x3f0006);
238            PA_WRITE24NE(u+18,    0x107);
239            PA_WRITE24NE(u+21, 0xF00008);
240            PA_WRITE24NE(u+24,   0x2009);
241            PA_WRITE24NE(u+27,   0x210A);
242            break;
243        }
244
245        default:
246            pa_assert_not_reached();
247    }
248
249    pa_memblock_release(r);
250
251    return r;
252}
253
254static void help(const char *argv0) {
255    printf("%s [options]\n\n"
256           "-h, --help                            Show this help\n"
257           "-v, --verbose                         Print debug messages\n"
258           "      --from-rate=SAMPLERATE          From sample rate in Hz (defaults to 44100)\n"
259           "      --from-format=SAMPLEFORMAT      From sample type (defaults to s16le)\n"
260           "      --from-channels=CHANNELS        From number of channels (defaults to 1)\n"
261           "      --to-rate=SAMPLERATE            To sample rate in Hz (defaults to 44100)\n"
262           "      --to-format=SAMPLEFORMAT        To sample type (defaults to s16le)\n"
263           "      --to-channels=CHANNELS          To number of channels (defaults to 1)\n"
264           "      --resample-method=METHOD        Resample method (defaults to auto)\n"
265           "      --seconds=SECONDS               From stream duration (defaults to 60)\n"
266           "\n"
267           "If the formats are not specified, the test performs all formats combinations,\n"
268           "back and forth.\n"
269           "\n"
270           "Sample type must be one of s16le, s16be, u8, float32le, float32be, ulaw, alaw,\n"
271           "s24le, s24be, s24-32le, s24-32be, s32le, s32be (defaults to s16ne)\n"
272           "\n"
273           "See --dump-resample-methods for possible values of resample methods.\n",
274           argv0);
275}
276
277enum {
278    ARG_VERSION = 256,
279    ARG_FROM_SAMPLERATE,
280    ARG_FROM_SAMPLEFORMAT,
281    ARG_FROM_CHANNELS,
282    ARG_TO_SAMPLERATE,
283    ARG_TO_SAMPLEFORMAT,
284    ARG_TO_CHANNELS,
285    ARG_SECONDS,
286    ARG_RESAMPLE_METHOD,
287    ARG_DUMP_RESAMPLE_METHODS
288};
289
290static void dump_resample_methods(void) {
291    int i;
292
293    for (i = 0; i < PA_RESAMPLER_MAX; i++)
294        if (pa_resample_method_supported(i))
295            printf("%s\n", pa_resample_method_to_string(i));
296
297}
298
299int main(int argc, char *argv[]) {
300    pa_mempool *pool = NULL;
301    pa_sample_spec a, b;
302    int ret = 1, c;
303    bool all_formats = true;
304    pa_resample_method_t method;
305    int seconds;
306    unsigned crossover_freq = 120;
307
308    static const struct option long_options[] = {
309        {"help",                  0, NULL, 'h'},
310        {"verbose",               0, NULL, 'v'},
311        {"version",               0, NULL, ARG_VERSION},
312        {"from-rate",             1, NULL, ARG_FROM_SAMPLERATE},
313        {"from-format",           1, NULL, ARG_FROM_SAMPLEFORMAT},
314        {"from-channels",         1, NULL, ARG_FROM_CHANNELS},
315        {"to-rate",               1, NULL, ARG_TO_SAMPLERATE},
316        {"to-format",             1, NULL, ARG_TO_SAMPLEFORMAT},
317        {"to-channels",           1, NULL, ARG_TO_CHANNELS},
318        {"seconds",               1, NULL, ARG_SECONDS},
319        {"resample-method",       1, NULL, ARG_RESAMPLE_METHOD},
320        {"dump-resample-methods", 0, NULL, ARG_DUMP_RESAMPLE_METHODS},
321        {NULL,                    0, NULL, 0}
322    };
323
324    setlocale(LC_ALL, "");
325#ifdef ENABLE_NLS
326    bindtextdomain(GETTEXT_PACKAGE, PULSE_LOCALEDIR);
327#endif
328
329    pa_log_set_level(PA_LOG_WARN);
330    if (!getenv("MAKE_CHECK"))
331        pa_log_set_level(PA_LOG_INFO);
332
333    a.channels = b.channels = 1;
334    a.rate = b.rate = 44100;
335    a.format = b.format = PA_SAMPLE_S16LE;
336
337    method = PA_RESAMPLER_AUTO;
338    seconds = 60;
339
340    while ((c = getopt_long(argc, argv, "hv", long_options, NULL)) != -1) {
341
342        switch (c) {
343            case 'h' :
344                help(argv[0]);
345                ret = 0;
346                goto quit;
347
348            case 'v':
349                pa_log_set_level(PA_LOG_DEBUG);
350                break;
351
352            case ARG_VERSION:
353                printf("%s %s\n", argv[0], PACKAGE_VERSION);
354                ret = 0;
355                goto quit;
356
357            case ARG_DUMP_RESAMPLE_METHODS:
358                dump_resample_methods();
359                ret = 0;
360                goto quit;
361
362            case ARG_FROM_CHANNELS:
363                a.channels = (uint8_t) atoi(optarg);
364                break;
365
366            case ARG_FROM_SAMPLEFORMAT:
367                a.format = pa_parse_sample_format(optarg);
368                all_formats = false;
369                break;
370
371            case ARG_FROM_SAMPLERATE:
372                a.rate = (uint32_t) atoi(optarg);
373                break;
374
375            case ARG_TO_CHANNELS:
376                b.channels = (uint8_t) atoi(optarg);
377                break;
378
379            case ARG_TO_SAMPLEFORMAT:
380                b.format = pa_parse_sample_format(optarg);
381                all_formats = false;
382                break;
383
384            case ARG_TO_SAMPLERATE:
385                b.rate = (uint32_t) atoi(optarg);
386                break;
387
388            case ARG_SECONDS:
389                seconds = atoi(optarg);
390                break;
391
392            case ARG_RESAMPLE_METHOD:
393                if (*optarg == '\0' || pa_streq(optarg, "help")) {
394                    dump_resample_methods();
395                    ret = 0;
396                    goto quit;
397                }
398                method = pa_parse_resample_method(optarg);
399                break;
400
401            default:
402                goto quit;
403        }
404    }
405
406    ret = 0;
407    pa_assert_se(pool = pa_mempool_new(PA_MEM_TYPE_PRIVATE, 0, true));
408
409    if (!all_formats) {
410
411        pa_resampler *resampler;
412        pa_memchunk i, j;
413        pa_usec_t ts;
414
415        pa_log_debug("Compilation CFLAGS: %s", PA_CFLAGS);
416        pa_log_debug("=== %d seconds: %d Hz %d ch (%s) -> %d Hz %d ch (%s)", seconds,
417                   a.rate, a.channels, pa_sample_format_to_string(a.format),
418                   b.rate, b.channels, pa_sample_format_to_string(b.format));
419
420        ts = pa_rtclock_now();
421        pa_assert_se(resampler = pa_resampler_new(pool, &a, NULL, &b, NULL, crossover_freq, method, 0));
422        pa_log_info("init: %llu", (long long unsigned)(pa_rtclock_now() - ts));
423
424        i.memblock = pa_memblock_new(pool, pa_usec_to_bytes(1*PA_USEC_PER_SEC, &a));
425
426        ts = pa_rtclock_now();
427        i.length = pa_memblock_get_length(i.memblock);
428        i.index = 0;
429        while (seconds--) {
430            pa_resampler_run(resampler, &i, &j);
431            if (j.memblock)
432                pa_memblock_unref(j.memblock);
433        }
434        pa_log_info("resampling: %llu", (long long unsigned)(pa_rtclock_now() - ts));
435        pa_memblock_unref(i.memblock);
436
437        pa_resampler_free(resampler);
438
439        goto quit;
440    }
441
442    for (a.format = 0; a.format < PA_SAMPLE_MAX; a.format ++) {
443        for (b.format = 0; b.format < PA_SAMPLE_MAX; b.format ++) {
444            pa_resampler *forth, *back;
445            pa_memchunk i, j, k;
446
447            pa_log_debug("=== %s -> %s -> %s -> /2",
448                       pa_sample_format_to_string(a.format),
449                       pa_sample_format_to_string(b.format),
450                       pa_sample_format_to_string(a.format));
451
452            pa_assert_se(forth = pa_resampler_new(pool, &a, NULL, &b, NULL, crossover_freq, method, 0));
453            pa_assert_se(back = pa_resampler_new(pool, &b, NULL, &a, NULL, crossover_freq, method, 0));
454
455            i.memblock = generate_block(pool, &a);
456            i.length = pa_memblock_get_length(i.memblock);
457            i.index = 0;
458            pa_resampler_run(forth, &i, &j);
459            pa_resampler_run(back, &j, &k);
460
461            dump_block("before", &a, &i);
462            dump_block("after", &b, &j);
463            dump_block("reverse", &a, &k);
464
465            pa_memblock_unref(i.memblock);
466            pa_memblock_unref(j.memblock);
467            pa_memblock_unref(k.memblock);
468
469            pa_resampler_free(forth);
470            pa_resampler_free(back);
471        }
472    }
473
474 quit:
475    if (pool)
476        pa_mempool_unref(pool);
477
478    return ret;
479}
480