#include <shinobi.h>

#include "..\include\onmempcm.h"

mpHn OnMemPcmHn[ MP_MAX_PORT ];

void mpInit() {
  Sint32 i, j;

  for ( i = 0 ; i < MP_MAX_PORT ; ++i ) {
    OnMemPcmHn[ i ].Hn       = NULL;
    OnMemPcmHn[ i ].Status   = MP_STAT_IDLE;
    OnMemPcmHn[ i ].NumOfCh  = 1;
    OnMemPcmHn[ i ].Current  = 0;
    OnMemPcmHn[ i ].BaseVol  = 0x7f;   /* Base volume is max                    */
    OnMemPcmHn[ i ].Freq     = 44100;  /* Default frequency 44.1 KHz            */
    OnMemPcmHn[ i ].OctFns   = 0;      /* Frequency 44.1 kHz means 0 on OCT/FNS */
    OnMemPcmHn[ i ].TrnsSize = 0x1000; /* Transfer size to Sound RAM at a time  */
    OnMemPcmHn[ i ].Loop     = false;
    for ( j = 0 ; j < MP_MAX_CHANNEL ; ++j ) {
      OnMemPcmHn[ i ].ch[ j ].start = NULL;
      OnMemPcmHn[ i ].ch[ j ].vol   = 0x7f; /* Current volume is max */
      OnMemPcmHn[ i ].ch[ j ].pan   = 0x7f; /* Current pan is center */
    }
  }
}
 
mpHn *mpOpen( const char *name[], Sint32 num ) {
  GDFS   fp;
  Sint32 i, j;
  Sint32 size, sect;
  Sint32 err;

  /* Search unused handle */
  for ( i = 0 ; i < MP_MAX_PORT ; ++i ) {
    if ( OnMemPcmHn[ i ].Status == MP_STAT_IDLE ) break;
  }
  if ( i == MP_MAX_PORT ) return NULL;

  /* Find file(s) */
  j = 0;
  while ( num != j ) {
    if ( name[ j ] != NULL ) {
      if ( ( fp = gdFsOpen( name[ j ], NULL ) ) != NULL ) {
        if ( ( err = gdFsGetFileSctSize( fp, &sect ) ) == false ) {
          return NULL;
        }
        if ( ( err = gdFsGetFileSize( fp, &size ) ) == false ) {
          return NULL;
        }
        OnMemPcmHn[ i ].ch[ j ].start = syMalloc( sect * GDD_FS_SCTSIZE );
        gdFsRead( fp, sect, OnMemPcmHn[ i ].ch[ j ].start );
        gdFsClose( fp );
      } else {
        return NULL;
      }
    } else {
      return NULL;
    }
    ++j;
  }
  OnMemPcmHn[ i ].Size      = ( size & 0xffffffe0 ); /* Adjust 32 byte align    */
  OnMemPcmHn[ i ].Current   = 0;
  OnMemPcmHn[ i ].CurOff    = 0;                     /* Reset current offset    */
  OnMemPcmHn[ i ].NumOfCh   = num;                   /* Channel number          */
  OnMemPcmHn[ i ].BaseVol   = 0x7f;                  /* Base volume is MAX      */
  OnMemPcmHn[ i ].TrnsSize  = 0x1000;                /* Default transfer size   */
  OnMemPcmHn[ i ].Loop      = false;                 /* Loop is invalid         */
  OnMemPcmHn[ i ].LoopStart = 0;                     /* Loop start point is top */
  OnMemPcmHn[ i ].LoopEnd   = ( size & 0xffffffe0 ); /* Loop end point is bottom*/
  OnMemPcmHn[ i ].Malloc    = true;                  /* Buffer was allocated by
                                                        syMalloc                */
  OnMemPcmHn[ i ].NumOfCopy = 0;
  OnMemPcmHn[ i ].Parent    = NULL;                  /* This handle is super
                                                        class                   */
  OnMemPcmHn[ i ].LFO       = false;                 /* Callback function is 
                                                        invalid                 */
  OnMemPcmHn[ i ].LFOcb     = NULL;


  OnMemPcmHn[ i ].Status = MP_STAT_RESERB;           /* Status is handle reserb */

  return &OnMemPcmHn[ i ];
}

