/*
      PCM Stream TEST
*/

/*                 */
/* Include headers */
/*                 */
#include <shinobi.h>
#include <nindows.h>
#include "..\include\sndutls.h"

/* Channel information */
#define CHANNEL_L 0
#define CHANNEL_R 1

struct gdstream {
  Sint32    BlkNum;  /* Block number of SH Main RAM Ring buffer  */
  Sint32    page;    /* Current block of SH Main RAM Ring buffer */
  SDMEMBLK  memblk;  /* Memory block handle                      */
  GDFS      fd;      /* File handle                              */
  Uint32    Total;   /* Total samples                            */
  Sint8     *buf;    /* SH4 Ring Buffer                          */
  Sint32    sz;      /* Single Block Size                        */
  SDPSTM    hn;      /* PCM Stream handle                        */
  Sint32    stat;    /* PCM Play Status                          */
  Sint32    bit;     /* Sampling bit                             */
  Sint32    oct;     /* Oct/FNS Value                            */
  Sint32    ringsz;  /* PCM Stream ring buffer size (AICA)       */
  Sint32    port;    /* Stream Buffer port number                */
  Sint8     *addr;   /* Transfer address                         */
  Sint32    fsz;     /* File size                                */
  Sint8     *silent; /* Silent data for dummy                    */
  Sint32    csz;     /* Current transfer size                    */
  Sint32    ssz;     /* Sound Transfer Size                      */
};

/*                   */
/* Declare Prototype */
/*                   */
void main( void );

/*                 */
/* Function Define */
/*                 */
Sint32 pcmInit( const char *name, struct gdstream *hn, Sint32 bit ) {
  Sint32 sz;

  if ( ( hn->fd = gdFsOpen( name, NULL ) ) == NULL ) {
    return -1;
  }
  if ( ( bit != 4 ) && ( bit != 8 ) && ( bit != 16 ) ) {
    return -2;
  }
  hn->bit = bit;

  if ( ( hn->silent = syMalloc( 0x1000 ) ) == NULL ) {
    return -5;
  }
  memset( hn->silent, 0, 0x1000 );
  
  gdFsGetFileSize( hn->fd, &sz );
  hn->Total = sz * 8 / bit;
  hn->fsz   = sz;
  hn->csz   = 0;
  hn->ssz   = 0;

  sdMemBlkCreate( &hn->memblk );
  hn->page = 0;
  hn->stat = 1;
  return 0;
}

Sint32 pcmSetPara( struct gdstream *hn, Sint32 ring, Sint32 size ) {
  Uint32 addr;

  hn->port = ring;
  /* Get AICA Ring buffer size */
  sdSndMemGetBankStat( SDE_DATA_TYPE_PSTM_RING_BUF, ring, &addr, &hn->ringsz );
  if ( hn->ringsz < 0x800 ) {
    return -8;
  }
  if ( ( hn->ringsz & 0x7ff ) != 0 ) {
    return -9;
  }
  if ( ( size * 0x800 ) > hn->ringsz ) {
    return -10;
  }
  if ( ( hn->buf = syMalloc( size * 0x1000 ) ) == NULL ) {
    return -5;
  }
  hn->BlkNum = size;

  if ( sdPstmOpenPort( &hn->hn, 1, ring ) != SDE_ERR_NOTHING ) {
    return -3;
  }

  gdFsReqRd32( hn->fd, size, hn->buf );
  hn->stat = 2;
  hn->csz  = size * GDD_FS_SCTSIZE;
}

Sint32 pcmWaitTrans( struct gdstream *hn ) {
  Sint32 stat;

  if ( ( stat = gdFsGetTransStat( hn->fd ) ) == GDD_FS_TRANS_COMPLETE ) {
    hn->stat = 3;
  }
  if ( stat == GDD_FS_TRANS_ERROR ) {
    hn->stat = -1;
  }
  hn->page = 1;
}

Sint32 pcmTransGd( struct gdstream *hn ) {
  Sint32 size;
  Sint8  *addr;

  size = hn->BlkNum * 0x800;
  addr = hn->buf + hn->page * size;
  
  hn->csz += size;
  if ( ( hn->fsz - hn->csz ) < 0 ) {
    /* Transfer from GD is complete */
    hn->stat = 5;
    size = ( hn->fsz + GDD_FS_SCTSIZE -1 );
  }
  size = size / GDD_FS_SCTSIZE;

  gdFsReqRd32( hn->fd, size, addr );
  hn->page = ( hn->page + 1 ) % 2;
}

