/*
 * ao_dart.c - libao2 DART Audio Output Driver for MPlayer
 *
 * Copyleft 2007 by KO Myung-Hun (komh@chollian.net)
 *
 */

#define INCL_DOS
#include <os2.h>

#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>

#include "config.h"
#include "libaf/af_format.h"
#include "audio_out.h"
#include "audio_out_internal.h"
#include "mp_msg.h"

#include "dart.h"

static ao_info_t info =
{
    "DART audio output",
    "dart",
    "KO Myung-Hun <komh@chollian.net>",
    ""
};

LIBAO_EXTERN( dart )

#define OUTBURST_SAMPLES	512
#define DART_SAMPLES		( OUTBURST_SAMPLES << 2 )
#define MPLAYER_SAMPLES		( OUTBURST_SAMPLES << 4 )

static uint8_t *m_audioBuf = NULL;
static volatile int m_nBufSize = 0;
static volatile int m_nBufLen = 0;
static volatile int m_iBufPos = 0;

static ULONG APIENTRY dart_audio_callback( PVOID pBuffer, ULONG ulSize )
{
    int nCopySize;

    nCopySize = ulSize < m_nBufLen ? ulSize : m_nBufLen;

    if( m_iBufPos + nCopySize > m_nBufSize )
    {
        int len = m_nBufSize - m_iBufPos;

        memcpy( pBuffer, m_audioBuf + m_iBufPos, len );
        memcpy(( uint8_t * )pBuffer + len, m_audioBuf, nCopySize - len );
    }
    else
        memcpy( pBuffer, m_audioBuf + m_iBufPos, nCopySize );

    m_iBufPos = ( m_iBufPos + nCopySize ) % m_nBufSize;
    m_nBufLen -= nCopySize;

    memset(( uint8_t * )pBuffer + nCopySize, DART.bSilence, ulSize - nCopySize );

    return ulSize;
}

// to set/get/query special features/parameters
static int control( int cmd, void *arg )
{
    switch (cmd)
    {
        case AOCONTROL_GET_VOLUME :
        {
            ao_control_vol_t *vol = ( ao_control_vol_t * )arg;

            vol->left = vol->right = LOUSHORT( dartGetVolume());

            return CONTROL_OK;
        }

        case AOCONTROL_SET_VOLUME:
        {
            int mid;
            ao_control_vol_t *vol = ( ao_control_vol_t * )arg;

            mid = ( vol->left + vol->right ) / 2;
            dartSetVolume( MCI_SET_AUDIO_ALL, mid );

            return CONTROL_OK;
        }
    }

    return CONTROL_UNKNOWN;
}

// open & setup audio device
// return: 1=success 0=fail
static int init( int rate, int channels, int format, int flags )
{
    int n;

    switch( format )
    {
        case AF_FORMAT_S16_LE:
        case AF_FORMAT_S8:
            break;

        default :
            format = AF_FORMAT_S16_LE;
            mp_msg(MSGT_AO, MSGL_V,"DART: format %s not supported defaulting to Signed 16-bit Little-Endian\n", af_fmt2str_short( format ));
            break;
    }

    n = ( af_fmt2bits( format ) >> 3 ) * channels;

    m_nBufSize = n * MPLAYER_SAMPLES;

    if( dartInit( 0, af_fmt2bits( format ), rate, MCI_WAVE_FORMAT_PCM, channels,
                  2, n * DART_SAMPLES, TRUE, dart_audio_callback ))
        return 0;

    ao_data.channels = channels;
    ao_data.samplerate = rate;
    ao_data.format = format;
    ao_data.bps = channels * rate * ( af_fmt2bits( format ) >> 3 );
    ao_data.outburst = n * OUTBURST_SAMPLES;
    ao_data.buffersize = m_nBufSize;

    m_audioBuf = malloc( m_nBufSize );

    m_nBufLen = 0;
    m_iBufPos = 0;

    dartPlay();

    return 1;
}

// close audio device
static void uninit( int immed )
{
    if( !immed )
    {
        while( m_nBufLen )
            DosSleep( 1 );
    }

    dartClose();

    free( m_audioBuf );
}

// stop playing and empty buffers (for seeking/pause)
static void reset( void )
{
    dartPause();

    // empty buffers
    m_nBufLen = 0;

    dartResume();
}

// stop playing, keep buffers (for pause)
static void audio_pause( void )
{
    dartPause();
}

// resume playing, after audio_pause()
static void audio_resume( void )
{
    dartResume();
}

// return: how many bytes can be played without blocking
static int get_space( void )
{
    return ( m_nBufSize - m_nBufLen );
}

// plays 'len' bytes of 'data'
// it should round it down to outburst*n
// return: number of bytes played
static int play( void *data, int len, int flags)
{
    int start_pos, end_pos;

    if( !( flags & AOPLAY_FINAL_CHUNK ))
        len = ( len / ao_data.outburst ) * ao_data.outburst;

    if( len == 0 )
        return 0;

    start_pos = ( m_iBufPos + m_nBufLen ) % m_nBufSize;

    end_pos = ( start_pos + len ) % m_nBufSize;
    if( end_pos <= start_pos )
    {
        int len1 = m_nBufSize - start_pos;

        if( end_pos > m_iBufPos )
            end_pos = m_iBufPos;

        memcpy( m_audioBuf + start_pos, data, len1 );
        memcpy( m_audioBuf, ( uint8_t * )data + len1, end_pos );

        len = len1 + end_pos;
    }
    else
        memcpy( m_audioBuf + start_pos, data, len );

    m_nBufLen += len;

    return len;
}

// return: delay in seconds between first and last sample in buffer
static float get_delay( void )
{
    return ( float )m_nBufLen / ( float )ao_data.bps;
}