mpHn *mpOpenStatic( const char *name[], void *addr[], Sint32 num ) {
  GDFS   fp;
  Sint32 i, j;
  Sint32 size, sect;
  Sint32 err;

  /* Search unused handle */
  for ( i = 0 ; i < MP_MAX_PORT ; ++i ) {
    if ( OnMemPcmHn[ i ].Status == MP_STAT_IDLE ) break;
  }
  if ( i == MP_MAX_PORT ) return NULL;

  /* Find file(s) */
  j = 0;
  while ( num != j ) {
    if ( name[ j ] != NULL ) {
      if ( ( fp = gdFsOpen( name[ j ], NULL ) ) != NULL ) {
        if ( ( err = gdFsGetFileSctSize( fp, &sect ) ) == false ) {
          return NULL;
        }
        if ( ( err = gdFsGetFileSize( fp, &size ) ) == false ) {
          return NULL;
        }
        OnMemPcmHn[ i ].ch[ j ].start = addr[ j ];
        gdFsRead( fp, sect, OnMemPcmHn[ i ].ch[ j ].start );
        gdFsClose( fp );
      } else {
        return NULL;
      }
    } else {
      return NULL;
    }
    ++j;
  }

  OnMemPcmHn[ i ].Size      = ( size & 0xffffffe0 ); /* Align 32 byte           */
  OnMemPcmHn[ i ].Current   = 0;                     /*                         */
  OnMemPcmHn[ i ].NumOfCh   = num;                   /* Number of channel       */
  OnMemPcmHn[ i ].BaseVol   = 0x7f;                  /* Base volume is max      */
  OnMemPcmHn[ i ].TrnsSize  = 0x1000;                /* Transfer size to sound
                                                        RAM at a time is 1000H  */
  OnMemPcmHn[ i ].Loop      = false;                 /* Loop is invalid         */
  OnMemPcmHn[ i ].LoopStart = 0;                     /* Loop start point is top */
  OnMemPcmHn[ i ].LoopEnd   = ( size & 0xffffffe0 ); /* Loop end point is bottom*/
  OnMemPcmHn[ i ].Malloc    = false;                 /* Wave data buffer is
                                                        allocated by syMalloc   */
  OnMemPcmHn[ i ].Status = MP_STAT_RESERB;           /* Status is handle reserb */

  return &OnMemPcmHn[ i ];
}

mpHn *mpCopyHandle( mpHn *hn ) {
  Sint32 i, j;

  for ( i = 0 ; i < MP_MAX_PORT ; ++i ) {
    if ( OnMemPcmHn[ i ].Status == MP_STAT_IDLE ) break;
  }
  if ( i == MP_MAX_PORT ) return NULL;
  OnMemPcmHn[ i ].Size      = hn->Size;
  OnMemPcmHn[ i ].Current   = 0;
  OnMemPcmHn[ i ].NumOfCh   = hn->NumOfCh;
  OnMemPcmHn[ i ].BaseVol   = hn->BaseVol;
  OnMemPcmHn[ i ].FxChn     = hn->FxChn;
  OnMemPcmHn[ i ].TrnsSize  = hn->TrnsSize;
  OnMemPcmHn[ i ].Loop      = hn->Loop;
  OnMemPcmHn[ i ].LoopStart = hn->LoopStart;
  OnMemPcmHn[ i ].LoopEnd   = hn->LoopEnd;
  OnMemPcmHn[ i ].NumOfCopy = 0;
  ++hn->NumOfCopy;                            /* Parent handle is copied        */
  OnMemPcmHn[ i ].Parent    = &hn->NumOfCopy; /* Set super class of this handle */

  for ( j = 0 ; j < hn->NumOfCh ; ++j ) {
    OnMemPcmHn[ i ].ch[ j ].start = hn->ch[ j ].start;
    OnMemPcmHn[ i ].ch[ j ].vol   = hn->ch[ j ].vol;
    OnMemPcmHn[ i ].ch[ j ].pan   = hn->ch[ j ].pan;
    /* Create memory block handle*/
    sdMemBlkCreate( &OnMemPcmHn[ i ].ch[ j ].memblk );
  }

  OnMemPcmHn[ i ].Freq      = hn->Freq;
  OnMemPcmHn[ i ].OctFns    = hn->OctFns;
  OnMemPcmHn[ i ].Bit       = hn->Bit;

  OnMemPcmHn[ i ].Status    = MP_STAT_RESERB; /* handle status is reserved */

  return &OnMemPcmHn[ i ];
}

