// sb-send: Sonobus audio sender

// Functions for managing the Opus encoder/decoder.
// The Opus developers are a group of awesome people.
// We stand on the shoulders of giants!

#include "sb-send.h"

int init_opus(AppState *state) {
    SB_ASSERT(state->channels > 0 && state->channels <= 255);
    SB_ASSERT(state->bitrate >= 500 && state->bitrate <= 300000);
    int error;
    int channels = state->channels;
    int streams, coupled_streams;
    unsigned char mapping[255];
    compute_opus_mapping(channels, state->joint_stereo, &streams, &coupled_streams, mapping);

    int app;
    if (state->voice) {
        app = OPUS_APPLICATION_VOIP;
    } else {
        app = OPUS_APPLICATION_AUDIO;
    }
    state->encoder = opus_multistream_encoder_create(
        SAMPLE_RATE, channels, streams, coupled_streams,
        mapping, app, &error);

    if (error != OPUS_OK || !state->encoder) {
        fprintf(stderr, "Opus encoder failed: %s\n", opus_strerror(error));
        return -1;
    }

    opus_multistream_encoder_ctl(state->encoder, OPUS_SET_BITRATE(state->bitrate * channels));
    opus_multistream_encoder_ctl(state->encoder, OPUS_SET_COMPLEXITY(state->complexity));
    opus_multistream_encoder_ctl(state->encoder, OPUS_SET_VBR(1));
    opus_multistream_encoder_ctl(state->encoder, OPUS_SET_VBR_CONSTRAINT(0));
    opus_multistream_encoder_ctl(state->encoder, OPUS_SET_DTX(0));
    opus_multistream_encoder_ctl(state->encoder, OPUS_SET_SIGNAL(OPUS_AUTO));

    const char *mode;
    if (state->voice) {
        mode = "voice";
    } else {
        mode = "music";
    }
    fprintf(stderr, "Opus: %d ch, %d bps/ch, complexity %d, %s, VBR, DTX off\n",
           channels, state->bitrate, state->complexity, mode);
    return 0;
}

int init_opus_decoder_ex(OpusMSDecoder **decoder, int rate, int channels,
                        int streams, int coupled_streams,
                        const unsigned char *mapping) {
    SB_ASSERT(decoder != NULL);
    SB_ASSERT(channels > 0 && channels <= 255);
    int error;

    *decoder = opus_multistream_decoder_create(rate, channels, streams,
                                               coupled_streams, mapping, &error);
    if (error != OPUS_OK || !*decoder) {
        fprintf(stderr, "Opus decoder create failed: %s\n", opus_strerror(error));
        return -1;
    }

    return 0;
}

void compute_opus_mapping(int channels, bool joint_stereo, int *streams,
                          int *coupled_streams, unsigned char *mapping) {
    SB_ASSERT(channels == 1 || channels == 2);
    if (channels == 2 && joint_stereo) {
        *streams = 1; *coupled_streams = 1;
    } else {
        *streams = channels; *coupled_streams = 0;
    }
    for (int i = 0; i < channels; i++) {
        mapping[i] = i;
    }
}

int init_opus_decoder(AppState *state) {
    int streams, coupled_streams;
    unsigned char mapping[255];
    compute_opus_mapping(state->channels, state->joint_stereo, &streams, &coupled_streams, mapping);
    return init_opus_decoder_ex(&state->decoder, SAMPLE_RATE, state->channels,
                                streams, coupled_streams, mapping);
}

// Decode Opus packet to s16le samples.
// Returns number of samples per channel, or negative on error.
// pcm buffer must be at least OPUS_MAX_SAMPLES * 2 floats.
// s16 buffer must be at least OPUS_MAX_SAMPLES * 2 int16_t.
// Decoder must be configured for the correct channel count at creation time.
// project limitation: only supports mono/stereo (1-2 channels).
int opus_decode_to_s16(OpusMSDecoder *decoder, const unsigned char *data, int data_len,
                       int16_t *s16, float *pcm, int channels) {
    if (channels < 1 || channels > 2) { return -1; }
    if (data_len < 1) { return -1; }

    int decoded = opus_multistream_decode_float(decoder, data, data_len,
                                                pcm, OPUS_MAX_SAMPLES, 0);
    if (decoded <= 0) { return decoded; }

    int total = decoded * channels;
    for (int i = 0; i < total; i++) {
        float v = pcm[i];
        if (v > 1.0f) { v = 1.0f; }
        else if (v < -1.0f) { v = -1.0f; }
        s16[i] = (int16_t)(v * 32767.0f);
    }
    return decoded;
}
// EOF
