/*
    FreeBASIC Sound Library (sfxlib)
    --------------------------------

    File: sfx_buffer.c

    Purpose:

        Implement the intermediate audio buffer used by the sound
        subsystem.

        The buffer stores the mixed audio output generated by the
        software mixer before it is consumed by platform drivers.

    Responsibilities:

        • buffer allocation
        • buffer clearing
        • buffer read/write access
        • buffer size management

    This file intentionally does NOT contain:

        • audio synthesis
        • mixer algorithms
        • driver playback logic
        • command parsing

    Architectural overview:

        synthesis → mixer → intermediate buffer → driver → OS audio
*/

#include <limits.h>
#include <stdlib.h>
#include <string.h>

#include "fb_sfx.h"
#include "fb_sfx_internal.h"
#include "fb_sfx_buffer.h"


/* ------------------------------------------------------------------------- */
/* Buffer size helpers                                                       */
/* ------------------------------------------------------------------------- */

/*
    Buffer sizes are exposed as int sample counts elsewhere in sfxlib.
    Keep the overflow checks here so every buffer path uses the same limits.
*/

static int fb_sfxBufferSampleCount(int frames, int channels, int *samples)
{
    if (!samples)
        return -1;

    *samples = 0;

    if (frames <= 0 || channels <= 0)
        return -1;

    if (frames > INT_MAX / channels)
        return -1;

    *samples = frames * channels;
    return 0;
}


static int fb_sfxBufferByteSize(int frames, int channels, size_t *size)
{
    int samples;

    if (!size)
        return -1;

    *size = 0;

    if (fb_sfxBufferSampleCount(frames, channels, &samples) != 0)
        return -1;

    if ((size_t)samples > ((size_t)-1) / sizeof(float))
        return -1;

    *size = (size_t)samples * sizeof(float);
    return 0;
}


/* ------------------------------------------------------------------------- */
/* Buffer initialization                                                     */
/* ------------------------------------------------------------------------- */

/*
    fb_sfxBufferInit()

    Allocate the main intermediate audio buffer used by the mixer.
*/

int fb_sfxBufferInit(int frames)
{
    size_t size;

    if (!__fb_sfx)
        return -1;

    if (fb_sfxBufferByteSize(frames, __fb_sfx->output_channels, &size) != 0)
        return -1;

    __fb_sfx->mixbuffer = (float*)malloc(size);

    if (!__fb_sfx->mixbuffer)
        return -1;

    memset(__fb_sfx->mixbuffer, 0, size);

    __fb_sfx->buffer_frames = frames;

    SFX_DEBUG("sfx_buffer: allocated %d frames (%llu bytes)",
              frames, (unsigned long long)size);

    return 0;
}


/* ------------------------------------------------------------------------- */
/* Buffer shutdown                                                           */
/* ------------------------------------------------------------------------- */

/*
    fb_sfxBufferShutdown()

    Release the intermediate audio buffer.
*/

void fb_sfxBufferShutdown(void)
{
    if (!__fb_sfx)
        return;

    if (__fb_sfx->mixbuffer)
    {
        free(__fb_sfx->mixbuffer);
        __fb_sfx->mixbuffer = NULL;
    }

    __fb_sfx->buffer_frames = 0;

    SFX_DEBUG("sfx_buffer: buffer released");
}


/* ------------------------------------------------------------------------- */
/* Buffer clear                                                              */
/* ------------------------------------------------------------------------- */

/*
    fb_sfxBufferClear()

    Reset the buffer to silence.
*/

void fb_sfxBufferClear(void)
{
    size_t size;

    if (!__fb_sfx)
        return;

    if (!__fb_sfx->mixbuffer)
        return;

    if (fb_sfxBufferByteSize(__fb_sfx->buffer_frames,
                             __fb_sfx->output_channels,
                             &size) != 0)
        return;

    memset(__fb_sfx->mixbuffer, 0, size);
}