MPE_ERR mpSetPort( mpHn *hn, Sint32 *port, Sint32 num ) {
  Sint32  i;
  SDE_ERR err;

  if ( hn == NULL ) return MPE_ERR_NULL_HANDLE;
  if ( hn->NumOfCh != num ) return MPE_ERR_CHANNEL_NUMBER_MISMATCH;
  if ( hn->Status == MP_STAT_IDLE ) return MPE_ERR_UNUSED_HANDLE;

  /* Set Ring buffer number of sound RAM to use on this handle */
  for ( i = 0 ; i < hn->NumOfCh ; ++i ) {
    /* Create memory block     */
    sdMemBlkCreate( &hn->ch[ i ].memblk );
    err = sdSndMemGetBankStat( SDE_DATA_TYPE_PSTM_RING_BUF, *port, 
                               &hn->ch[ i ].baddr, &hn->ch[ i ].bsize );
    if ( err == SDE_ERR_DATA_ILLEGAL_TYPE ) return MPE_ERR_BUFFER_ATTRIBUTE;
    if ( hn->ch[ i ].bsize != hn->ch[ 0 ].bsize ) return MPE_ERR_BUFFER_SIZE;
    hn->ch[ i ].port = *port++;
  }

  /* Open PCM Stream port */
  if ( hn->NumOfCh == 1 ) {
    err = sdPstmOpenPort( &hn->Hn, 1, hn->ch[ 0 ].port );
    if ( hn->Hn == NULL ) return err;
  } else if ( hn->NumOfCh == 2 ) {
    err = sdPstmOpenPort( &hn->Hn, 2, hn->ch[ 0 ].port, hn->ch[ 1 ].port );
    if ( hn->Hn == NULL ) return err;
  }

  hn->Status = MP_STAT_STANDBY; /* Status is stand-by */

  return MPE_ERR_NONE;
}

MPE_ERR mpSetPrm( mpHn *hn, Sint32 freq, Sint32 bit ) {
  Float  i, j;
  Sint32 oct, k, tmp;

  if ( hn == NULL ) return MPE_ERR_NULL_HANDLE;
  if ( hn->Status == MP_STAT_IDLE ) return MPE_ERR_UNUSED_HANDLE;

  /* Caluculate OCT/FNS Value */
  i = ( Float )freq / 44100.0F;
  oct = njLog2( i );
  /* SHC R28 have bug. It can't cast negative float to signed integer */
  /* I think UNBELIEVABLE !!                                          */
  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;
  }

  hn->OctFns  = ( ( njPow( 2.0F, -tmp ) * 1024.0F * ( Float )freq / 44100.0F ) - 1024.0F );
  hn->OctFns += ( tmp * 2048 );
  if ( ( bit == 4 ) || ( bit == 8 ) || ( bit == 16 ) ) {
    hn->Bit = bit;
    for ( k = 0 ; k < hn->NumOfCh ; ++k ) {
      switch( bit ) {
      case 4:
        if ( hn->ch[ k ].bsize > 0x7000 ) hn->ch[ k ].bsize = 0x7000;
        hn->Samples = hn->Size * 2;
        break;
      case 8:
        if ( hn->ch[ k ].bsize > 0xF000 ) hn->ch[ k ].bsize = 0xF000;
        hn->Samples = hn->Size;
        break;
      case 16:
        if ( hn->ch[ k ].bsize > 0x1F000 ) hn->ch[ k ].bsize = 0x1F000;
        hn->Samples = hn->Size / 2;
        break;
      }
    }
  } else {
    return MPE_ERR_ILLEGAL_BITRATE;
  }
  /* Caluculate total time */
  j = ( hn->Size * 8 / bit ); /* Number of Sample                */
  j /= freq + 0.017;          /* Total time with Second, plus 1V */
  j /= 0.00028F;              /* FRT Count value                 */
  hn->Time = ( Sint32 )j;
  
  return MPE_ERR_NONE;
}

