/*
MIT License

Copyright (c) 2020-2022 Rupert Carmichael

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

#include <stdint.h>
#include <stdlib.h>

#include <speex/speex_resampler.h>

#include "vecx_mixer.h"

#define SAMPLERATE_PSG 187500 // Approximate PSG sample rate (Hz)
#define SIZE_BUF 3800

static int16_t *abuf = NULL; // Buffer to output resampled data into
static int16_t *psgbuf = NULL; // PSG buffer
static int16_t *dacbuf = NULL; // Buffer for DAC audio
static size_t dacsamps = 0; // Sample counter for DAC audio buffer

static size_t samplerate = 48000; // Default sample rate is 48000Hz
static uint8_t framerate = 50; // Default to 60 for NTSC
static uint8_t rsq = 2; // Default resampler quality is 2

// Speex
static SpeexResamplerState *resampler = NULL;
static int err;

static ay38910_t *psg = NULL;

// Callback to notify the fronted that N samples are ready
static void (*vecx_mixer_cb)(size_t);

// Set the output sample rate
void vecx_mixer_set_rate(size_t rate) {
    switch (rate) {
        case 44100: case 48000: case 96000: case 192000:
            samplerate = rate;
            break;
        default:
            break;
    }
}

void vecx_mixer_set_psg(ay38910_t *ptr) {
    psg = ptr;
}

// Set the resampler quality
void vecx_mixer_set_rsqual(uint8_t qual) {
    if (qual <= 10)
        rsq = qual;
}

// Set the pointer to the output audio buffer
void vecx_mixer_set_buffer(int16_t *ptr) {
    abuf = ptr;
}

// Set the callback that notifies the frontend that N audio samples are ready
void vecx_mixer_set_callback(void (*cb)(size_t)) {
    vecx_mixer_cb = cb;
}

// Deinitialize the resampler
void vecx_mixer_deinit(void) {
    if (resampler) {
        speex_resampler_destroy(resampler);
        resampler = NULL;
    }

    if (psgbuf)
        free(psgbuf);

    if (dacbuf)
        free(dacbuf);
}

// Bring up the Speex resampler and PSG
void vecx_mixer_init(void) {
    resampler = speex_resampler_init(1, SAMPLERATE_PSG, samplerate, rsq, &err);
    psgbuf = (int16_t*)calloc(1, SIZE_BUF * sizeof(int16_t));
    dacbuf = (int16_t*)calloc(1, SIZE_BUF * sizeof(int16_t));
    psg->buf = psgbuf;
}

// Write a sample to the DAC buffer
void vecx_mixer_wr_dac(int16_t samp) {
    dacbuf[dacsamps++] = samp;
}

// Resample raw audio and execute the callback
void vecx_mixer_resamp(size_t in_psg) {
    dacsamps = 0; // Reset the DAC buffer
    psg->bufpos = 0; // Reset the PSG buffer

    spx_uint32_t in_len = in_psg;
    spx_uint32_t outsamps = samplerate / framerate;

    // Mix DAC and PSG
    for (size_t i = 0; i < in_psg; ++i)
        psgbuf[i] += dacbuf[i];

    err = speex_resampler_process_int(resampler, 0, (spx_int16_t*)psgbuf,
        &in_len, (spx_int16_t*)abuf, &outsamps);

    vecx_mixer_cb(outsamps);
}
