1/***
2  This file is part of PulseAudio.
3
4  Copyright 2004-2006 Lennart Poettering
5  Copyright 2009 Wim Taymans <wim.taymans@collabora.co.uk.com>
6
7  PulseAudio is free software; you can redistribute it and/or modify
8  it under the terms of the GNU Lesser General Public License as published
9  by the Free Software Foundation; either version 2.1 of the License,
10  or (at your option) any later version.
11
12  PulseAudio is distributed in the hope that it will be useful, but
13  WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  General Public License for more details.
16
17  You should have received a copy of the GNU Lesser General Public License
18  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
19***/
20
21#ifdef HAVE_CONFIG_H
22#include <config.h>
23#endif
24
25#include <string.h>
26
27#include <pulse/xmalloc.h>
28#include <pulse/sample.h>
29#include <pulse/volume.h>
30#include <pulsecore/log.h>
31#include <pulsecore/macro.h>
32
33#include "cpu.h"
34#include "remap.h"
35
36static void remap_mono_to_stereo_s16ne_c(pa_remap_t *m, int16_t *dst, const int16_t *src, unsigned n) {
37    unsigned i;
38
39    for (i = n >> 2; i; i--) {
40        dst[0] = dst[1] = src[0];
41        dst[2] = dst[3] = src[1];
42        dst[4] = dst[5] = src[2];
43        dst[6] = dst[7] = src[3];
44        src += 4;
45        dst += 8;
46    }
47    for (i = n & 3; i; i--) {
48        dst[0] = dst[1] = src[0];
49        src++;
50        dst += 2;
51    }
52}
53
54static void remap_mono_to_stereo_s32ne_c(pa_remap_t *m, int32_t *dst, const int32_t *src, unsigned n) {
55    unsigned i;
56
57    for (i = n >> 2; i; i--) {
58        dst[0] = dst[1] = src[0];
59        dst[2] = dst[3] = src[1];
60        dst[4] = dst[5] = src[2];
61        dst[6] = dst[7] = src[3];
62        src += 4;
63        dst += 8;
64    }
65    for (i = n & 3; i; i--) {
66        dst[0] = dst[1] = src[0];
67        src++;
68        dst += 2;
69    }
70}
71
72static void remap_mono_to_stereo_float32ne_c(pa_remap_t *m, float *dst, const float *src, unsigned n) {
73    unsigned i;
74
75    for (i = n >> 2; i; i--) {
76        dst[0] = dst[1] = src[0];
77        dst[2] = dst[3] = src[1];
78        dst[4] = dst[5] = src[2];
79        dst[6] = dst[7] = src[3];
80        src += 4;
81        dst += 8;
82    }
83    for (i = n & 3; i; i--) {
84        dst[0] = dst[1] = src[0];
85        src++;
86        dst += 2;
87    }
88}
89
90static void remap_stereo_to_mono_s16ne_c(pa_remap_t *m, int16_t *dst, const int16_t *src, unsigned n) {
91    unsigned i;
92
93    for (i = n >> 2; i > 0; i--) {
94        dst[0] = (src[0] + src[1])/2;
95        dst[1] = (src[2] + src[3])/2;
96        dst[2] = (src[4] + src[5])/2;
97        dst[3] = (src[6] + src[7])/2;
98        src += 8;
99        dst += 4;
100    }
101    for (i = n & 3; i; i--) {
102        dst[0] = (src[0] + src[1])/2;
103        src += 2;
104        dst += 1;
105    }
106}
107
108static void remap_stereo_to_mono_s32ne_c(pa_remap_t *m, int32_t *dst, const int32_t *src, unsigned n) {
109    unsigned i;
110
111    for (i = n >> 2; i > 0; i--) {
112        /* Avoid overflow by performing division first. We accept a
113         * difference of +/- 1 to the ideal result. */
114        dst[0] = (src[0]/2 + src[1]/2);
115        dst[1] = (src[2]/2 + src[3]/2);
116        dst[2] = (src[4]/2 + src[5]/2);
117        dst[3] = (src[6]/2 + src[7]/2);
118        src += 8;
119        dst += 4;
120    }
121    for (i = n & 3; i; i--) {
122        /* Avoid overflow by performing division first. We accept a
123         * difference of +/- 1 to the ideal result. */
124        dst[0] = (src[0]/2 + src[1]/2);
125        src += 2;
126        dst += 1;
127    }
128}
129
130static void remap_stereo_to_mono_float32ne_c(pa_remap_t *m, float *dst, const float *src, unsigned n) {
131    unsigned i;
132
133    for (i = n >> 2; i > 0; i--) {
134        dst[0] = (src[0] + src[1])*0.5f;
135        dst[1] = (src[2] + src[3])*0.5f;
136        dst[2] = (src[4] + src[5])*0.5f;
137        dst[3] = (src[6] + src[7])*0.5f;
138        src += 8;
139        dst += 4;
140    }
141    for (i = n & 3; i; i--) {
142        dst[0] = (src[0] + src[1])*0.5f;
143        src += 2;
144        dst += 1;
145    }
146}
147
148static void remap_mono_to_ch4_s16ne_c(pa_remap_t *m, int16_t *dst, const int16_t *src, unsigned n) {
149    unsigned i;
150
151    for (i = n >> 2; i; i--) {
152        dst[0] = dst[1] = dst[2] = dst[3] = src[0];
153        dst[4] = dst[5] = dst[6] = dst[7] = src[1];
154        dst[8] = dst[9] = dst[10] = dst[11] = src[2];
155        dst[12] = dst[13] = dst[14] = dst[15] = src[3];
156        src += 4;
157        dst += 16;
158    }
159    for (i = n & 3; i; i--) {
160        dst[0] = dst[1] = dst[2] = dst[3] = src[0];
161        src++;
162        dst += 4;
163    }
164}
165
166static void remap_mono_to_ch4_s32ne_c(pa_remap_t *m, int32_t *dst, const int32_t *src, unsigned n) {
167    unsigned i;
168
169    for (i = n >> 2; i; i--) {
170        dst[0] = dst[1] = dst[2] = dst[3] = src[0];
171        dst[4] = dst[5] = dst[6] = dst[7] = src[1];
172        dst[8] = dst[9] = dst[10] = dst[11] = src[2];
173        dst[12] = dst[13] = dst[14] = dst[15] = src[3];
174        src += 4;
175        dst += 16;
176    }
177    for (i = n & 3; i; i--) {
178        dst[0] = dst[1] = dst[2] = dst[3] = src[0];
179        src++;
180        dst += 4;
181    }
182}
183
184static void remap_mono_to_ch4_float32ne_c(pa_remap_t *m, float *dst, const float *src, unsigned n) {
185    unsigned i;
186
187    for (i = n >> 2; i; i--) {
188        dst[0] = dst[1] = dst[2] = dst[3] = src[0];
189        dst[4] = dst[5] = dst[6] = dst[7] = src[1];
190        dst[8] = dst[9] = dst[10] = dst[11] = src[2];
191        dst[12] = dst[13] = dst[14] = dst[15] = src[3];
192        src += 4;
193        dst += 16;
194    }
195    for (i = n & 3; i; i--) {
196        dst[0] = dst[1] = dst[2] = dst[3] = src[0];
197        src++;
198        dst += 4;
199    }
200}
201
202static void remap_ch4_to_mono_s16ne_c(pa_remap_t *m, int16_t *dst, const int16_t *src, unsigned n) {
203    unsigned i;
204
205    for (i = n >> 2; i > 0; i--) {
206        dst[0] = (src[0] + src[1] + src[2] + src[3])/4;
207        dst[1] = (src[4] + src[5] + src[6] + src[7])/4;
208        dst[2] = (src[8] + src[9] + src[10] + src[11])/4;
209        dst[3] = (src[12] + src[13] + src[14] + src[15])/4;
210        src += 16;
211        dst += 4;
212    }
213    for (i = n & 3; i; i--) {
214        dst[0] = (src[0] + src[1] + src[2] + src[3])/4;
215        src += 4;
216        dst += 1;
217    }
218}
219
220static void remap_ch4_to_mono_s32ne_c(pa_remap_t *m, int32_t *dst, const int32_t *src, unsigned n) {
221    unsigned i;
222
223    for (i = n >> 2; i > 0; i--) {
224        /* Avoid overflow by performing division first. We accept a
225         * difference of +/- 3 to the ideal result. */
226        dst[0] = (src[0]/4 + src[1]/4 + src[2]/4 + src[3]/4);
227        dst[1] = (src[4]/4 + src[5]/4 + src[6]/4 + src[7]/4);
228        dst[2] = (src[8]/4 + src[9]/4 + src[10]/4 + src[11]/4);
229        dst[3] = (src[12]/4 + src[13]/4 + src[14]/4 + src[15]/4);
230        src += 16;
231        dst += 4;
232    }
233    for (i = n & 3; i; i--) {
234        /* Avoid overflow by performing division first. We accept a
235         * difference of +/- 3 to the ideal result. */
236        dst[0] = (src[0]/4 + src[1]/4 + src[2]/4 + src[3]/4);
237        src += 4;
238        dst += 1;
239    }
240}
241
242static void remap_ch4_to_mono_float32ne_c(pa_remap_t *m, float *dst, const float *src, unsigned n) {
243    unsigned i;
244
245    for (i = n >> 2; i > 0; i--) {
246        dst[0] = (src[0] + src[1] + src[2] + src[3])*0.25f;
247        dst[1] = (src[4] + src[5] + src[6] + src[7])*0.25f;
248        dst[2] = (src[8] + src[9] + src[10] + src[11])*0.25f;
249        dst[3] = (src[12] + src[13] + src[14] + src[15])*0.25f;
250        src += 16;
251        dst += 4;
252    }
253    for (i = n & 3; i; i--) {
254        dst[0] = (src[0] + src[1] + src[2] + src[3])*0.25f;
255        src += 4;
256        dst += 1;
257    }
258}
259
260static void remap_channels_matrix_s16ne_c(pa_remap_t *m, int16_t *dst, const int16_t *src, unsigned n) {
261
262    unsigned oc, ic, i;
263    unsigned n_ic, n_oc;
264
265    n_ic = m->i_ss.channels;
266    n_oc = m->o_ss.channels;
267
268    memset(dst, 0, n * sizeof(int16_t) * n_oc);
269
270    for (oc = 0; oc < n_oc; oc++) {
271
272        for (ic = 0; ic < n_ic; ic++) {
273            int16_t *d = dst + oc;
274            const int16_t *s = src + ic;
275            int32_t vol = m->map_table_i[oc][ic];
276
277            if (vol <= 0)
278                continue;
279
280            if (vol >= 0x10000) {
281                for (i = n; i > 0; i--, s += n_ic, d += n_oc)
282                    *d += *s;
283            } else {
284                for (i = n; i > 0; i--, s += n_ic, d += n_oc)
285                    *d += (int16_t) (((int32_t)*s * vol) >> 16);
286            }
287        }
288    }
289}
290
291static void remap_channels_matrix_s32ne_c(pa_remap_t *m, int32_t *dst, const int32_t *src, unsigned n) {
292    unsigned oc, ic, i;
293    unsigned n_ic, n_oc;
294
295    n_ic = m->i_ss.channels;
296    n_oc = m->o_ss.channels;
297
298    memset(dst, 0, n * sizeof(int32_t) * n_oc);
299
300    for (oc = 0; oc < n_oc; oc++) {
301
302        for (ic = 0; ic < n_ic; ic++) {
303            int32_t *d = dst + oc;
304            const int32_t *s = src + ic;
305            int32_t vol = m->map_table_i[oc][ic];
306
307            if (vol <= 0)
308                continue;
309
310            if (vol >= 0x10000) {
311                for (i = n; i > 0; i--, s += n_ic, d += n_oc)
312                    *d += *s;
313            } else {
314                for (i = n; i > 0; i--, s += n_ic, d += n_oc)
315                    *d += (int32_t) (((int64_t)*s * vol) >> 16);
316            }
317        }
318    }
319}
320
321static void remap_channels_matrix_float32ne_c(pa_remap_t *m, float *dst, const float *src, unsigned n) {
322    unsigned oc, ic, i;
323    unsigned n_ic, n_oc;
324
325    n_ic = m->i_ss.channels;
326    n_oc = m->o_ss.channels;
327
328    memset(dst, 0, n * sizeof(float) * n_oc);
329
330    for (oc = 0; oc < n_oc; oc++) {
331
332        for (ic = 0; ic < n_ic; ic++) {
333            float *d = dst + oc;
334            const float *s = src + ic;
335            float vol = m->map_table_f[oc][ic];
336
337            if (vol <= 0.0f)
338                continue;
339
340            if (vol >= 1.0f) {
341                for (i = n; i > 0; i--, s += n_ic, d += n_oc)
342                    *d += *s;
343            } else {
344                for (i = n; i > 0; i--, s += n_ic, d += n_oc)
345                    *d += *s * vol;
346            }
347        }
348    }
349}
350
351/* Produce an array containing input channel indices to map to output channels.
352 * If the output channel is empty, the array element is -1. */
353bool pa_setup_remap_arrange(const pa_remap_t *m, int8_t arrange[PA_CHANNELS_MAX]) {
354    unsigned ic, oc;
355    unsigned n_ic, n_oc;
356    unsigned count_output = 0;
357
358    pa_assert(m);
359
360    n_ic = m->i_ss.channels;
361    n_oc = m->o_ss.channels;
362
363    for (oc = 0; oc < n_oc; oc++) {
364        arrange[oc] = -1;
365        for (ic = 0; ic < n_ic; ic++) {
366            int32_t vol = m->map_table_i[oc][ic];
367
368            /* input channel is not used */
369            if (vol == 0)
370                continue;
371
372            /* if mixing this channel, we cannot just rearrange */
373            if (vol != 0x10000 || arrange[oc] >= 0)
374                return false;
375
376            arrange[oc] = ic;
377            count_output++;
378        }
379    }
380
381    return count_output > 0;
382}
383
384static void remap_arrange_mono_s16ne_c(pa_remap_t *m, int16_t *dst, const int16_t *src, unsigned n) {
385    const unsigned n_ic = m->i_ss.channels;
386    const int8_t *arrange = m->state;
387
388    src += arrange[0];
389    for (; n > 0; n--) {
390        *dst++ = *src;
391        src += n_ic;
392    }
393}
394
395static void remap_arrange_stereo_s16ne_c(pa_remap_t *m, int16_t *dst, const int16_t *src, unsigned n) {
396    const unsigned n_ic = m->i_ss.channels;
397    const int8_t *arrange = m->state;
398    const int8_t ic0 = arrange[0], ic1 = arrange[1];
399
400    for (; n > 0; n--) {
401        *dst++ = (ic0 >= 0) ? *(src + ic0) : 0;
402        *dst++ = (ic1 >= 0) ? *(src + ic1) : 0;
403        src += n_ic;
404    }
405}
406
407static void remap_arrange_ch4_s16ne_c(pa_remap_t *m, int16_t *dst, const int16_t *src, unsigned n) {
408    const unsigned n_ic = m->i_ss.channels;
409    const int8_t *arrange = m->state;
410    const int8_t ic0 = arrange[0], ic1 = arrange[1],
411        ic2 = arrange[2], ic3 = arrange[3];
412
413    for (; n > 0; n--) {
414        *dst++ = (ic0 >= 0) ? *(src + ic0) : 0;
415        *dst++ = (ic1 >= 0) ? *(src + ic1) : 0;
416        *dst++ = (ic2 >= 0) ? *(src + ic2) : 0;
417        *dst++ = (ic3 >= 0) ? *(src + ic3) : 0;
418        src += n_ic;
419    }
420}
421
422static void remap_arrange_mono_s32ne_c(pa_remap_t *m, int32_t *dst, const int32_t *src, unsigned n) {
423    const unsigned n_ic = m->i_ss.channels;
424    const int8_t *arrange = m->state;
425
426    src += arrange[0];
427    for (; n > 0; n--) {
428        *dst++ = *src;
429        src += n_ic;
430    }
431}
432
433static void remap_arrange_stereo_s32ne_c(pa_remap_t *m, int32_t *dst, const int32_t *src, unsigned n) {
434    const unsigned n_ic = m->i_ss.channels;
435    const int8_t *arrange = m->state;
436    const int ic0 = arrange[0], ic1 = arrange[1];
437
438    for (; n > 0; n--) {
439        *dst++ = (ic0 >= 0) ? *(src + ic0) : 0;
440        *dst++ = (ic1 >= 0) ? *(src + ic1) : 0;
441        src += n_ic;
442    }
443}
444
445static void remap_arrange_ch4_s32ne_c(pa_remap_t *m, int32_t *dst, const int32_t *src, unsigned n) {
446    const unsigned n_ic = m->i_ss.channels;
447    const int8_t *arrange = m->state;
448    const int ic0 = arrange[0], ic1 = arrange[1],
449        ic2 = arrange[2], ic3 = arrange[3];
450
451    for (; n > 0; n--) {
452        *dst++ = (ic0 >= 0) ? *(src + ic0) : 0;
453        *dst++ = (ic1 >= 0) ? *(src + ic1) : 0;
454        *dst++ = (ic2 >= 0) ? *(src + ic2) : 0;
455        *dst++ = (ic3 >= 0) ? *(src + ic3) : 0;
456        src += n_ic;
457    }
458}
459
460static void remap_arrange_mono_float32ne_c(pa_remap_t *m, float *dst, const float *src, unsigned n) {
461    const unsigned n_ic = m->i_ss.channels;
462    const int8_t *arrange = m->state;
463
464    src += arrange[0];
465    for (; n > 0; n--) {
466        *dst++ = *src;
467        src += n_ic;
468    }
469}
470
471static void remap_arrange_stereo_float32ne_c(pa_remap_t *m, float *dst, const float *src, unsigned n) {
472    const unsigned n_ic = m->i_ss.channels;
473    const int8_t *arrange = m->state;
474    const int ic0 = arrange[0], ic1 = arrange[1];
475
476    for (; n > 0; n--) {
477        *dst++ = (ic0 >= 0) ? *(src + ic0) : 0.0f;
478        *dst++ = (ic1 >= 0) ? *(src + ic1) : 0.0f;
479        src += n_ic;
480    }
481}
482
483static void remap_arrange_ch4_float32ne_c(pa_remap_t *m, float *dst, const float *src, unsigned n) {
484    const unsigned n_ic = m->i_ss.channels;
485    const int8_t *arrange = m->state;
486    const int ic0 = arrange[0], ic1 = arrange[1],
487        ic2 = arrange[2], ic3 = arrange[3];
488
489    for (; n > 0; n--) {
490        *dst++ = (ic0 >= 0) ? *(src + ic0) : 0.0f;
491        *dst++ = (ic1 >= 0) ? *(src + ic1) : 0.0f;
492        *dst++ = (ic2 >= 0) ? *(src + ic2) : 0.0f;
493        *dst++ = (ic3 >= 0) ? *(src + ic3) : 0.0f;
494        src += n_ic;
495    }
496}
497
498void pa_set_remap_func(pa_remap_t *m, pa_do_remap_func_t func_s16,
499    pa_do_remap_func_t func_s32, pa_do_remap_func_t func_float) {
500
501    pa_assert(m);
502
503    if (m->format == PA_SAMPLE_S16NE)
504        m->do_remap = func_s16;
505    else if (m->format == PA_SAMPLE_S32NE)
506        m->do_remap = func_s32;
507    else if (m->format == PA_SAMPLE_FLOAT32NE)
508        m->do_remap = func_float;
509    else
510        pa_assert_not_reached();
511    pa_assert(m->do_remap);
512}
513
514static bool force_generic_code = false;
515
516/* set the function that will execute the remapping based on the matrices */
517static void init_remap_c(pa_remap_t *m) {
518    unsigned n_oc, n_ic;
519    int8_t arrange[PA_CHANNELS_MAX];
520
521    n_oc = m->o_ss.channels;
522    n_ic = m->i_ss.channels;
523
524    /* find some common channel remappings, fall back to full matrix operation. */
525    if (force_generic_code) {
526        pa_log_info("Forced to use generic matrix remapping");
527        pa_set_remap_func(m, (pa_do_remap_func_t) remap_channels_matrix_s16ne_c,
528            (pa_do_remap_func_t) remap_channels_matrix_s32ne_c,
529            (pa_do_remap_func_t) remap_channels_matrix_float32ne_c);
530        return;
531    }
532
533    if (n_ic == 1 && n_oc == 2 &&
534            m->map_table_i[0][0] == 0x10000 && m->map_table_i[1][0] == 0x10000) {
535
536        pa_log_info("Using mono to stereo remapping");
537        pa_set_remap_func(m, (pa_do_remap_func_t) remap_mono_to_stereo_s16ne_c,
538            (pa_do_remap_func_t) remap_mono_to_stereo_s32ne_c,
539            (pa_do_remap_func_t) remap_mono_to_stereo_float32ne_c);
540    } else if (n_ic == 2 && n_oc == 1 &&
541            m->map_table_i[0][0] == 0x8000 && m->map_table_i[0][1] == 0x8000) {
542
543        pa_log_info("Using stereo to mono remapping");
544        pa_set_remap_func(m, (pa_do_remap_func_t) remap_stereo_to_mono_s16ne_c,
545            (pa_do_remap_func_t) remap_stereo_to_mono_s32ne_c,
546            (pa_do_remap_func_t) remap_stereo_to_mono_float32ne_c);
547    } else if (n_ic == 1 && n_oc == 4 &&
548            m->map_table_i[0][0] == 0x10000 && m->map_table_i[1][0] == 0x10000 &&
549            m->map_table_i[2][0] == 0x10000 && m->map_table_i[3][0] == 0x10000) {
550
551        pa_log_info("Using mono to 4-channel remapping");
552        pa_set_remap_func(m, (pa_do_remap_func_t)remap_mono_to_ch4_s16ne_c,
553            (pa_do_remap_func_t) remap_mono_to_ch4_s32ne_c,
554            (pa_do_remap_func_t) remap_mono_to_ch4_float32ne_c);
555    } else if (n_ic == 4 && n_oc == 1 &&
556            m->map_table_i[0][0] == 0x4000 && m->map_table_i[0][1] == 0x4000 &&
557            m->map_table_i[0][2] == 0x4000 && m->map_table_i[0][3] == 0x4000) {
558
559        pa_log_info("Using 4-channel to mono remapping");
560        pa_set_remap_func(m, (pa_do_remap_func_t) remap_ch4_to_mono_s16ne_c,
561            (pa_do_remap_func_t) remap_ch4_to_mono_s32ne_c,
562            (pa_do_remap_func_t) remap_ch4_to_mono_float32ne_c);
563    } else if (pa_setup_remap_arrange(m, arrange) && n_oc == 1) {
564
565        pa_log_info("Using mono arrange remapping");
566        pa_set_remap_func(m, (pa_do_remap_func_t) remap_arrange_mono_s16ne_c,
567            (pa_do_remap_func_t) remap_arrange_mono_s32ne_c,
568            (pa_do_remap_func_t) remap_arrange_mono_float32ne_c);
569
570        /* setup state */
571        m->state = pa_xnewdup(int8_t, arrange, PA_CHANNELS_MAX);
572    } else if (pa_setup_remap_arrange(m, arrange) && n_oc == 2) {
573
574        pa_log_info("Using stereo arrange remapping");
575        pa_set_remap_func(m, (pa_do_remap_func_t) remap_arrange_stereo_s16ne_c,
576            (pa_do_remap_func_t) remap_arrange_stereo_s32ne_c,
577            (pa_do_remap_func_t) remap_arrange_stereo_float32ne_c);
578
579        /* setup state */
580        m->state = pa_xnewdup(int8_t, arrange, PA_CHANNELS_MAX);
581    } else if (pa_setup_remap_arrange(m, arrange) && n_oc == 4) {
582
583        pa_log_info("Using 4-channel arrange remapping");
584        pa_set_remap_func(m, (pa_do_remap_func_t) remap_arrange_ch4_s16ne_c,
585            (pa_do_remap_func_t) remap_arrange_ch4_s32ne_c,
586            (pa_do_remap_func_t) remap_arrange_ch4_float32ne_c);
587
588        /* setup state */
589        m->state = pa_xnewdup(int8_t, arrange, PA_CHANNELS_MAX);
590    } else {
591
592        pa_log_info("Using generic matrix remapping");
593        pa_set_remap_func(m, (pa_do_remap_func_t) remap_channels_matrix_s16ne_c,
594            (pa_do_remap_func_t) remap_channels_matrix_s32ne_c,
595            (pa_do_remap_func_t) remap_channels_matrix_float32ne_c);
596    }
597}
598
599/* default C implementation */
600static pa_init_remap_func_t init_remap_func = init_remap_c;
601
602void pa_init_remap_func(pa_remap_t *m) {
603    pa_assert(init_remap_func);
604
605    m->do_remap = NULL;
606
607    /* call the installed remap init function */
608    init_remap_func(m);
609
610    if (m->do_remap == NULL) {
611        /* nothing was installed, fallback to C version */
612        init_remap_c(m);
613    }
614}
615
616pa_init_remap_func_t pa_get_init_remap_func(void) {
617    return init_remap_func;
618}
619
620void pa_set_init_remap_func(pa_init_remap_func_t func) {
621    init_remap_func = func;
622}
623
624void pa_remap_func_init(const pa_cpu_info *cpu_info) {
625    force_generic_code = cpu_info->force_generic_code;
626}
627