MPE_ERR mpPlay( mpHn *hn, Sint32 prio ) {
  Sint32       i, size;
  Bool         flag;
  SDE_PCM_TYPE bit;
  SDE_ERR      err;

  if ( hn == NULL ) return MPE_ERR_NULL_HANDLE;  
  if ( ( hn->Status != MP_STAT_STANDBY ) && ( hn->Status != MP_STAT_STOP ) ) {
    return MPE_ERR_NOT_PREPARE_TO_PLAY;
  }

  /* Maximun transfer size befor play is 7000H it's spec of sound driver */
  if ( hn->ch[ 0 ].bsize > 0x7000 ) size = 0x7000;
  else size = hn->ch[ 0 ].bsize;

  /* Check transfer whether to transfer wave data or give up */
  sdPstmIsTransferWaveData( hn->Hn, 0, size, &flag );
  if ( flag != true ) return MPE_ERR_CANT_TO_TRANSFER;
  hn->Prio = prio;

  for ( i = 0 ; i < hn->NumOfCh ; ++i ) {
    /* Create memory block */
    sdMemBlkSetPrm( hn->ch[ i ].memblk, hn->ch[ i ].start, size,
                    SDD_MEMBLK_NO_FUNC, NULL );
    /* Transfer wave data */
    sdPstmTransferWaveData( hn->Hn, i, hn->ch[ i ].memblk );
  }

  hn->Current = size;
  hn->CurOff  = size;
  if ( hn->CurOff == hn->ch[ 0 ].bsize ) {
    hn->CurOff = 0;
  }
  sdPstmSetBaseVol( hn->Hn, hn->BaseVol );

  if ( hn->Bit == 4 ) {
    bit = SDE_PCM_TYPE_4BIT_ADPCM;
  } else if ( hn->Bit == 8 ) {
    bit = SDE_PCM_TYPE_8BIT_PCM;
  } else {
    bit = SDE_PCM_TYPE_16BIT_PCM;
  }

  err = sdPstmPlay( hn->Hn, bit, hn->OctFns, hn->Prio );

  if ( err == SDE_ERR_NOTHING ) {
    hn->Status = MP_STAT_PLAY;
    hn->StartTime = syTmrGetCount();
  }
}

void mpTaskPlay( mpHn *hn ) {
  Sint32     i;
  Sint32     size;
  Bool       flag;
  SDS_PSTM_STAT stat;

  /* Execute callback function */
  if ( ( hn->LFO == true ) && ( hn->LFOcb != NULL ) ) {
    hn->LFOcb( hn );
  }

  sdPstmGetStat( hn->Hn, 0, &stat );
  if ( !( stat.m_Flg & SDD_PORT_FLG_PLAY ) ) {
    return;
  }

  /* Adjust transfer size */
  if ( ( hn->ch[ 0 ].bsize - hn->CurOff ) < hn->TrnsSize ) {
    size = hn->ch[ 0 ].bsize - hn->CurOff;
  } else {
    size = hn->TrnsSize;
  }

  /* Do music play to end ? */
  if ( hn->Current + size >= hn->LoopEnd ) {
    size = hn->LoopEnd - hn->Current;
    hn->Status = MP_STAT_END;
  }

  /* Check transfer whether to transfer wave data or give up */
  sdPstmIsTransferWaveData( hn->Hn, 0, size, &flag );
  if ( flag != true ) return;

  for ( i = 0 ; i < hn->NumOfCh ; ++i ) {
    /* Create memory block */
    sdMemBlkSetPrm( hn->ch[ i ].memblk, hn->ch[ i ].start + hn->Current / sizeof( Uint32 ),
                    size, SDD_MEMBLK_NO_FUNC, NULL );
    /* Transfer wave data */
    sdPstmTransferWaveData( hn->Hn, i, hn->ch[ i ].memblk );
  }
  hn->CurOff += size;
  if ( hn->CurOff == hn->ch[ 0 ].bsize ) {
    hn->CurOff = 0;
  }
  /* If loop play is valid, prepare loop play */
  if ( ( hn->Status == MP_STAT_END ) && ( hn->Loop == true ) ) {
    hn->Current = hn->LoopStart;
    hn->Status = MP_STAT_PLAY;
    hn->StartTime = syTmrGetCount() + syTmrMicroToCount( 34000 );
  } else {
    hn->Current += hn->TrnsSize;
  }
}

