root/lib/virtual_oss/bt/sbc_encode.c
/*-
 * Copyright (c) 2015 Nathanial Sloss <nathanialsloss@yahoo.com.au>
 *
 *              This software is dedicated to the memory of -
 *         Baron James Anlezark (Barry) - 1 Jan 1949 - 13 May 2012.
 *
 *              Barry was a man who loved his music.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include <sys/cdefs.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/endian.h>
#include <sys/uio.h>

#include <stdio.h>
#include <errno.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <math.h>

#include "sbc_coeffs.h"
#include "bt.h"

#define SYNCWORD        0x9c
#define ABS(x)          (((x) < 0) ? -(x) : (x))
#define BIT30           (1U << 30)
#define BM(x)           ((1LL << (x)) - 1LL)

/* Loudness offset allocations. */
static const int loudnessoffset8[4][8] = {
        {-2, 0, 0, 0, 0, 0, 0, 1},
        {-3, 0, 0, 0, 0, 0, 1, 2},
        {-4, 0, 0, 0, 0, 0, 1, 2},
        {-4, 0, 0, 0, 0, 0, 1, 2},
};

static const int loudnessoffset4[4][4] = {
        {-1, 0, 0, 0},
        {-2, 0, 0, 1},
        {-2, 0, 0, 1},
        {-2, 0, 0, 1},
};

static uint8_t
calc_scalefactors_joint(struct sbc_encode *sbc)
{
        float sb_j[16][2];
        uint32_t x;
        uint32_t y;
        uint8_t block;
        uint8_t joint;
        uint8_t sb;
        uint8_t lz;

        joint = 0;
        for (sb = 0; sb != sbc->bands - 1; sb++) {
                for (block = 0; block < sbc->blocks; block++) {
                        sb_j[block][0] = (sbc->samples[block][0][sb] +
                            sbc->samples[block][1][sb]) / 2.0f;
                        sb_j[block][1] = (sbc->samples[block][0][sb] -
                            sbc->samples[block][1][sb]) / 2.0f;
                }

                x = 1 << 15;
                y = 1 << 15;
                for (block = 0; block < sbc->blocks; block++) {
                        x |= (uint32_t)ABS(sb_j[block][0]);
                        y |= (uint32_t)ABS(sb_j[block][1]);
                }

                lz = 1;
                while (!(x & BIT30)) {
                        lz++;
                        x <<= 1;
                }
                x = 16 - lz;

                lz = 1;
                while (!(y & BIT30)) {
                        lz++;
                        y <<= 1;
                }
                y = 16 - lz;

                if ((sbc->scalefactor[0][sb] + sbc->scalefactor[1][sb]) > x + y) {
                        joint |= 1 << (sbc->bands - sb - 1);
                        sbc->scalefactor[0][sb] = x;
                        sbc->scalefactor[1][sb] = y;
                        for (block = 0; block < sbc->blocks; block++) {
                                sbc->samples[block][0][sb] = sb_j[block][0];
                                sbc->samples[block][1][sb] = sb_j[block][1];
                        }
                }
        }
        return (joint);
}

static void
calc_scalefactors(struct sbc_encode *sbc)
{
        uint8_t block;
        uint8_t ch;
        uint8_t sb;

        for (ch = 0; ch != sbc->channels; ch++) {
                for (sb = 0; sb != sbc->bands; sb++) {
                        uint32_t x = 1 << 15;
                        uint8_t lx = 1;

                        for (block = 0; block != sbc->blocks; block++)
                                x |= (uint32_t)ABS(sbc->samples[block][ch][sb]);

                        while (!(x & BIT30)) {
                                lx++;
                                x <<= 1;
                        }
                        sbc->scalefactor[ch][sb] = 16 - lx;
                }
        }
}