/* ------------------------------------------------------------------------- */
/* Buffer access                                                             */
/* ------------------------------------------------------------------------- */

/*
    fb_sfxBufferPtr()

    Return pointer to the intermediate mix buffer.

    Drivers use this pointer to retrieve generated audio.
*/

float *fb_sfxBufferPtr(void)
{
    if (!__fb_sfx)
        return NULL;

    return __fb_sfx->mixbuffer;
}


/*
    fb_sfxBufferFrames()

    Return the number of frames contained in the buffer.
*/

int fb_sfxBufferFrames(void)
{
    if (!__fb_sfx)
        return 0;

    return __fb_sfx->buffer_frames;
}


/*
    fb_sfxBufferSamples()

    Return the number of samples in the buffer.
*/

int fb_sfxBufferSamples(void)
{
    int samples;

    if (!__fb_sfx)
        return 0;

    if (fb_sfxBufferSampleCount(__fb_sfx->buffer_frames,
                                __fb_sfx->output_channels,
                                &samples) != 0)
        return 0;

    return samples;
}


/* ------------------------------------------------------------------------- */
/* Buffer write helper                                                       */
/* ------------------------------------------------------------------------- */

/*
    fb_sfxBufferWrite()

    Write a sample to the buffer.

    This helper is primarily intended for testing or for
    alternative synthesis paths.
*/

void fb_sfxBufferWrite(int index, float value)
{
    int samples;

    if (!__fb_sfx)
        return;

    if (!__fb_sfx->mixbuffer)
        return;

    if (fb_sfxBufferSampleCount(__fb_sfx->buffer_frames,
                                __fb_sfx->output_channels,
                                &samples) != 0)
        return;

    if (index < 0 || index >= samples)
        return;

    __fb_sfx->mixbuffer[index] = value;
}


/* ------------------------------------------------------------------------- */
/* Buffer read helper                                                        */
/* ------------------------------------------------------------------------- */

/*
    fb_sfxBufferRead()

    Read a sample from the intermediate buffer.

    This function is useful for automated testing of the
    synthesis pipeline.
*/

float fb_sfxBufferRead(int index)
{
    int samples;

    if (!__fb_sfx)
        return 0.0f;

    if (!__fb_sfx->mixbuffer)
        return 0.0f;

    if (fb_sfxBufferSampleCount(__fb_sfx->buffer_frames,
                                __fb_sfx->output_channels,
                                &samples) != 0)
        return 0.0f;

    if (index < 0 || index >= samples)
        return 0.0f;

    return __fb_sfx->mixbuffer[index];
}


void fb_sfxMixBufferInit(void)
{
    if (!__fb_sfx)
        return;

    (void)fb_sfxBufferInit(__fb_sfx->buffer_size);
}


void fb_sfxMixBufferShutdown(void)
{
    fb_sfxBufferShutdown();
}


int fb_sfxMixBufferWrite(const float *samples, int frames)
{
    int channels;
    int total;
    int i;

    if (!samples || frames <= 0)
        return 0;

    channels = (__fb_sfx ? __fb_sfx->output_channels : FB_SFX_DEFAULT_CHANNELS);
    if (fb_sfxBufferSampleCount(frames, channels, &total) != 0)
        return 0;

    for (i = 0; i < total; ++i)
        fb_sfxBufferWrite(i, samples[i]);

    return frames;
}


int fb_sfxMixBufferRead(float *samples, int frames)
{
    int channels;
    int total;
    int i;

    if (!samples || frames <= 0)
        return 0;

    channels = (__fb_sfx ? __fb_sfx->output_channels : FB_SFX_DEFAULT_CHANNELS);
    if (fb_sfxBufferSampleCount(frames, channels, &total) != 0)
        return 0;

    for (i = 0; i < total; ++i)
        samples[i] = fb_sfxBufferRead(i);

    return frames;
}


/* end of sfx_buffer.c */