void mpTaskEnd( mpHn *hn ) {
  Uint32 now;

  /* Get now counter value */
/*  now = syTmrDiffCount( hn->StartTime, syTmrGetCount() );*/  /* '99.3.25 */
  sdPstmGetTotalSmpFrame( hn->Hn, 0, &now );
  /* Play time passed ? */
  if ( now > hn->Samples ) {
    hn->Status = MP_STAT_STOP;
    sdPstmStop( hn->Hn );
  }
}

void mpTask( void ) {
  Sint32 i;

  for ( i = 0 ; i < MP_MAX_PORT ; ++i ) {
    switch( OnMemPcmHn[ i ].Status ) {
    /* Transfer wave data to sound RAM */
    case MP_STAT_PLAY:
      mpTaskPlay( &OnMemPcmHn[ i ] );
      break;
    /* Transfer wave data is end. Waiting end of play time */
    case MP_STAT_END:
      mpTaskEnd( &OnMemPcmHn[ i ] );
      break;
    }
  }
}

MPE_ERR mpStop( mpHn *hn ) {
  if ( hn == NULL ) return MPE_ERR_NULL_HANDLE;
  if ( hn->Status == MP_STAT_IDLE ) return MPE_ERR_UNUSED_HANDLE;

  hn->Status = MP_STAT_STOP;
  sdPstmStop( hn->Hn );

  return MPE_ERR_NONE;
}
 
MPE_ERR mpClose( mpHn *hn ) {
  Sint32 i;

  if ( hn == NULL ) return MPE_ERR_NULL_HANDLE;  
  if ( hn->Status == MP_STAT_IDLE ) return MPE_ERR_UNUSED_HANDLE;
  /* This handle was copied and buffer is used any other handle */
  if ( hn->NumOfCopy > 0 ) return MPE_ERR_BUFFER_IS_USED;

  for ( i = 0 ; i < hn->NumOfCh ; ++i ) {
    if ( hn->Malloc == true ) {
      syFree( hn->ch[ i ].start );
    }
    hn->ch[ i ].vol   = 0x7f;
    hn->ch[ i ].pan   = 0x7f;
    sdMemBlkDestroy( hn->ch[ i ].memblk );
  }
  /* Set handle value to default */
  hn->Hn       = NULL;
  hn->Status   = MP_STAT_IDLE;
  hn->NumOfCh  = 1;
  hn->Current  = 0;
  hn->BaseVol  = 0x7f;
  hn->Freq     = 44100;
  hn->OctFns   = 0;
  hn->TrnsSize = 0x1000;
  hn->Loop     = false;
  /* Free parent handle copy flag */
  if ( hn->Parent != NULL ) {
    --*hn->Parent;
  }


  return MPE_ERR_NONE;
}

MPE_ERR mpSetBaseVol( mpHn *hn, Sint32 vol ) {
  if ( hn == NULL ) return MPE_ERR_NULL_HANDLE;
  if ( hn->Status == MP_STAT_IDLE ) return MPE_ERR_UNUSED_HANDLE;
  if ( ( vol < 0 ) || ( vol > 0x7f ) ) return MPE_ERR_ILLEGAL_VOLUME;

  hn->BaseVol = vol;
  sdPstmSetBaseVol( hn->Hn, vol );
  return MPE_ERR_NONE;
}