static void
calc_bitneed(struct bt_config *cfg)
{
        struct sbc_encode *sbc = cfg->handle.sbc_enc;
        int32_t bitneed[2][8];
        int32_t max_bitneed, bitcount;
        int32_t slicecount, bitslice;
        int32_t loudness;
        int ch, sb, start_chan = 0;

        if (cfg->chmode == MODE_DUAL)
                sbc->channels = 1;

next_chan:
        max_bitneed = 0;
        bitcount = 0;
        slicecount = 0;

        if (cfg->allocm == ALLOC_SNR) {
                for (ch = start_chan; ch < sbc->channels; ch++) {
                        for (sb = 0; sb < sbc->bands; sb++) {
                                bitneed[ch][sb] = sbc->scalefactor[ch][sb];

                                if (bitneed[ch][sb] > max_bitneed)
                                        max_bitneed = bitneed[ch][sb];
                        }
                }
        } else {
                for (ch = start_chan; ch < sbc->channels; ch++) {
                        for (sb = 0; sb < sbc->bands; sb++) {
                                if (sbc->scalefactor[ch][sb] == 0) {
                                        bitneed[ch][sb] = -5;
                                } else {
                                        if (sbc->bands == 8) {
                                                loudness = sbc->scalefactor[ch][sb] -
                                                    loudnessoffset8[cfg->freq][sb];
                                        } else {
                                                loudness = sbc->scalefactor[ch][sb] -
                                                    loudnessoffset4[cfg->freq][sb];
                                        }
                                        if (loudness > 0)
                                                bitneed[ch][sb] = loudness / 2;
                                        else
                                                bitneed[ch][sb] = loudness;
                                }
                                if (bitneed[ch][sb] > max_bitneed)
                                        max_bitneed = bitneed[ch][sb];
                        }
                }
        }

        slicecount = bitcount = 0;
        bitslice = max_bitneed + 1;
        do {
                bitslice--;
                bitcount += slicecount;
                slicecount = 0;
                for (ch = start_chan; ch < sbc->channels; ch++) {
                        for (sb = 0; sb < sbc->bands; sb++) {
                                if ((bitneed[ch][sb] > bitslice + 1) &&
                                    (bitneed[ch][sb] < bitslice + 16))
                                        slicecount++;
                                else if (bitneed[ch][sb] == bitslice + 1)
                                        slicecount += 2;
                        }
                }
        } while (bitcount + slicecount < cfg->bitpool);

        /* check if exactly one more fits */
        if (bitcount + slicecount == cfg->bitpool) {
                bitcount += slicecount;
                bitslice--;
        }
        for (ch = start_chan; ch < sbc->channels; ch++) {
                for (sb = 0; sb < sbc->bands; sb++) {
                        if (bitneed[ch][sb] < bitslice + 2) {
                                sbc->bits[ch][sb] = 0;
                        } else {
                                sbc->bits[ch][sb] = bitneed[ch][sb] - bitslice;
                                if (sbc->bits[ch][sb] > 16)
                                        sbc->bits[ch][sb] = 16;
                        }
                }
        }

        if (cfg->chmode == MODE_DUAL)
                ch = start_chan;
        else
                ch = 0;
        sb = 0;
        while (bitcount < cfg->bitpool && sb < sbc->bands) {
                if ((sbc->bits[ch][sb] >= 2) && (sbc->bits[ch][sb] < 16)) {
                        sbc->bits[ch][sb]++;
                        bitcount++;
                } else if ((bitneed[ch][sb] == bitslice + 1) &&
                    (cfg->bitpool > bitcount + 1)) {
                        sbc->bits[ch][sb] = 2;
                        bitcount += 2;
                }
                if (sbc->channels == 1 || start_chan == 1)
                        sb++;
                else if (ch == 1) {
                        ch = 0;
                        sb++;
                } else
                        ch = 1;
        }

        if (cfg->chmode == MODE_DUAL)
                ch = start_chan;
        else
                ch = 0;
        sb = 0;
        while (bitcount < cfg->bitpool && sb < sbc->bands) {
                if (sbc->bits[ch][sb] < 16) {
                        sbc->bits[ch][sb]++;
                        bitcount++;
                }
                if (sbc->channels == 1 || start_chan == 1)
                        sb++;
                else if (ch == 1) {
                        ch = 0;
                        sb++;
                } else
                        ch = 1;
        }

        if (cfg->chmode == MODE_DUAL && start_chan == 0) {
                start_chan = 1;
                sbc->channels = 2;
                goto next_chan;
        }
}

static void
sbc_store_bits_crc(struct sbc_encode *sbc, uint32_t numbits, uint32_t value)
{
        uint32_t off = sbc->bitoffset;

        while (numbits-- && off != sbc->maxoffset) {
                if (value & (1 << numbits)) {
                        sbc->data[off / 8] |= 1 << ((7 - off) & 7);
                        sbc->crc ^= 0x80;
                }
                sbc->crc *= 2;
                if (sbc->crc & 0x100)
                        sbc->crc ^= 0x11d;      /* CRC-8 polynomial */

                off++;
        }
        sbc->bitoffset = off;
}