Sint32 pcmStop( struct gdstream *hn ) {
  sdPstmStop( hn->hn );
  /* Reset Status */
  hn->page = 0;
  hn->stat = 2;
  hn->csz  = 0;
  hn->ssz  = 0;

  /* Rewind file system */
  gdFsSeek( hn->fd, 0, GDD_SEEK_SET );
  gdFsReqRd32( hn->fd, hn->BlkNum, hn->buf );
}

void pcmTransSndEnd( Void *arg ) {
  struct gdstream *hn = ( struct gdstream * )arg;
  Uint32 diff;

  hn->addr += 0x1000;
  hn->ssz  += 0x1000;
  if ( hn->ssz > hn->fsz ) {
    hn->stat = 6;
  }
  /* Reach end of SH buffer? */
  diff = ( Uint32 )hn->addr - ( ( Uint32 )hn->buf + hn->BlkNum * 0x1000 );
  if ( diff <= 0 ) {
    /* Rewind buffer pointer */
    hn->addr = hn->buf;
  }
}

Sint32 pcmTransSnd( struct gdstream *hn ) {
  Bool flag;

  sdPstmIsTransferWaveData( hn->hn, CHANNEL_L, 0x1000, &flag );
  if ( flag == false ) {
    return 0;
  }
  sdMemBlkSetPrm( hn->memblk, hn->addr, 0x1000, pcmTransSndEnd, hn );
  sdPstmTransferWaveData( hn->hn, CHANNEL_L, hn->memblk );
}

Sint32 pcmNormalTask( struct gdstream *hn ) {
  Sint32 size;
  Sint8  *addr;
  SDS_PSTM_STAT stat;

  sdPstmGetStat( hn->hn, CHANNEL_L, &stat );
  if ( !( stat.m_Flg & SDD_PORT_FLG_PLAY ) ) {
    return 0;
  }
  /* GD Transfer Section */
  size = hn->BlkNum * 0x800;
  addr = hn->buf + size;
  if ( hn->stat == 4 ) {
    if ( hn->page == 0 ) {
      if ( ( Uint32 )hn->addr > ( Uint32 )addr ) {
        pcmTransGd( hn );
      }
    } else {
      if ( ( Uint32 )hn->addr <= ( Uint32 )addr ) {
        pcmTransGd( hn );
      }
    }
  }

  /* PCM Transfer Section */
  if ( hn->page == 1 ) {
    if ( ( Uint32 )( hn->addr + 0x1000 ) != ( Uint32 )addr ) {
      pcmTransSnd( hn );
    }
  } else {
    if ( ( Uint32 )hn->addr <= ( ( Uint32 )addr + size - 0x1000 ) ) {
      pcmTransSnd( hn );
    }
  }
}

Sint32 pcmWaitEnd( struct gdstream *hn ){
  Uint32 samp;
  Bool flag;

  sdPstmGetTotalSmpFrame( hn->hn, CHANNEL_L, &samp );
  if ( samp > hn->Total ) {
    pcmStop( hn );
    return;
  }
  sdPstmIsTransferWaveData( hn->hn, CHANNEL_L, 0x1000, &flag );
  if ( flag == false ) {
    return 0;
  }
  sdMemBlkSetPrm( hn->memblk, hn->silent, 0x1000, SDD_MEMBLK_SYNC_FUNC, NULL );
  sdPstmTransferWaveData( hn->hn, CHANNEL_L, hn->memblk );
}

Sint32 pcmTask( struct gdstream *hn ) {
  switch( hn->stat ) {
  case 2:
    pcmWaitTrans( hn );
    break;
  case 3:
    break;
  case 4:
    pcmNormalTask( hn );
    break;
  case 5:
    pcmNormalTask( hn );
    break;
  case 6:
    pcmWaitEnd( hn );
    break;
  }
}

Sint32 pcmPreLoad( struct gdstream *hn ) {
  Sint32 sz;

  if ( hn->ringsz > 0x7000 ) {
    sz = 0x7000;
  } else {
    sz = hn->ringsz;
  }
  sdMemBlkSetPrm( hn->memblk, hn->buf, sz, SDD_MEMBLK_NO_FUNC, NULL );
  sdPstmTransferWaveData( hn->hn, 0, hn->memblk );
  hn->addr = hn->buf + sz;
  hn->ssz  = sz;
}