MPE_ERR mpSetVol( mpHn *hn, Sint32 vol, Sint32 fade ) {
  Sint32 i;

  if ( hn == NULL ) return MPE_ERR_NULL_HANDLE;
  if ( hn->Status == MP_STAT_IDLE ) return MPE_ERR_UNUSED_HANDLE;
  if ( ( vol < -0x7f ) || ( vol > 0x7f ) ) return MPE_ERR_ILLEGAL_VOLUME;
  if ( ( fade < 0 ) || ( fade > 0x7fff ) ) return MPE_ERR_ILLEGAL_FADE;

  for ( i = 0 ; i < hn->NumOfCh ; ++i ) {
    sdPstmSetVol( hn->Hn, i, vol, fade );
  }
  return MPE_ERR_NONE;
}

MPE_ERR mpSetPan( mpHn *hn, Sint32 pan, Sint32 fade ) {
  if ( hn == NULL ) return MPE_ERR_NULL_HANDLE;
  if ( hn->Status == MP_STAT_IDLE ) return MPE_ERR_UNUSED_HANDLE;
  if ( ( pan < -0x7f ) || ( pan > 0x7f ) ) return MPE_ERR_ILLEGAL_PAN;
  if ( ( fade < 0 ) || ( fade > 0x7fff ) ) return MPE_ERR_ILLEGAL_FADE;

  sdPstmSetPan( hn->Hn, hn->ch[ 0 ].port, pan, fade );
  return MPE_ERR_NONE;
}

MPE_ERR mpSetFx( mpHn *hn, Sint32 ch, Sint32 lev, Sint32 dep ) {
  if ( hn == NULL ) return MPE_ERR_NULL_HANDLE;
  if ( hn->Status == MP_STAT_IDLE ) return MPE_ERR_UNUSED_HANDLE;

  if ( ch < 0 || ch > 15 ) return MPE_ERR_ILLEGAL_FX_CHANNEL;
  if ( lev < 0 ) lev = 0;
  if ( lev > 15 ) lev = 15;
  sdPstmSetFxCh( hn->Hn, ch, lev );
  if ( dep < -15 ) dep = -15;
  if ( dep > 15 ) dep = 15;
  sdPstmSetFxLev( hn->Hn, dep );

  return MPE_ERR_NONE;;
}

MPE_ERR mpSetDry( mpHn *hn, Sint32 lev ) {
  if ( hn == NULL ) return MPE_ERR_NULL_HANDLE;
  if ( hn->Status == MP_STAT_IDLE ) return MPE_ERR_UNUSED_HANDLE;

  if ( lev < -0xf ) lev = -0xf;
  if ( lev > 0xf ) lev = 0xf;

  sdPstmSetDrctLev( hn->Hn, lev );
}

MPE_ERR mpSetLoop( mpHn *hn, Bool flag ) {
  if ( hn == NULL ) return MPE_ERR_NULL_HANDLE;
  if ( hn->Status == MP_STAT_IDLE ) return MPE_ERR_UNUSED_HANDLE;
  if ( hn->Bit == 4 ) return MPE_ERR_ADPCM_NOT_FOR_LOOP;

  hn->Loop = flag;

  return MPE_ERR_NONE;
} 

MPE_ERR mpSetTransferSize( mpHn *hn, Sint32 size ) {
  if ( hn == NULL ) return MPE_ERR_NULL_HANDLE;
  if ( hn->Status == MP_STAT_IDLE ) return MPE_ERR_UNUSED_HANDLE;

  size &= 0xfffff700;
  if ( size > hn->ch[ 0 ].bsize ) return MPE_ERR_TOO_LARGE_TRANSFER_SIZE;
  hn->TrnsSize = size;

  return MPE_ERR_NONE;
}

MPE_ERR mpChangePlaySample( mpHn *hn, Sint32 sample ) {
  Sint32 addr;

  if ( hn == NULL ) return MPE_ERR_NULL_HANDLE;
  if ( hn->Status == MP_STAT_IDLE ) return MPE_ERR_UNUSED_HANDLE;
  if ( hn->Bit == 4 ) return MPE_ERR_ADPCM_NOT_FOR_LOOP;

  if ( sample > ( hn->Size * 8 / hn->Bit ) ) return MPE_ERR_SAMPLE_POINT_OVER;

  addr = sample * hn->Bit / 8;

  /* DMA required transfer address with aligned 32 byte */
  addr &= 0xffffffe0;

  hn->Current = addr;
  
  return MPE_ERR_NONE;
}