static int
sbc_encode(struct bt_config *cfg)
{
        struct sbc_encode *sbc = cfg->handle.sbc_enc;
        const int16_t *input = sbc->music_data;
        float delta[2][8];
        float levels[2][8];
        float mask[2][8];
        float S;
        float *X;
        float Z[80];
        float Y[80];
        float audioout;
        int16_t left[8];
        int16_t right[8];
        int16_t *data;
        int numsamples;
        int i;
        int k;
        int block;
        int chan;
        int sb;

        for (block = 0; block < sbc->blocks; block++) {

                for (i = 0; i < sbc->bands; i++) {
                        left[i] = *input++;
                        if (sbc->channels == 2)
                                right[i] = *input++;
                }

                for (chan = 0; chan < sbc->channels; chan++) {

                        /* select right or left channel */
                        if (chan == 0) {
                                X = sbc->left;
                                data = left;
                        } else {
                                X = sbc->right;
                                data = right;
                        }

                        /* shift up old data */
                        for (i = (sbc->bands * 10) - 1; i > sbc->bands - 1; i--)
                                X[i] = X[i - sbc->bands];
                        k = 0;
                        for (i = sbc->bands - 1; i >= 0; i--)
                                X[i] = data[k++];
                        for (i = 0; i < sbc->bands * 10; i++) {
                                if (sbc->bands == 8)
                                        Z[i] = sbc_coeffs8[i] * X[i];
                                else
                                        Z[i] = sbc_coeffs4[i] * X[i];
                        }
                        for (i = 0; i < sbc->bands * 2; i++) {
                                Y[i] = 0;
                                for (k = 0; k < 5; k++)
                                        Y[i] += Z[i + k * sbc->bands * 2];
                        }
                        for (i = 0; i < sbc->bands; i++) {
                                S = 0;
                                for (k = 0; k < sbc->bands * 2; k++) {
                                        if (sbc->bands == 8) {
                                                S += cosdata8[i][k] * Y[k];
                                        } else {
                                                S += cosdata4[i][k] * Y[k];
                                        }
                                }
                                sbc->samples[block][chan][i] = S * (1 << 15);
                        }
                }
        }

        calc_scalefactors(sbc);

        if (cfg->chmode == MODE_JOINT)
                sbc->join = calc_scalefactors_joint(sbc);
        else
                sbc->join = 0;

        calc_bitneed(cfg);

        for (chan = 0; chan < sbc->channels; chan++) {
                for (sb = 0; sb < sbc->bands; sb++) {
                        if (sbc->bits[chan][sb] == 0)
                                continue;
                        mask[chan][sb] = BM(sbc->bits[chan][sb]);
                        levels[chan][sb] = mask[chan][sb] *
                            (1LL << (15 - sbc->scalefactor[chan][sb]));
                        delta[chan][sb] =
                            (1LL << (sbc->scalefactor[chan][sb] + 16));
                }
        }

        numsamples = 0;
        for (block = 0; block < sbc->blocks; block++) {
                for (chan = 0; chan < sbc->channels; chan++) {
                        for (sb = 0; sb < sbc->bands; sb++) {
                                if (sbc->bits[chan][sb] == 0)
                                        continue;
                                audioout = (levels[chan][sb] *
                                    (delta[chan][sb] + sbc->samples[block][chan][sb]));
                                audioout /= (1LL << 32);

                                audioout = roundf(audioout);

                                /* range check */
                                if (audioout > mask[chan][sb])
                                        audioout = mask[chan][sb];

                                sbc->output[numsamples++] = audioout;
                        }
                }
        }
        return (numsamples);
}

