
#include "osd.h"

int seal_sample_rate =   44100;
int seal_sound_card  =   -1;

HAC hVoice[NUMVOICES];
LPAUDIOWAVE lpWave[NUMVOICES];
AUDIOINFO info;
AUDIOCAPS caps;
int c[MAX_STREAM_CHANNELS];
int nominal_sample_rate;
static int attenuation = 0;
static int master_volume = 256;


void osd_update_audio(void)
{
    if (seal_sample_rate == 0) return;
    AUpdateAudio();
}


/* attenuation in dB */
void osd_set_mastervolume(int _attenuation)
{
    float volume;

    attenuation = _attenuation;

    volume = 256.0; /* range is 0-256 */
    while (_attenuation++ < 0)
        volume /= 1.122018454;  /* = (10 ^ (1/20)) = 1dB */

    master_volume = volume;

    ASetAudioMixerValue(AUDIO_MIXER_MASTER_VOLUME,master_volume);
}


int msdos_init_sound(int *rate, int card)
{
    int i;

    seal_sample_rate = *rate;
    seal_sound_card  = card;

    if (AInitialize() != AUDIO_ERROR_NONE)
        return 1;

    /* Ask the user if no sound card was chosen */
    if (seal_sound_card == -1)
    {
        unsigned int k;

        printf("\n SELECT YOUR AUDIO DEVICE :\n\n"
               " AWE32/64 playback requires onboard DRAM,\n"
               " Sound Blaster playback is the most compatible & better for emulation\n\n");

        for (k = 0;k < AGetAudioNumDevs();k++)
        {
            if (AGetAudioDevCaps(k,&caps) == AUDIO_ERROR_NONE)
                printf("  %2d. %s\n",k,caps.szProductName);
        }
        printf("\n");

        if (k < 10)
        {
            i = getch();
            seal_sound_card = i - '0';
        }
        else
            scanf("%d",&seal_sound_card);
    }

    /* initialize SEAL audio library */
    if (seal_sound_card == 0)     /* silence */
    {
        /* update the Machine structure to show that sound is disabled */
        seal_sample_rate = 0;
        exit(0);
        return 0;
    }

    /* open audio device */
    /*                              info.nDeviceId = AUDIO_DEVICE_MAPPER;*/
    info.nDeviceId = seal_sound_card;
    /* always use 16 bit mixing if possible - better quality and same speed of 8 bit */
    info.wFormat = AUDIO_FORMAT_16BITS | AUDIO_FORMAT_STEREO | AUDIO_FORMAT_RAW_SAMPLE;

    info.nSampleRate = seal_sample_rate;
    if (AOpenAudio(&info) != AUDIO_ERROR_NONE)
    {
        return (1);
    }

    AGetAudioDevCaps(info.nDeviceId,&caps);
    printf("Using `%s' at %d-bit %s %u Hz\n",
			caps.szProductName,
			info.wFormat & AUDIO_FORMAT_16BITS ? 16 : 8,
			info.wFormat & AUDIO_FORMAT_STEREO ? "stereo" : "mono",
			info.nSampleRate);

    /* open and allocate voices, allocate waveforms */
    if (AOpenVoices(NUMVOICES) != AUDIO_ERROR_NONE)
    {
        printf("voices initialization failed\n");
        return 1;
    }

    for (i = 0; i < NUMVOICES; i++)
    {
        if (ACreateAudioVoice(&hVoice[i]) != AUDIO_ERROR_NONE)
        {
            printf("voice #%d creation failed\n",i);
            return 1;
        }

        ASetVoicePanning(hVoice[i],128);

        lpWave[i] = 0;
    }

    /* update the Machine structure to reflect the actual sample rate */
    *rate = seal_sample_rate = info.nSampleRate;

    {
        uclock_t a,b;
        LONG start,end;


        if ((lpWave[0] = (LPAUDIOWAVE)malloc(sizeof(AUDIOWAVE))) == 0)
            return 1;

        lpWave[0]->wFormat = AUDIO_FORMAT_8BITS | AUDIO_FORMAT_MONO;
        lpWave[0]->nSampleRate = seal_sample_rate;
        lpWave[0]->dwLength = 3*seal_sample_rate;
        lpWave[0]->dwLoopStart = 0;
        lpWave[0]->dwLoopEnd = 3*seal_sample_rate;
        if (ACreateAudioData(lpWave[0]) != AUDIO_ERROR_NONE)
        {
            free(lpWave[0]);
            lpWave[0] = 0;

            return 1;
        }

        memset(lpWave[0]->lpData,0,3*seal_sample_rate);
        /* upload the data to the audio DRAM local memory */
        AWriteAudioData(lpWave[0],0,3*seal_sample_rate);
        APrimeVoice(hVoice[0],lpWave[0]);
        ASetVoiceFrequency(hVoice[0],seal_sample_rate);
        ASetVoiceVolume(hVoice[0],0);
        AStartVoice(hVoice[0]);

        a = uclock();
        /* wait some time to let everything stabilize */
        do
        {
            osd_update_audio();
            b = uclock();
        } while (b-a < UCLOCKS_PER_SEC/10);

        a = uclock();
        AGetVoicePosition(hVoice[0],&start);
        do
        {
            osd_update_audio();
            b = uclock();
        } while (b-a < UCLOCKS_PER_SEC);
        AGetVoicePosition(hVoice[0],&end);

        nominal_sample_rate = seal_sample_rate;
        seal_sample_rate = end - start;

        AStopVoice(hVoice[0]);
        ADestroyAudioData(lpWave[0]);
        free(lpWave[0]);
        lpWave[0] = 0;
    }

    osd_set_mastervolume(0);    /* start at maximum volume */

    return 0;
}


