/*****************************************************************************\
     Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
                This file is licensed under the Snes9x License.
   For further information, consult the LICENSE file in the root directory.
\*****************************************************************************/

#include "gtk_sound_driver_sdl.h"
#include "gtk_s9x.h"
#include "apu/apu.h"
#include "snes9x.h"

static void sdl_audio_callback(void *userdata, Uint8 *stream, int len)
{
    ((S9xSDLSoundDriver *)userdata)->mix((unsigned char *)stream, len);
}

static void c_samples_available(void *data)
{
    ((S9xSDLSoundDriver *)data)->samples_available();
}

void S9xSDLSoundDriver::samples_available()
{
    int snes_samples_available = S9xGetSampleCount();
    S9xMixSamples((uint8 *)temp, snes_samples_available);

    if (Settings.SoundSync && !Settings.TurboMode && !Settings.Mute)
    {
        mutex.lock();
        int samples = buffer.space_empty();
        mutex.unlock();

        while (samples < snes_samples_available)
        {
            usleep(100);
            mutex.lock();
            samples = buffer.space_empty();
            mutex.unlock();
        }
    }

    mutex.lock();
    buffer.push(temp, snes_samples_available);
    mutex.unlock();
}

void S9xSDLSoundDriver::mix(unsigned char *output, int bytes)
{
    mutex.lock();
    if (buffer.avail() >= bytes >> 1)
        buffer.read((int16_t *)output, bytes >> 1);
    mutex.unlock();
}

S9xSDLSoundDriver::S9xSDLSoundDriver()
{
}

void S9xSDLSoundDriver::init()
{
    SDL_InitSubSystem(SDL_INIT_AUDIO);
    stop();
}

void S9xSDLSoundDriver::terminate()
{
    stop();
    SDL_CloseAudio();
    SDL_QuitSubSystem(SDL_INIT_AUDIO);
}

void S9xSDLSoundDriver::start()
{
    if (!gui_config->mute_sound)
    {
        SDL_PauseAudio(0);
    }
}

void S9xSDLSoundDriver::stop()
{
    SDL_PauseAudio(1);
}

bool S9xSDLSoundDriver::open_device()
{
    audiospec = {};
    audiospec.freq = Settings.SoundPlaybackRate;
    audiospec.channels = 2;
    audiospec.format = AUDIO_S16SYS;
    audiospec.samples = (gui_config->sound_buffer_size * audiospec.freq / 1000) >> 2;
    audiospec.callback = sdl_audio_callback;
    audiospec.userdata = this;

    printf("SDL sound driver initializing...\n");
    printf("    --> (Frequency: %dhz, Latency: %dms)...",
           audiospec.freq,
           (audiospec.samples * 1000 / audiospec.freq));

    if (SDL_OpenAudio(&audiospec, NULL) < 0)
    {
        printf("Failed\n");
        return false;
    }

    printf("OK\n");

    buffer.resize(gui_config->sound_buffer_size * audiospec.freq / 500);
    buffer.time_ratio(1.0);

    S9xSetSamplesAvailableCallback(c_samples_available, this);

    return true;
}