static void
sbc_decode(struct bt_config *cfg)
{
        struct sbc_encode *sbc = cfg->handle.sbc_enc;
        float delta[2][8];
        float levels[2][8];
        float audioout;
        float *X;
        float *V;
        float left[160];
        float right[160];
        float U[160];
        float W[160];
        float S[8];
        int position;
        int block;
        int chan;
        int sb;
        int i;
        int k;

        for (chan = 0; chan < sbc->channels; chan++) {
                for (sb = 0; sb < sbc->bands; sb++) {
                        levels[chan][sb] = (1 << sbc->bits[chan][sb]) - 1;
                        delta[chan][sb] = (1 << sbc->scalefactor[chan][sb]);
                }
        }

        i = 0;
        for (block = 0; block < sbc->blocks; block++) {
                for (chan = 0; chan < sbc->channels; chan++) {
                        for (sb = 0; sb < sbc->bands; sb++) {
                                if (sbc->bits[chan][sb] == 0) {
                                        audioout = 0;
                                } else {
                                        audioout =
                                          ((((sbc->output[i] * 2.0f) + 1.0f) * delta[chan][sb]) /
                                          levels[chan][sb]) - delta[chan][sb];
                                }
                                sbc->output[i++] = audioout;
                        }
                }
        }

        if (cfg->chmode == MODE_JOINT) {
                i = 0;
                while (i < (sbc->blocks * sbc->bands * sbc->channels)) {
                        for (sb = 0; sb < sbc->bands; sb++) {
                                if (sbc->join & (1 << (sbc->bands - sb - 1))) {
                                        audioout = sbc->output[i];
                                        sbc->output[i] = (2.0f * sbc->output[i]) +
                                            (2.0f * sbc->output[i + sbc->bands]);
                                        sbc->output[i + sbc->bands] =
                                            (2.0f * audioout) -
                                            (2.0f * sbc->output[i + sbc->bands]);
                                        sbc->output[i] /= 2.0f;
                                        sbc->output[i + sbc->bands] /= 2.0f;
                                }
                                i++;
                        }
                        i += sbc->bands;
                }
        }
        position = 0;
        for (block = 0; block < sbc->blocks; block++) {
                for (chan = 0; chan < sbc->channels; chan++) {
                        /* select right or left channel */
                        if (chan == 0) {
                                X = left;
                                V = sbc->left;
                        } else {
                                X = right;
                                V = sbc->right;
                        }
                        for (i = 0; i < sbc->bands; i++)
                                S[i] = sbc->output[position++];

                        for (i = (sbc->bands * 20) - 1; i >= (sbc->bands * 2); i--)
                                V[i] = V[i - (sbc->bands * 2)];
                        for (k = 0; k < sbc->bands * 2; k++) {
                                float vk = 0;
                                for (i = 0; i < sbc->bands; i++) {
                                        if (sbc->bands == 8) {
                                                vk += cosdecdata8[i][k] * S[i];
                                        } else {
                                                vk += cosdecdata4[i][k] * S[i];
                                        }
                                }
                                V[k] = vk;
                        }
                        for (i = 0; i <= 4; i++) {
                                for (k = 0; k < sbc->bands; k++) {
                                        U[(i * sbc->bands * 2) + k] =
                                            V[(i * sbc->bands * 4) + k];
                                        U[(i * sbc->bands
                                            * 2) + sbc->bands + k] =
                                            V[(i * sbc->bands * 4) +
                                            (sbc->bands * 3) + k];
                                }
                        }
                        for (i = 0; i < sbc->bands * 10; i++) {
                                if (sbc->bands == 4) {
                                        W[i] = U[i] * (sbc_coeffs4[i] * -4.0f);
                                } else if (sbc->bands == 8) {
                                        W[i] = U[i] * (sbc_coeffs8[i] * -8.0f);
                                } else {
                                        W[i] = 0;
                                }
                        }

                        for (k = 0; k < sbc->bands; k++) {
                                unsigned int offset = k + (block * sbc->bands);

                                X[offset] = 0;
                                for (i = 0; i < 10; i++) {
                                        X[offset] += W[k + (i * sbc->bands)];
                                }

                                if (X[offset] > 32767.0)
                                        X[offset] = 32767.0;
                                else if (X[offset] < -32767.0)
                                        X[offset] = -32767.0;
                        }
                }
        }

        for (i = 0, k = 0; k != (sbc->blocks * sbc->bands); k++) {
                sbc->music_data[i++] = left[k];
                if (sbc->channels == 2)
                        sbc->music_data[i++] = right[k];
        }
}

