153a5a1b3Sopenharmony_ci/***
253a5a1b3Sopenharmony_ci  This file is part of PulseAudio.
353a5a1b3Sopenharmony_ci
453a5a1b3Sopenharmony_ci  Copyright 2009 Lennart Poettering
553a5a1b3Sopenharmony_ci
653a5a1b3Sopenharmony_ci  PulseAudio is free software; you can redistribute it and/or modify
753a5a1b3Sopenharmony_ci  it under the terms of the GNU Lesser General Public License as published
853a5a1b3Sopenharmony_ci  by the Free Software Foundation; either version 2.1 of the License,
953a5a1b3Sopenharmony_ci  or (at your option) any later version.
1053a5a1b3Sopenharmony_ci
1153a5a1b3Sopenharmony_ci  PulseAudio is distributed in the hope that it will be useful, but
1253a5a1b3Sopenharmony_ci  WITHOUT ANY WARRANTY; without even the implied warranty of
1353a5a1b3Sopenharmony_ci  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1453a5a1b3Sopenharmony_ci  General Public License for more details.
1553a5a1b3Sopenharmony_ci
1653a5a1b3Sopenharmony_ci  You should have received a copy of the GNU Lesser General Public License
1753a5a1b3Sopenharmony_ci  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
1853a5a1b3Sopenharmony_ci***/
1953a5a1b3Sopenharmony_ci
2053a5a1b3Sopenharmony_ci#ifdef HAVE_CONFIG_H
2153a5a1b3Sopenharmony_ci#include <config.h>
2253a5a1b3Sopenharmony_ci#endif
2353a5a1b3Sopenharmony_ci
2453a5a1b3Sopenharmony_ci/* Shared between pacat/parec/paplay and the server */
2553a5a1b3Sopenharmony_ci
2653a5a1b3Sopenharmony_ci#include <pulse/xmalloc.h>
2753a5a1b3Sopenharmony_ci#include <pulse/utf8.h>
2853a5a1b3Sopenharmony_ci
2953a5a1b3Sopenharmony_ci#include <pulsecore/macro.h>
3053a5a1b3Sopenharmony_ci
3153a5a1b3Sopenharmony_ci#include "sndfile-util.h"
3253a5a1b3Sopenharmony_ci
3353a5a1b3Sopenharmony_ciint pa_sndfile_read_sample_spec(SNDFILE *sf, pa_sample_spec *ss) {
3453a5a1b3Sopenharmony_ci    SF_INFO sfi;
3553a5a1b3Sopenharmony_ci    int sf_errno;
3653a5a1b3Sopenharmony_ci
3753a5a1b3Sopenharmony_ci    pa_assert(sf);
3853a5a1b3Sopenharmony_ci    pa_assert(ss);
3953a5a1b3Sopenharmony_ci
4053a5a1b3Sopenharmony_ci    pa_zero(sfi);
4153a5a1b3Sopenharmony_ci    if ((sf_errno = sf_command(sf, SFC_GET_CURRENT_SF_INFO, &sfi, sizeof(sfi)))) {
4253a5a1b3Sopenharmony_ci        pa_log_error("sndfile: %s", sf_error_number(sf_errno));
4353a5a1b3Sopenharmony_ci        return -1;
4453a5a1b3Sopenharmony_ci    }
4553a5a1b3Sopenharmony_ci
4653a5a1b3Sopenharmony_ci    switch (sfi.format & SF_FORMAT_SUBMASK) {
4753a5a1b3Sopenharmony_ci
4853a5a1b3Sopenharmony_ci        case SF_FORMAT_PCM_16:
4953a5a1b3Sopenharmony_ci        case SF_FORMAT_PCM_U8:
5053a5a1b3Sopenharmony_ci        case SF_FORMAT_PCM_S8:
5153a5a1b3Sopenharmony_ci            ss->format = PA_SAMPLE_S16NE;
5253a5a1b3Sopenharmony_ci            break;
5353a5a1b3Sopenharmony_ci
5453a5a1b3Sopenharmony_ci        case SF_FORMAT_PCM_24:
5553a5a1b3Sopenharmony_ci            ss->format = PA_SAMPLE_S24NE;
5653a5a1b3Sopenharmony_ci            break;
5753a5a1b3Sopenharmony_ci
5853a5a1b3Sopenharmony_ci        case SF_FORMAT_PCM_32:
5953a5a1b3Sopenharmony_ci            ss->format = PA_SAMPLE_S32NE;
6053a5a1b3Sopenharmony_ci            break;
6153a5a1b3Sopenharmony_ci
6253a5a1b3Sopenharmony_ci        case SF_FORMAT_ULAW:
6353a5a1b3Sopenharmony_ci            ss->format = PA_SAMPLE_ULAW;
6453a5a1b3Sopenharmony_ci            break;
6553a5a1b3Sopenharmony_ci
6653a5a1b3Sopenharmony_ci        case SF_FORMAT_ALAW:
6753a5a1b3Sopenharmony_ci            ss->format = PA_SAMPLE_ALAW;
6853a5a1b3Sopenharmony_ci            break;
6953a5a1b3Sopenharmony_ci
7053a5a1b3Sopenharmony_ci        case SF_FORMAT_FLOAT:
7153a5a1b3Sopenharmony_ci        case SF_FORMAT_DOUBLE:
7253a5a1b3Sopenharmony_ci        default:
7353a5a1b3Sopenharmony_ci            ss->format = PA_SAMPLE_FLOAT32NE;
7453a5a1b3Sopenharmony_ci            break;
7553a5a1b3Sopenharmony_ci    }
7653a5a1b3Sopenharmony_ci
7753a5a1b3Sopenharmony_ci    ss->rate = (uint32_t) sfi.samplerate;
7853a5a1b3Sopenharmony_ci    ss->channels = (uint8_t) sfi.channels;
7953a5a1b3Sopenharmony_ci
8053a5a1b3Sopenharmony_ci    if (!pa_sample_spec_valid(ss))
8153a5a1b3Sopenharmony_ci        return -1;
8253a5a1b3Sopenharmony_ci
8353a5a1b3Sopenharmony_ci    return 0;
8453a5a1b3Sopenharmony_ci}
8553a5a1b3Sopenharmony_ci
8653a5a1b3Sopenharmony_ciint pa_sndfile_write_sample_spec(SF_INFO *sfi, pa_sample_spec *ss) {
8753a5a1b3Sopenharmony_ci    pa_assert(sfi);
8853a5a1b3Sopenharmony_ci    pa_assert(ss);
8953a5a1b3Sopenharmony_ci
9053a5a1b3Sopenharmony_ci    sfi->samplerate = (int) ss->rate;
9153a5a1b3Sopenharmony_ci    sfi->channels = (int) ss->channels;
9253a5a1b3Sopenharmony_ci
9353a5a1b3Sopenharmony_ci    if (pa_sample_format_is_le(ss->format) > 0)
9453a5a1b3Sopenharmony_ci        sfi->format = SF_ENDIAN_LITTLE;
9553a5a1b3Sopenharmony_ci    else if (pa_sample_format_is_be(ss->format) > 0)
9653a5a1b3Sopenharmony_ci        sfi->format = SF_ENDIAN_BIG;
9753a5a1b3Sopenharmony_ci
9853a5a1b3Sopenharmony_ci    switch (ss->format) {
9953a5a1b3Sopenharmony_ci
10053a5a1b3Sopenharmony_ci        case PA_SAMPLE_U8:
10153a5a1b3Sopenharmony_ci            ss->format = PA_SAMPLE_S16NE;
10253a5a1b3Sopenharmony_ci            sfi->format = SF_FORMAT_PCM_U8;
10353a5a1b3Sopenharmony_ci            break;
10453a5a1b3Sopenharmony_ci
10553a5a1b3Sopenharmony_ci        case PA_SAMPLE_S16LE:
10653a5a1b3Sopenharmony_ci        case PA_SAMPLE_S16BE:
10753a5a1b3Sopenharmony_ci            ss->format = PA_SAMPLE_S16NE;
10853a5a1b3Sopenharmony_ci            sfi->format |= SF_FORMAT_PCM_16;
10953a5a1b3Sopenharmony_ci            break;
11053a5a1b3Sopenharmony_ci
11153a5a1b3Sopenharmony_ci        case PA_SAMPLE_S24LE:
11253a5a1b3Sopenharmony_ci        case PA_SAMPLE_S24BE:
11353a5a1b3Sopenharmony_ci            ss->format = PA_SAMPLE_S24NE;
11453a5a1b3Sopenharmony_ci            sfi->format |= SF_FORMAT_PCM_24;
11553a5a1b3Sopenharmony_ci            break;
11653a5a1b3Sopenharmony_ci
11753a5a1b3Sopenharmony_ci        case PA_SAMPLE_S24_32LE:
11853a5a1b3Sopenharmony_ci        case PA_SAMPLE_S24_32BE:
11953a5a1b3Sopenharmony_ci            ss->format = PA_SAMPLE_S24_32NE;
12053a5a1b3Sopenharmony_ci            sfi->format |= SF_FORMAT_PCM_32;
12153a5a1b3Sopenharmony_ci            break;
12253a5a1b3Sopenharmony_ci
12353a5a1b3Sopenharmony_ci        case PA_SAMPLE_S32LE:
12453a5a1b3Sopenharmony_ci        case PA_SAMPLE_S32BE:
12553a5a1b3Sopenharmony_ci            ss->format = PA_SAMPLE_S32NE;
12653a5a1b3Sopenharmony_ci            sfi->format |= SF_FORMAT_PCM_32;
12753a5a1b3Sopenharmony_ci            break;
12853a5a1b3Sopenharmony_ci
12953a5a1b3Sopenharmony_ci        case PA_SAMPLE_ULAW:
13053a5a1b3Sopenharmony_ci            sfi->format = SF_FORMAT_ULAW;
13153a5a1b3Sopenharmony_ci            break;
13253a5a1b3Sopenharmony_ci
13353a5a1b3Sopenharmony_ci        case PA_SAMPLE_ALAW:
13453a5a1b3Sopenharmony_ci            sfi->format = SF_FORMAT_ALAW;
13553a5a1b3Sopenharmony_ci            break;
13653a5a1b3Sopenharmony_ci
13753a5a1b3Sopenharmony_ci        case PA_SAMPLE_FLOAT32LE:
13853a5a1b3Sopenharmony_ci        case PA_SAMPLE_FLOAT32BE:
13953a5a1b3Sopenharmony_ci        default:
14053a5a1b3Sopenharmony_ci            ss->format = PA_SAMPLE_FLOAT32NE;
14153a5a1b3Sopenharmony_ci            sfi->format |= SF_FORMAT_FLOAT;
14253a5a1b3Sopenharmony_ci            break;
14353a5a1b3Sopenharmony_ci    }
14453a5a1b3Sopenharmony_ci
14553a5a1b3Sopenharmony_ci    if (!pa_sample_spec_valid(ss))
14653a5a1b3Sopenharmony_ci        return -1;
14753a5a1b3Sopenharmony_ci
14853a5a1b3Sopenharmony_ci    return 0;
14953a5a1b3Sopenharmony_ci}
15053a5a1b3Sopenharmony_ci
15153a5a1b3Sopenharmony_ciint pa_sndfile_read_channel_map(SNDFILE *sf, pa_channel_map *cm) {
15253a5a1b3Sopenharmony_ci
15353a5a1b3Sopenharmony_ci    static const pa_channel_position_t table[] = {
15453a5a1b3Sopenharmony_ci        [SF_CHANNEL_MAP_MONO] =                  PA_CHANNEL_POSITION_MONO,
15553a5a1b3Sopenharmony_ci        [SF_CHANNEL_MAP_LEFT] =                  PA_CHANNEL_POSITION_FRONT_LEFT, /* libsndfile distinguishes left and front-left, which we don't */
15653a5a1b3Sopenharmony_ci        [SF_CHANNEL_MAP_RIGHT] =                 PA_CHANNEL_POSITION_FRONT_RIGHT,
15753a5a1b3Sopenharmony_ci        [SF_CHANNEL_MAP_CENTER] =                PA_CHANNEL_POSITION_FRONT_CENTER,
15853a5a1b3Sopenharmony_ci        [SF_CHANNEL_MAP_FRONT_LEFT] =            PA_CHANNEL_POSITION_FRONT_LEFT,
15953a5a1b3Sopenharmony_ci        [SF_CHANNEL_MAP_FRONT_RIGHT] =           PA_CHANNEL_POSITION_FRONT_RIGHT,
16053a5a1b3Sopenharmony_ci        [SF_CHANNEL_MAP_FRONT_CENTER] =          PA_CHANNEL_POSITION_FRONT_CENTER,
16153a5a1b3Sopenharmony_ci        [SF_CHANNEL_MAP_REAR_CENTER] =           PA_CHANNEL_POSITION_REAR_CENTER,
16253a5a1b3Sopenharmony_ci        [SF_CHANNEL_MAP_REAR_LEFT] =             PA_CHANNEL_POSITION_REAR_LEFT,
16353a5a1b3Sopenharmony_ci        [SF_CHANNEL_MAP_REAR_RIGHT] =            PA_CHANNEL_POSITION_REAR_RIGHT,
16453a5a1b3Sopenharmony_ci        [SF_CHANNEL_MAP_LFE] =                   PA_CHANNEL_POSITION_LFE,
16553a5a1b3Sopenharmony_ci        [SF_CHANNEL_MAP_FRONT_LEFT_OF_CENTER] =  PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER,
16653a5a1b3Sopenharmony_ci        [SF_CHANNEL_MAP_FRONT_RIGHT_OF_CENTER] = PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER,
16753a5a1b3Sopenharmony_ci        [SF_CHANNEL_MAP_SIDE_LEFT] =             PA_CHANNEL_POSITION_SIDE_LEFT,
16853a5a1b3Sopenharmony_ci        [SF_CHANNEL_MAP_SIDE_RIGHT] =            PA_CHANNEL_POSITION_SIDE_RIGHT,
16953a5a1b3Sopenharmony_ci        [SF_CHANNEL_MAP_TOP_CENTER] =            PA_CHANNEL_POSITION_TOP_CENTER,
17053a5a1b3Sopenharmony_ci        [SF_CHANNEL_MAP_TOP_FRONT_LEFT] =        PA_CHANNEL_POSITION_TOP_FRONT_LEFT,
17153a5a1b3Sopenharmony_ci        [SF_CHANNEL_MAP_TOP_FRONT_RIGHT] =       PA_CHANNEL_POSITION_TOP_FRONT_RIGHT,
17253a5a1b3Sopenharmony_ci        [SF_CHANNEL_MAP_TOP_FRONT_CENTER] =      PA_CHANNEL_POSITION_TOP_FRONT_CENTER,
17353a5a1b3Sopenharmony_ci        [SF_CHANNEL_MAP_TOP_REAR_LEFT] =         PA_CHANNEL_POSITION_TOP_REAR_LEFT,
17453a5a1b3Sopenharmony_ci        [SF_CHANNEL_MAP_TOP_REAR_RIGHT] =        PA_CHANNEL_POSITION_TOP_REAR_RIGHT,
17553a5a1b3Sopenharmony_ci        [SF_CHANNEL_MAP_TOP_REAR_CENTER] =       PA_CHANNEL_POSITION_TOP_REAR_CENTER
17653a5a1b3Sopenharmony_ci    };
17753a5a1b3Sopenharmony_ci
17853a5a1b3Sopenharmony_ci    SF_INFO sfi;
17953a5a1b3Sopenharmony_ci    int sf_errno;
18053a5a1b3Sopenharmony_ci    int *channels;
18153a5a1b3Sopenharmony_ci    unsigned c;
18253a5a1b3Sopenharmony_ci
18353a5a1b3Sopenharmony_ci    pa_assert(sf);
18453a5a1b3Sopenharmony_ci    pa_assert(cm);
18553a5a1b3Sopenharmony_ci
18653a5a1b3Sopenharmony_ci    pa_zero(sfi);
18753a5a1b3Sopenharmony_ci    if ((sf_errno = sf_command(sf, SFC_GET_CURRENT_SF_INFO, &sfi, sizeof(sfi)))) {
18853a5a1b3Sopenharmony_ci        pa_log_error("sndfile: %s", sf_error_number(sf_errno));
18953a5a1b3Sopenharmony_ci        return -1;
19053a5a1b3Sopenharmony_ci    }
19153a5a1b3Sopenharmony_ci
19253a5a1b3Sopenharmony_ci    channels = pa_xnew(int, sfi.channels);
19353a5a1b3Sopenharmony_ci    if (!sf_command(sf, SFC_GET_CHANNEL_MAP_INFO, channels, sizeof(channels[0]) * sfi.channels)) {
19453a5a1b3Sopenharmony_ci        pa_xfree(channels);
19553a5a1b3Sopenharmony_ci        return -1;
19653a5a1b3Sopenharmony_ci    }
19753a5a1b3Sopenharmony_ci
19853a5a1b3Sopenharmony_ci    cm->channels = (uint8_t) sfi.channels;
19953a5a1b3Sopenharmony_ci    for (c = 0; c < cm->channels; c++) {
20053a5a1b3Sopenharmony_ci        if (channels[c] <= SF_CHANNEL_MAP_INVALID ||
20153a5a1b3Sopenharmony_ci            (unsigned) channels[c] >= PA_ELEMENTSOF(table)) {
20253a5a1b3Sopenharmony_ci            pa_xfree(channels);
20353a5a1b3Sopenharmony_ci            return -1;
20453a5a1b3Sopenharmony_ci        }
20553a5a1b3Sopenharmony_ci
20653a5a1b3Sopenharmony_ci        cm->map[c] = table[channels[c]];
20753a5a1b3Sopenharmony_ci    }
20853a5a1b3Sopenharmony_ci
20953a5a1b3Sopenharmony_ci    pa_xfree(channels);
21053a5a1b3Sopenharmony_ci
21153a5a1b3Sopenharmony_ci    if (!pa_channel_map_valid(cm))
21253a5a1b3Sopenharmony_ci        return -1;
21353a5a1b3Sopenharmony_ci
21453a5a1b3Sopenharmony_ci    return 0;
21553a5a1b3Sopenharmony_ci}
21653a5a1b3Sopenharmony_ci
21753a5a1b3Sopenharmony_ciint pa_sndfile_write_channel_map(SNDFILE *sf, pa_channel_map *cm) {
21853a5a1b3Sopenharmony_ci    static const int table[PA_CHANNEL_POSITION_MAX] = {
21953a5a1b3Sopenharmony_ci        [PA_CHANNEL_POSITION_MONO] = SF_CHANNEL_MAP_MONO,
22053a5a1b3Sopenharmony_ci
22153a5a1b3Sopenharmony_ci        [PA_CHANNEL_POSITION_FRONT_LEFT] = SF_CHANNEL_MAP_FRONT_LEFT,
22253a5a1b3Sopenharmony_ci        [PA_CHANNEL_POSITION_FRONT_RIGHT] = SF_CHANNEL_MAP_FRONT_RIGHT,
22353a5a1b3Sopenharmony_ci        [PA_CHANNEL_POSITION_FRONT_CENTER] = SF_CHANNEL_MAP_FRONT_CENTER,
22453a5a1b3Sopenharmony_ci
22553a5a1b3Sopenharmony_ci        [PA_CHANNEL_POSITION_REAR_CENTER] = SF_CHANNEL_MAP_REAR_CENTER,
22653a5a1b3Sopenharmony_ci        [PA_CHANNEL_POSITION_REAR_LEFT] = SF_CHANNEL_MAP_REAR_LEFT,
22753a5a1b3Sopenharmony_ci        [PA_CHANNEL_POSITION_REAR_RIGHT] = SF_CHANNEL_MAP_REAR_RIGHT,
22853a5a1b3Sopenharmony_ci
22953a5a1b3Sopenharmony_ci        [PA_CHANNEL_POSITION_LFE] = SF_CHANNEL_MAP_LFE,
23053a5a1b3Sopenharmony_ci
23153a5a1b3Sopenharmony_ci        [PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER] = SF_CHANNEL_MAP_FRONT_LEFT_OF_CENTER,
23253a5a1b3Sopenharmony_ci        [PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER] = SF_CHANNEL_MAP_FRONT_RIGHT_OF_CENTER,
23353a5a1b3Sopenharmony_ci
23453a5a1b3Sopenharmony_ci        [PA_CHANNEL_POSITION_SIDE_LEFT] = SF_CHANNEL_MAP_SIDE_LEFT,
23553a5a1b3Sopenharmony_ci        [PA_CHANNEL_POSITION_SIDE_RIGHT] = SF_CHANNEL_MAP_SIDE_RIGHT,
23653a5a1b3Sopenharmony_ci
23753a5a1b3Sopenharmony_ci        [PA_CHANNEL_POSITION_AUX0] = -1,
23853a5a1b3Sopenharmony_ci        [PA_CHANNEL_POSITION_AUX1] = -1,
23953a5a1b3Sopenharmony_ci        [PA_CHANNEL_POSITION_AUX2] = -1,
24053a5a1b3Sopenharmony_ci        [PA_CHANNEL_POSITION_AUX3] = -1,
24153a5a1b3Sopenharmony_ci        [PA_CHANNEL_POSITION_AUX4] = -1,
24253a5a1b3Sopenharmony_ci        [PA_CHANNEL_POSITION_AUX5] = -1,
24353a5a1b3Sopenharmony_ci        [PA_CHANNEL_POSITION_AUX6] = -1,
24453a5a1b3Sopenharmony_ci        [PA_CHANNEL_POSITION_AUX7] = -1,
24553a5a1b3Sopenharmony_ci        [PA_CHANNEL_POSITION_AUX8] = -1,
24653a5a1b3Sopenharmony_ci        [PA_CHANNEL_POSITION_AUX9] = -1,
24753a5a1b3Sopenharmony_ci        [PA_CHANNEL_POSITION_AUX10] = -1,
24853a5a1b3Sopenharmony_ci        [PA_CHANNEL_POSITION_AUX11] = -1,
24953a5a1b3Sopenharmony_ci        [PA_CHANNEL_POSITION_AUX12] = -1,
25053a5a1b3Sopenharmony_ci        [PA_CHANNEL_POSITION_AUX13] = -1,
25153a5a1b3Sopenharmony_ci        [PA_CHANNEL_POSITION_AUX14] = -1,
25253a5a1b3Sopenharmony_ci        [PA_CHANNEL_POSITION_AUX15] = -1,
25353a5a1b3Sopenharmony_ci        [PA_CHANNEL_POSITION_AUX16] = -1,
25453a5a1b3Sopenharmony_ci        [PA_CHANNEL_POSITION_AUX17] = -1,
25553a5a1b3Sopenharmony_ci        [PA_CHANNEL_POSITION_AUX18] = -1,
25653a5a1b3Sopenharmony_ci        [PA_CHANNEL_POSITION_AUX19] = -1,
25753a5a1b3Sopenharmony_ci        [PA_CHANNEL_POSITION_AUX20] = -1,
25853a5a1b3Sopenharmony_ci        [PA_CHANNEL_POSITION_AUX21] = -1,
25953a5a1b3Sopenharmony_ci        [PA_CHANNEL_POSITION_AUX22] = -1,
26053a5a1b3Sopenharmony_ci        [PA_CHANNEL_POSITION_AUX23] = -1,
26153a5a1b3Sopenharmony_ci        [PA_CHANNEL_POSITION_AUX24] = -1,
26253a5a1b3Sopenharmony_ci        [PA_CHANNEL_POSITION_AUX25] = -1,
26353a5a1b3Sopenharmony_ci        [PA_CHANNEL_POSITION_AUX26] = -1,
26453a5a1b3Sopenharmony_ci        [PA_CHANNEL_POSITION_AUX27] = -1,
26553a5a1b3Sopenharmony_ci        [PA_CHANNEL_POSITION_AUX28] = -1,
26653a5a1b3Sopenharmony_ci        [PA_CHANNEL_POSITION_AUX29] = -1,
26753a5a1b3Sopenharmony_ci        [PA_CHANNEL_POSITION_AUX30] = -1,
26853a5a1b3Sopenharmony_ci        [PA_CHANNEL_POSITION_AUX31] = -1,
26953a5a1b3Sopenharmony_ci
27053a5a1b3Sopenharmony_ci        [PA_CHANNEL_POSITION_TOP_CENTER] = SF_CHANNEL_MAP_TOP_CENTER,
27153a5a1b3Sopenharmony_ci
27253a5a1b3Sopenharmony_ci        [PA_CHANNEL_POSITION_TOP_FRONT_LEFT] = SF_CHANNEL_MAP_TOP_FRONT_LEFT,
27353a5a1b3Sopenharmony_ci        [PA_CHANNEL_POSITION_TOP_FRONT_RIGHT] = SF_CHANNEL_MAP_TOP_FRONT_RIGHT,
27453a5a1b3Sopenharmony_ci        [PA_CHANNEL_POSITION_TOP_FRONT_CENTER] = SF_CHANNEL_MAP_TOP_FRONT_CENTER ,
27553a5a1b3Sopenharmony_ci
27653a5a1b3Sopenharmony_ci        [PA_CHANNEL_POSITION_TOP_REAR_LEFT] = SF_CHANNEL_MAP_TOP_REAR_LEFT,
27753a5a1b3Sopenharmony_ci        [PA_CHANNEL_POSITION_TOP_REAR_RIGHT] = SF_CHANNEL_MAP_TOP_REAR_RIGHT,
27853a5a1b3Sopenharmony_ci        [PA_CHANNEL_POSITION_TOP_REAR_CENTER] = SF_CHANNEL_MAP_TOP_REAR_CENTER,
27953a5a1b3Sopenharmony_ci    };
28053a5a1b3Sopenharmony_ci
28153a5a1b3Sopenharmony_ci    int *channels;
28253a5a1b3Sopenharmony_ci    unsigned c;
28353a5a1b3Sopenharmony_ci
28453a5a1b3Sopenharmony_ci    pa_assert(sf);
28553a5a1b3Sopenharmony_ci    pa_assert(cm);
28653a5a1b3Sopenharmony_ci
28753a5a1b3Sopenharmony_ci    /* Suppress channel mapping for the obvious cases */
28853a5a1b3Sopenharmony_ci    if (cm->channels == 1 && cm->map[0] == PA_CHANNEL_POSITION_MONO)
28953a5a1b3Sopenharmony_ci        return 0;
29053a5a1b3Sopenharmony_ci
29153a5a1b3Sopenharmony_ci    if (cm->channels == 2 &&
29253a5a1b3Sopenharmony_ci        cm->map[0] == PA_CHANNEL_POSITION_FRONT_LEFT &&
29353a5a1b3Sopenharmony_ci        cm->map[1] == PA_CHANNEL_POSITION_FRONT_RIGHT)
29453a5a1b3Sopenharmony_ci        return 0;
29553a5a1b3Sopenharmony_ci
29653a5a1b3Sopenharmony_ci    channels = pa_xnew(int, cm->channels);
29753a5a1b3Sopenharmony_ci    for (c = 0; c < cm->channels; c++) {
29853a5a1b3Sopenharmony_ci
29953a5a1b3Sopenharmony_ci        if (cm->map[c] < 0 ||
30053a5a1b3Sopenharmony_ci            cm->map[c] >= PA_CHANNEL_POSITION_MAX ||
30153a5a1b3Sopenharmony_ci            table[cm->map[c]] < 0) {
30253a5a1b3Sopenharmony_ci            pa_xfree(channels);
30353a5a1b3Sopenharmony_ci            return -1;
30453a5a1b3Sopenharmony_ci        }
30553a5a1b3Sopenharmony_ci
30653a5a1b3Sopenharmony_ci        channels[c] = table[cm->map[c]];
30753a5a1b3Sopenharmony_ci    }
30853a5a1b3Sopenharmony_ci
30953a5a1b3Sopenharmony_ci    if (!sf_command(sf, SFC_SET_CHANNEL_MAP_INFO, channels, sizeof(channels[0]) * cm->channels)) {
31053a5a1b3Sopenharmony_ci        pa_xfree(channels);
31153a5a1b3Sopenharmony_ci        return -1;
31253a5a1b3Sopenharmony_ci    }
31353a5a1b3Sopenharmony_ci
31453a5a1b3Sopenharmony_ci    pa_xfree(channels);
31553a5a1b3Sopenharmony_ci    return 0;
31653a5a1b3Sopenharmony_ci}
31753a5a1b3Sopenharmony_ci
31853a5a1b3Sopenharmony_civoid pa_sndfile_init_proplist(SNDFILE *sf, pa_proplist *p) {
31953a5a1b3Sopenharmony_ci
32053a5a1b3Sopenharmony_ci    static const char* table[] = {
32153a5a1b3Sopenharmony_ci        [SF_STR_TITLE] = PA_PROP_MEDIA_TITLE,
32253a5a1b3Sopenharmony_ci        [SF_STR_COPYRIGHT] = PA_PROP_MEDIA_COPYRIGHT,
32353a5a1b3Sopenharmony_ci        [SF_STR_SOFTWARE] = PA_PROP_MEDIA_SOFTWARE,
32453a5a1b3Sopenharmony_ci        [SF_STR_ARTIST] = PA_PROP_MEDIA_ARTIST,
32553a5a1b3Sopenharmony_ci        [SF_STR_COMMENT] = "media.comment",
32653a5a1b3Sopenharmony_ci        [SF_STR_DATE] = "media.date"
32753a5a1b3Sopenharmony_ci    };
32853a5a1b3Sopenharmony_ci
32953a5a1b3Sopenharmony_ci    SF_INFO sfi;
33053a5a1b3Sopenharmony_ci    SF_FORMAT_INFO fi;
33153a5a1b3Sopenharmony_ci    int sf_errno;
33253a5a1b3Sopenharmony_ci    unsigned c;
33353a5a1b3Sopenharmony_ci
33453a5a1b3Sopenharmony_ci    pa_assert(sf);
33553a5a1b3Sopenharmony_ci    pa_assert(p);
33653a5a1b3Sopenharmony_ci
33753a5a1b3Sopenharmony_ci    for (c = 0; c < PA_ELEMENTSOF(table); c++) {
33853a5a1b3Sopenharmony_ci        const char *s;
33953a5a1b3Sopenharmony_ci        char *t;
34053a5a1b3Sopenharmony_ci
34153a5a1b3Sopenharmony_ci        if (!table[c])
34253a5a1b3Sopenharmony_ci            continue;
34353a5a1b3Sopenharmony_ci
34453a5a1b3Sopenharmony_ci        if (!(s = sf_get_string(sf, c)))
34553a5a1b3Sopenharmony_ci            continue;
34653a5a1b3Sopenharmony_ci
34753a5a1b3Sopenharmony_ci        t = pa_utf8_filter(s);
34853a5a1b3Sopenharmony_ci        pa_proplist_sets(p, table[c], t);
34953a5a1b3Sopenharmony_ci        pa_xfree(t);
35053a5a1b3Sopenharmony_ci    }
35153a5a1b3Sopenharmony_ci
35253a5a1b3Sopenharmony_ci    pa_zero(sfi);
35353a5a1b3Sopenharmony_ci    if ((sf_errno = sf_command(sf, SFC_GET_CURRENT_SF_INFO, &sfi, sizeof(sfi)))) {
35453a5a1b3Sopenharmony_ci        pa_log_error("sndfile: %s", sf_error_number(sf_errno));
35553a5a1b3Sopenharmony_ci        return;
35653a5a1b3Sopenharmony_ci    }
35753a5a1b3Sopenharmony_ci
35853a5a1b3Sopenharmony_ci    pa_zero(fi);
35953a5a1b3Sopenharmony_ci    fi.format = sfi.format;
36053a5a1b3Sopenharmony_ci    if (sf_command(sf, SFC_GET_FORMAT_INFO, &fi, sizeof(fi)) == 0 && fi.name) {
36153a5a1b3Sopenharmony_ci        char *t;
36253a5a1b3Sopenharmony_ci
36353a5a1b3Sopenharmony_ci        t = pa_utf8_filter(fi.name);
36453a5a1b3Sopenharmony_ci        pa_proplist_sets(p, "media.format", t);
36553a5a1b3Sopenharmony_ci        pa_xfree(t);
36653a5a1b3Sopenharmony_ci    }
36753a5a1b3Sopenharmony_ci}
36853a5a1b3Sopenharmony_ci
36953a5a1b3Sopenharmony_cipa_sndfile_readf_t pa_sndfile_readf_function(const pa_sample_spec *ss) {
37053a5a1b3Sopenharmony_ci    pa_assert(ss);
37153a5a1b3Sopenharmony_ci
37253a5a1b3Sopenharmony_ci    switch (ss->format) {
37353a5a1b3Sopenharmony_ci        case PA_SAMPLE_S16NE:
37453a5a1b3Sopenharmony_ci            return (pa_sndfile_readf_t) sf_readf_short;
37553a5a1b3Sopenharmony_ci
37653a5a1b3Sopenharmony_ci        case PA_SAMPLE_S32NE:
37753a5a1b3Sopenharmony_ci        case PA_SAMPLE_S24_32NE:
37853a5a1b3Sopenharmony_ci            return (pa_sndfile_readf_t) sf_readf_int;
37953a5a1b3Sopenharmony_ci
38053a5a1b3Sopenharmony_ci        case PA_SAMPLE_FLOAT32NE:
38153a5a1b3Sopenharmony_ci            return (pa_sndfile_readf_t) sf_readf_float;
38253a5a1b3Sopenharmony_ci
38353a5a1b3Sopenharmony_ci        case PA_SAMPLE_ULAW:
38453a5a1b3Sopenharmony_ci        case PA_SAMPLE_ALAW:
38553a5a1b3Sopenharmony_ci        case PA_SAMPLE_S24NE:
38653a5a1b3Sopenharmony_ci            return NULL;
38753a5a1b3Sopenharmony_ci
38853a5a1b3Sopenharmony_ci        default:
38953a5a1b3Sopenharmony_ci            pa_assert_not_reached();
39053a5a1b3Sopenharmony_ci    }
39153a5a1b3Sopenharmony_ci}
39253a5a1b3Sopenharmony_ci
39353a5a1b3Sopenharmony_cipa_sndfile_writef_t pa_sndfile_writef_function(const pa_sample_spec *ss) {
39453a5a1b3Sopenharmony_ci    pa_assert(ss);
39553a5a1b3Sopenharmony_ci
39653a5a1b3Sopenharmony_ci    switch (ss->format) {
39753a5a1b3Sopenharmony_ci        case PA_SAMPLE_S16NE:
39853a5a1b3Sopenharmony_ci            return (pa_sndfile_writef_t) sf_writef_short;
39953a5a1b3Sopenharmony_ci
40053a5a1b3Sopenharmony_ci        case PA_SAMPLE_S32NE:
40153a5a1b3Sopenharmony_ci        case PA_SAMPLE_S24_32NE:
40253a5a1b3Sopenharmony_ci            return (pa_sndfile_writef_t) sf_writef_int;
40353a5a1b3Sopenharmony_ci
40453a5a1b3Sopenharmony_ci        case PA_SAMPLE_FLOAT32NE:
40553a5a1b3Sopenharmony_ci            return (pa_sndfile_writef_t) sf_writef_float;
40653a5a1b3Sopenharmony_ci
40753a5a1b3Sopenharmony_ci        case PA_SAMPLE_ULAW:
40853a5a1b3Sopenharmony_ci        case PA_SAMPLE_ALAW:
40953a5a1b3Sopenharmony_ci        case PA_SAMPLE_S24NE:
41053a5a1b3Sopenharmony_ci            return NULL;
41153a5a1b3Sopenharmony_ci
41253a5a1b3Sopenharmony_ci        default:
41353a5a1b3Sopenharmony_ci            pa_assert_not_reached();
41453a5a1b3Sopenharmony_ci    }
41553a5a1b3Sopenharmony_ci}
41653a5a1b3Sopenharmony_ci
41753a5a1b3Sopenharmony_ciint pa_sndfile_format_from_string(const char *name) {
41853a5a1b3Sopenharmony_ci    int i, count = 0;
41953a5a1b3Sopenharmony_ci
42053a5a1b3Sopenharmony_ci    if (!name[0])
42153a5a1b3Sopenharmony_ci        return -1;
42253a5a1b3Sopenharmony_ci
42353a5a1b3Sopenharmony_ci    pa_assert_se(sf_command(NULL, SFC_GET_FORMAT_MAJOR_COUNT, &count, sizeof(int)) == 0);
42453a5a1b3Sopenharmony_ci
42553a5a1b3Sopenharmony_ci    for (i = 0; i < count; i++) {
42653a5a1b3Sopenharmony_ci        SF_FORMAT_INFO fi;
42753a5a1b3Sopenharmony_ci        pa_zero(fi);
42853a5a1b3Sopenharmony_ci        fi.format = i;
42953a5a1b3Sopenharmony_ci
43053a5a1b3Sopenharmony_ci        pa_assert_se(sf_command(NULL, SFC_GET_FORMAT_MAJOR, &fi, sizeof(fi)) == 0);
43153a5a1b3Sopenharmony_ci
43253a5a1b3Sopenharmony_ci        /* First try to match via full type string */
43353a5a1b3Sopenharmony_ci        if (strcasecmp(name, fi.name) == 0)
43453a5a1b3Sopenharmony_ci            return fi.format;
43553a5a1b3Sopenharmony_ci
43653a5a1b3Sopenharmony_ci        /* Then, try to match via the full extension */
43753a5a1b3Sopenharmony_ci        if (strcasecmp(name, fi.extension) == 0)
43853a5a1b3Sopenharmony_ci            return fi.format;
43953a5a1b3Sopenharmony_ci
44053a5a1b3Sopenharmony_ci        /* Then, try to match via the start of the type string */
44153a5a1b3Sopenharmony_ci        if (strncasecmp(name, fi.name, strlen(name)) == 0)
44253a5a1b3Sopenharmony_ci            return fi.format;
44353a5a1b3Sopenharmony_ci    }
44453a5a1b3Sopenharmony_ci
44553a5a1b3Sopenharmony_ci    return -1;
44653a5a1b3Sopenharmony_ci}
44753a5a1b3Sopenharmony_ci
44853a5a1b3Sopenharmony_civoid pa_sndfile_dump_formats(void) {
44953a5a1b3Sopenharmony_ci    int i, count = 0;
45053a5a1b3Sopenharmony_ci
45153a5a1b3Sopenharmony_ci    pa_assert_se(sf_command(NULL, SFC_GET_FORMAT_MAJOR_COUNT, &count, sizeof(int)) == 0);
45253a5a1b3Sopenharmony_ci
45353a5a1b3Sopenharmony_ci    for (i = 0; i < count; i++) {
45453a5a1b3Sopenharmony_ci        SF_FORMAT_INFO fi;
45553a5a1b3Sopenharmony_ci        pa_zero(fi);
45653a5a1b3Sopenharmony_ci        fi.format = i;
45753a5a1b3Sopenharmony_ci
45853a5a1b3Sopenharmony_ci        pa_assert_se(sf_command(NULL, SFC_GET_FORMAT_MAJOR, &fi, sizeof(fi)) == 0);
45953a5a1b3Sopenharmony_ci        printf("%s\t%s\n", fi.extension, fi.name);
46053a5a1b3Sopenharmony_ci    }
46153a5a1b3Sopenharmony_ci}
462