MPE_ERR mpSetLoopStart( mpHn *hn, Sint32 sample ) {
  Sint32 addr;
  Float  a;

  if ( hn == NULL ) return MPE_ERR_NULL_HANDLE;
  if ( hn->Status == MP_STAT_IDLE ) return MPE_ERR_UNUSED_HANDLE;
  if ( sample > ( hn->Size * 8 / hn->Bit ) ) return MPE_ERR_SAMPLE_POINT_OVER;
  if ( hn->Bit == 4 ) return MPE_ERR_ADPCM_NOT_FOR_LOOP;

  addr = sample * hn->Bit / 8;

  /* DMA required transfer address with aligned 32 byte */
  addr &= 0xffffffe0;

  hn->LoopStart = addr;

  /* Calcurate play time in free running timer conter value */
  a  = ( hn->LoopEnd - addr ) * 8 / hn->Bit;
  a /= hn->Freq;
  a += 0.017;
  hn->Time =  ( Uint32 )( a / 0.00028F );

  return MPE_ERR_NONE;
}

MPE_ERR mpSetLoopEnd( mpHn *hn, Sint32 sample ) {
  Sint32 addr;
  Float  a;

  if ( hn == NULL ) return MPE_ERR_NULL_HANDLE;
  if ( hn->Status == MP_STAT_IDLE ) return MPE_ERR_UNUSED_HANDLE;
  if ( sample > ( hn->Size * 8 / hn->Bit ) ) return MPE_ERR_SAMPLE_POINT_OVER;
  if ( hn->Bit == 4 ) return MPE_ERR_ADPCM_NOT_FOR_LOOP;

  /* Fix address with sampling bit */
  addr = sample * hn->Bit / 8;

  /* DMA required transfer address with aligned 32 byte */
  addr &= 0xffffffe0;

  hn->LoopEnd = addr;

  /* Calcurate play time in free running timer conter value */
  a  = ( addr - hn->LoopStart ) * 8 / hn->Bit;
  a /= hn->Freq;
  a += 0.017;
  hn->Time =  ( Uint32 )( a / 0.00028F );

  return MPE_ERR_NONE;
}

MPE_ERR mpSetLoopEndToBottom( mpHn *hn ) {
  Float a;

  if ( hn == NULL ) return MPE_ERR_NULL_HANDLE;
  if ( hn->Status == MP_STAT_IDLE ) return MPE_ERR_UNUSED_HANDLE;

  hn->LoopEnd = hn->Size;

  /* Calcurate play time in free running timer conter value */
  a  = ( hn->LoopEnd - hn->LoopStart ) * 8 / hn->Bit;
  a /= hn->Freq;
  a += 0.017;
  hn->Time =  ( Uint32 )( a / 0.00028F );

  return MPE_ERR_NONE;
}

MPE_ERR mpSetLFOCallback( mpHn *hn, void (*func)(mpHn *) ) {
  if ( hn == NULL ) return MPE_ERR_NULL_HANDLE;
  if ( hn->Status == MP_STAT_IDLE ) return MPE_ERR_UNUSED_HANDLE;

  if ( func != NULL && hn->LFOcb !=NULL ) return MPE_ERR_REGISTER_NULL_FUNCTION;
  
  hn->LFOcb = func;
  if ( func == NULL ) {
    hn->LFO = 0;
    sdPstmResetPrm( hn->Hn );
  }
  return MPE_ERR_NONE;
}

MPE_ERR mpSetLFO( mpHn *hn, Bool sw ) {
  if ( hn == NULL ) return MPE_ERR_NULL_HANDLE;
  if ( hn->Status == MP_STAT_IDLE ) return MPE_ERR_UNUSED_HANDLE;

  hn->LFO = sw;

  if ( sw == true ) {
    hn->LFOStart = syTmrGetCount();
  } else {
    hn->LFOStart = 0;
  }
  return MPE_ERR_NONE;
}

Sint32 mpGetStat( mpHn *hn ) {
  return hn->Status;
}
