/***************************************************************************
 *             __________               __   ___.
 *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
 *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
 *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
 *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
 *                     \/            \/     \/    \/            \/
 *
 * Pacbox - a Pacman Emulator for Rockbox
 *
 * Based on PIE - Pacman Instructional Emulator
 *
 * Namco custom waveform sound generator 3 (Pacman hardware)
 *
 * Copyright (c) 2003,2004 Alessandro Scotti
 * http://www.ascotti.org/
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
 * KIND, either express or implied.
 *
 ****************************************************************************/
#include "plugin.h"
#include "wsg3.h"

struct wsg3 wsg3;

static bool wsg3_get_voice(struct wsg3_voice *voice, int index)
{
    int base = 5*index;
    const unsigned char *regs = wsg3.sound_regs;
    unsigned x;
    
    x = regs[0x15 + base] & 0x0F;

    if (x == 0)
        return false;

    voice->volume = x;

    x =            (regs[0x14 + base] & 0x0F);
    x = (x << 4) | (regs[0x13 + base] & 0x0F);
    x = (x << 4) | (regs[0x12 + base] & 0x0F);
    x = (x << 4) | (regs[0x11 + base] & 0x0F);
    x = (x << 4);

    if (index == 0)
    {
        /* The first voice has an extra 4-bit of data */
        x |= regs[0x10 + base] & 0x0F; 
    }

    if (x == 0)
        return false;

    voice->frequency = x;

    voice->waveform = regs[0x05 + base] & 0x07;

    return true;
}

void wsg3_play_sound(int16_t * buf, int len)
{
    struct wsg3_voice voice;

    if (wsg3_get_voice(&voice, 0))
    {
        unsigned offset = wsg3.wave_offset[0];
        unsigned step = voice.frequency * wsg3.resample_step;
        int16_t * wave_data = wsg3.sound_wave_data + 32 * voice.waveform;
        int volume = voice.volume;
        int i;

        for (i = 0; i < len; i++)
        {
            /* Should be shifted right by 15, but we must also get rid
             * of the 10 bits used for decimals */
            buf[i] = (int)wave_data[(offset >> 25) & 0x1F] * volume;
            offset += step;
        }

        wsg3.wave_offset[0] = offset;
    }

    if (wsg3_get_voice(&voice, 1))
    {
        unsigned offset = wsg3.wave_offset[1];
        unsigned step = voice.frequency * wsg3.resample_step;
        int16_t * wave_data = wsg3.sound_wave_data + 32 * voice.waveform;
        int volume = voice.volume;
        int i;

        for (i = 0; i < len; i++)
        {
            /* Should be shifted right by 15, but we must also get rid
             * of the 10 bits used for decimals */
            buf[i] += (int)wave_data[(offset >> 25) & 0x1F] * volume;
            offset += step;
        }

        wsg3.wave_offset[1] = offset;
    }

    if (wsg3_get_voice(&voice, 2))
    {
        unsigned offset = wsg3.wave_offset[2];
        unsigned step = voice.frequency * wsg3.resample_step;
        int16_t * wave_data = wsg3.sound_wave_data + 32 * voice.waveform;
        int volume = voice.volume;
        int i;

        for (i = 0; i < len; i++)
        {
            /* Should be shifted right by 15, but we must also get rid
             * of the 10 bits used for decimals */
            buf[i] += (int)wave_data[(offset >> 25) & 0x1F] * volume;
            offset += step;
        }

        wsg3.wave_offset[2] = offset;
    }
}

void wsg3_set_sampling_rate(unsigned sampling_rate)
{
    wsg3.sampling_rate = sampling_rate;
    wsg3.resample_step = (wsg3.master_clock << 10) / sampling_rate;
}

void wsg3_set_sound_prom( const unsigned char * prom )
{
    int i;

    memcpy(wsg3.sound_prom, prom, 32*8);

    /* Copy wave data and convert 4-bit unsigned -> 16-bit signed,
     * prenormalized */
    for (i = 0; i < 32*8; i++)
        wsg3.sound_wave_data[i] = ((int16_t)wsg3.sound_prom[i] - 8) * 85;
}

void wsg3_init(unsigned master_clock)
{
    memset(&wsg3, 0, sizeof (struct wsg3));
    wsg3.master_clock = master_clock;
}