void msdos_shutdown_sound(void)
{
    if (seal_sample_rate != 0)
    {
        int n;

        /* stop and release voices */
        for (n = 0; n < NUMVOICES; n++)
        {
            AStopVoice(hVoice[n]);
            ADestroyAudioVoice(hVoice[n]);
            if (lpWave[n])
            {
                ADestroyAudioData(lpWave[n]);
                free(lpWave[n]);
                lpWave[n] = 0;
            }
        }
        ACloseVoices();
        ACloseAudio();
    }
}


void playstreamedsample(int channel,signed char *data,int len,int freq,int volume,int pan,int bits)
{
    static int playing[NUMVOICES];
    static int c[NUMVOICES];

    /* backwards compatibility with old 0-255 volume range */
    if (volume > 100) volume = volume * 25 / 255;

    if (seal_sample_rate == 0 || channel >= NUMVOICES) return;

    if (!playing[channel])
    {
        if (lpWave[channel])
        {
            AStopVoice(hVoice[channel]);
            ADestroyAudioData(lpWave[channel]);
            free(lpWave[channel]);
            lpWave[channel] = 0;
        }

        if ((lpWave[channel] = (LPAUDIOWAVE)malloc(sizeof(AUDIOWAVE))) == 0)
            return;

        lpWave[channel]->wFormat = (bits == 8 ? AUDIO_FORMAT_8BITS : AUDIO_FORMAT_16BITS)
                | AUDIO_FORMAT_MONO | AUDIO_FORMAT_LOOP;
        lpWave[channel]->nSampleRate = nominal_sample_rate;
        lpWave[channel]->dwLength = 3*len;
        lpWave[channel]->dwLoopStart = 0;
        lpWave[channel]->dwLoopEnd = 3*len;
        if (ACreateAudioData(lpWave[channel]) != AUDIO_ERROR_NONE)
        {
            free(lpWave[channel]);
            lpWave[channel] = 0;

            return;
        }

        memset(lpWave[channel]->lpData,0,3*len);
        memcpy(lpWave[channel]->lpData,data,len);
        /* upload the data to the audio DRAM local memory */
        AWriteAudioData(lpWave[channel],0,3*len);
        APrimeVoice(hVoice[channel],lpWave[channel]);
    /* need to cast to double because freq*nominal_sample_rate can exceed the size of an int */
        ASetVoiceFrequency(hVoice[channel],(double)freq*nominal_sample_rate/seal_sample_rate);
        AStartVoice(hVoice[channel]);
        playing[channel] = 1;
        c[channel] = 1;
    }
    else
    {
        LONG pos;

        for(;;)
        {
                AGetVoicePosition(hVoice[channel],&pos);
                if (c[channel] == 0 && pos >= len) break;
                if (c[channel] == 1 && (pos < len || pos >= 2*len)) break;
                if (c[channel] == 2 && pos < 2*len) break;
                osd_update_audio();
        }

        memcpy(&lpWave[channel]->lpData[len * c[channel]],data,len);
        AWriteAudioData(lpWave[channel],len*c[channel],len);
        c[channel]++;
        if (c[channel] == 3) c[channel] = 0;
    }


    ASetVoiceVolume(hVoice[channel],volume * 64 / 100);
    ASetVoicePanning(hVoice[channel],(pan + 100) * 255 / 200);
}

void osd_play_streamed_sample_16(int channel,signed short *data,int len,int freq,int volume,int pan)
{
    playstreamedsample(channel,(signed char *)data,len,freq,volume,pan,16);
}