size_t
sbc_encode_frame(struct bt_config *cfg)
{
        struct sbc_encode *sbc = cfg->handle.sbc_enc;
        uint8_t config;
        uint8_t block;
        uint8_t chan;
        uint8_t sb;
        uint8_t j;
        uint8_t i;

        config = (cfg->freq << 6) | (cfg->blocks << 4) |
            (cfg->chmode << 2) | (cfg->allocm << 1) | cfg->bands;

        sbc_encode(cfg);

        /* set initial CRC */
        sbc->crc = 0x5e;

        /* reset data position and size */
        sbc->bitoffset = 0;
        sbc->maxoffset = sizeof(sbc->data) * 8;

        sbc_store_bits_crc(sbc, 8, SYNCWORD);
        sbc_store_bits_crc(sbc, 8, config);
        sbc_store_bits_crc(sbc, 8, cfg->bitpool);

        /* skip 8-bit CRC */
        sbc->bitoffset += 8;

        if (cfg->chmode == MODE_JOINT) {
                if (sbc->bands == 8)
                        sbc_store_bits_crc(sbc, 8, sbc->join);
                else if (sbc->bands == 4)
                        sbc_store_bits_crc(sbc, 4, sbc->join);
        }
        for (i = 0; i < sbc->channels; i++) {
                for (j = 0; j < sbc->bands; j++)
                        sbc_store_bits_crc(sbc, 4, sbc->scalefactor[i][j]);
        }

        /* store 8-bit CRC */
        sbc->data[3] = (sbc->crc & 0xFF);

        i = 0;
        for (block = 0; block < sbc->blocks; block++) {
                for (chan = 0; chan < sbc->channels; chan++) {
                        for (sb = 0; sb < sbc->bands; sb++) {
                                if (sbc->bits[chan][sb] == 0)
                                        continue;

                                sbc_store_bits_crc(sbc, sbc->bits[chan][sb], sbc->output[i++]);
                        }
                }
        }
        return ((sbc->bitoffset + 7) / 8);
}

static uint32_t
sbc_load_bits_crc(struct sbc_encode *sbc, uint32_t numbits)
{
        uint32_t off = sbc->bitoffset;
        uint32_t value = 0;

        while (numbits-- && off != sbc->maxoffset) {
                if (sbc->rem_data_ptr[off / 8] & (1 << ((7 - off) & 7))) {
                        value |= (1 << numbits);
                        sbc->crc ^= 0x80;
                }
                sbc->crc *= 2;
                if (sbc->crc & 0x100)
                        sbc->crc ^= 0x11d;      /* CRC-8 polynomial */

                off++;
        }
        sbc->bitoffset = off;
        return (value);
}

size_t
sbc_decode_frame(struct bt_config *cfg, int bits)
{
        struct sbc_encode *sbc = cfg->handle.sbc_enc;
        uint8_t config;
        uint8_t block;
        uint8_t chan;
        uint8_t sb;
        uint8_t j;
        uint8_t i;

        sbc->rem_off = 0;
        sbc->rem_len = 0;

        config = (cfg->freq << 6) | (cfg->blocks << 4) |
            (cfg->chmode << 2) | (cfg->allocm << 1) | cfg->bands;

        /* set initial CRC */
        sbc->crc = 0x5e;

        /* reset data position and size */
        sbc->bitoffset = 0;
        sbc->maxoffset = bits;

        /* verify SBC header */
        if (sbc->maxoffset < (8 * 4))
                return (0);
        if (sbc_load_bits_crc(sbc, 8) != SYNCWORD)
                return (0);
        if (sbc_load_bits_crc(sbc, 8) != config)
                return (0);
        cfg->bitpool = sbc_load_bits_crc(sbc, 8);

        (void)sbc_load_bits_crc(sbc, 8);/* CRC */

        if (cfg->chmode == MODE_JOINT) {
                if (sbc->bands == 8)
                        sbc->join = sbc_load_bits_crc(sbc, 8);
                else if (sbc->bands == 4)
                        sbc->join = sbc_load_bits_crc(sbc, 4);
                else
                        sbc->join = 0;
        } else {
                sbc->join = 0;
        }

        for (i = 0; i < sbc->channels; i++) {
                for (j = 0; j < sbc->bands; j++)
                        sbc->scalefactor[i][j] = sbc_load_bits_crc(sbc, 4);
        }

        calc_bitneed(cfg);

        i = 0;
        for (block = 0; block < sbc->blocks; block++) {
                for (chan = 0; chan < sbc->channels; chan++) {
                        for (sb = 0; sb < sbc->bands; sb++) {
                                if (sbc->bits[chan][sb] == 0) {
                                        i++;
                                        continue;
                                }
                                sbc->output[i++] =
                                    sbc_load_bits_crc(sbc, sbc->bits[chan][sb]);
                        }
                }
        }

        sbc_decode(cfg);

        sbc->rem_off = 0;
        sbc->rem_len = sbc->blocks * sbc->channels * sbc->bands;

        return ((sbc->bitoffset + 7) / 8);
}