Sint32 pcmPlay( struct gdstream *hn, Sint32 freq ) {
  volatile Float  i, oct;
  volatile Sint32 ret, tmp;
  SDE_PCM_TYPE    type;

  if ( hn->stat == -1 ) {
    return -7;
  }

  if ( hn->stat != 3 ) {
    return -6;
  }
  pcmPreLoad( hn );
  /* Caluculate OCT/FNS Value */
  i = ( Float )freq / 44100.0F;
  oct = njLog2( i );
  if ( oct >= 1 ) {
    tmp = 1;
  } else if ( oct >= 0 ) {
    tmp = 0;
  } else if ( oct >= -1 ) {
    tmp = -1;
  } else if ( oct >= -2 ) {
    tmp = -2;
  } else if ( oct >= -3 ) {
    tmp = -3;
  }

  ret = ( ( njPow( 2.0F, -oct ) * 1024.0F * ( Float )freq / 44100.0F ) - 1024.0F );
  ret += ( tmp * 2048 );
  ret &= 0x00007bff;
  hn->oct = ret;

  switch ( hn->bit ) {
  case 4:
    type = SDE_PCM_TYPE_4BIT_ADPCM;
    break;
  case 8:
    type = SDE_PCM_TYPE_8BIT_PCM;
    break;
  case 16:
    type = SDE_PCM_TYPE_16BIT_PCM;
    break;
  }
  sdPstmSetBaseVol( hn->hn, 0x7F);
  if ( sdPstmPlay( hn->hn, type, ret, 0) != SDE_ERR_NOTHING ) {
    return -4;
  }
  hn->stat = 4;
}

Sint32 pcmGetStat( struct gdstream *hn ) {
  return hn->stat;
}

void main(void)
{
  static Sint32   i = 0, num = 0;
  PDS_PERIPHERAL  *pad;
  struct gdstream hn;
  Sint8 *mes[] =  { "NO_USE", "RESERVE", "PRESET", "WAIT(STANDBY)", "PLAY", 
                    "GD TRANS END", "WAIT PLAY END" };
  Sint8 *trm[] = { "CPU", "DMA" };
  Sint32          a, b, c, d;
  Float           e, f, buf[ 4 ], avr;

  /* Initialize System */
  njSetBorderColor( 0x00000000 );
#ifdef VGA
  sbInitSystem( NJD_RESOLUTION_VGA, NJD_FRAMEBUFFER_MODE_RGB565, 1 );
#else
  sbInitSystem( NJD_RESOLUTION_320x240_NTSCNI, NJD_FRAMEBUFFER_MODE_RGB565, 1 );
#endif

  njInitVertexBuffer( 100000, 0, 100000, 0, 0 );
  njInitPrint( NULL, 0, NJD_TEXFMT_ARGB_1555 );
  njSetBackColor( 0x00000000, 0x00000000, 0x000000FF );

  SoundInitialize( "MANATEE.DRV", "STREAM.MLT" );
  sdMemBlkSetTransferMode( SDE_MEMBLK_TRANSFER_MODE_DMA );
  pcmInit( "STREAM.DAT", &hn, 16 );
  pcmSetPara( &hn, 0, 8 );
  sdMemBlkSetTransferMode( SDE_MEMBLK_TRANSFER_MODE_CPU );

  a = syTmrGetCount();
  while ( 1 ) {
    pad = pdGetPeripheral( PDD_PORT_A0 );
    if ( pad->press & PDD_DGT_TA ) {
      if ( pcmGetStat( &hn ) == 4 ) {
        pcmStop( &hn );
      } else {
        pcmPlay( &hn, 22050 );
      }
    }
    if ( pad->press & PDD_DGT_TB ) {
      if ( i == 0 ) {
        sdMemBlkSetTransferMode( SDE_MEMBLK_TRANSFER_MODE_DMA );
        ++i;
      } else {
        sdMemBlkSetTransferMode( SDE_MEMBLK_TRANSFER_MODE_CPU );
        i = 0;
      }
    }
    njPrintC( NJM_LOCATION( 7, 3 ), "PCM Stream FROM GD ROM Test" );
    njPrint( NJM_LOCATION( 12, 7 ), "%s", mes[ pcmGetStat( &hn ) ] );
    SoundLibVer( 4, 11 );
    SoundDrvVer( 4, 12 );
    SoundDrvStat( 4, 13 );
    c = syTmrGetCount();
    pcmTask( &hn );
    d = syTmrGetCount();
    sdSysServer();
    a = b;
    b = syTmrGetCount();
    njWaitVSync();
    e = syTmrDiffCount( c, d );
    f = syTmrDiffCount( a, b );
    buf[ ( num++ % 4 ) ] = e * 100.0F / f;
    avr = ( buf[ 0 ] + buf[ 1 ] + buf[ 2 ] + buf[ 3 ] ) / 4;
    njPrint( NJM_LOCATION( 7, 15 ), "TASK WEIGHT %lf PERCENT", avr );
    njPrint( NJM_LOCATION( 7, 16 ), "TRANSFER METHOD IS %s", trm[ i ] );
  }
}

/******************************* end of file *******************************/
