/// ****************************************************************************
/// @file   
/// @author Dr.Neeton
/// @date   2008/X/X - 2014/01/28
/// 
/// @brief  CSC(Core Sound Codec)関連
/// 
/// ****************************************************************************

#include "../../Core/Base/CPL_Core_Encryption.h"
#include "CPL_Core_CSC.h"


namespace CPL {
namespace Core {


char_cptr CPL_CSC_DECODE_ERROR =  "CSCファイルのデーコードに失敗しました";

const int MAX_BIT = 15;

inline int  zzf_bitcnv_low( int in_value, int in_bit_shift )
{
  return (in_value >> in_bit_shift);
}
inline int  zzf_bitcnv_high( int in_value, int in_bit_shift )
{
  return (in_value << in_bit_shift);
}

int     zzlf_getBitArray( const U8* in_ptr_buf, int in_bit, int in_index );
void    zzlf_setBitArray( U8* in_ptr_buf, int in_bit, int in_index, int val );
void    zzlf_bitConvertLowTable( short* out_ptr_start, short* out_ptr_end, int in_bit_shift );
void    zzlf_bitConvertHighTable( short* out_ptr_start, short* out_ptr_end, int in_bit_shift );
void    zzlf_flatInertia( short* io_org_buf, float in_weight );
void    zzlf_flatAverage( short* io_org_buf, int in_num );
void    zzlf_getSubTable( short_cptr in_ptr_org_buf, short* out_ptr_sub_buf, int in_num );
void    zzlf_getSubTable_by_PredictiveValue( short_cptr in_ptr_org_buf, int_ptr out_ptr_sub_buf, int in_num );
short2  zzlf_getMinMax( short_cptr in_ptr_start, short_cptr in_ptr_end );
int     zzlf_getRequiredBit_by_Table( int_cptr in_ptr_start, int_cptr in_ptr_end );
void    zzlf_getDecodeBuf( u_char_cptr in_data_addr, int in_idx_start, int in_idx_end, int in_bit, short_ptr out_pcm_buf );

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// 予測値を取得
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
inline short zzlf_predictiveValue( int in_prev1, int in_prev2, int in_prev3 )
{
//  int add_value0 = in_prev1 - in_prev2;
//  int add_value1 = in_prev2 - in_prev3;
//  return in_prev1 + add_value0 + (add_value0 - add_value1);

//  = in_prev1 + (in_prev1 - in_prev2) + (in_prev1 - in_prev2) - (in_prev2 - in_prev3)
//  = in_prev1 + in_prev1 - in_prev2 + in_prev1 - in_prev2 - in_prev2 + in_prev3
//  = in_prev1*3 - in_prev2*3 + in_prev3
//  = (in_prev1 - in_prev2) * 3 + in_prev3

  int pre_value = (in_prev1 - in_prev2) * 3 + in_prev3;

  if( pre_value < -32768 )
  {
    pre_value = -32768;
  }
  else
  if( pre_value > 32767 )
  {
    pre_value = 32767;
  }

  return pre_value;
}


// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// 必要ビット数を取得
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
inline int zzlf_getRequiredBit( int in_value )
{
  static const int bit_check_tbl[17][2] = {
         0,     0,
        -1,     0,
        -2,     1,
        -4,     3,
        -8,     7,
       -16,    15,
       -32,    31,
       -64,    63,
      -128,   127,
      -256,   255,
      -512,   511,
     -1024,  1023,
     -2048,  2047,
     -4096,  4095,
     -8192,  8191,
    -16384, 16383,
    -32768, 32767,
  };

  for( int bit = 0; bit < 17; ++bit )
  {
    if( bit_check_tbl[bit][0] <= in_value  &&  in_value <= bit_check_tbl[bit][1] )
    {
      return bit;
    }
  }

  return 17; // shortで表せられない場合
}


#if (CPL_CSC_VERSION == 1)
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// エンコード（WAVE -> CSC）
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bool baseCscData::Encode(
      const BaseWaveData& in_wave_data,
      int                 in_max_bit,
      float               in_zero_limit )
{
  baseStream const* ptr_wave_stream = dynamic_cast<baseStream const*>(&in_wave_data);
  baseStream*       ptr_csc_stream  = dynamic_cast<baseStream*>(this);
  if( !ptr_wave_stream  ||  !ptr_csc_stream )
  {
    return false;
  }

  if( in_max_bit > MAX_BIT )
  {
    in_max_bit = MAX_BIT;
  }

  // 情報設定
  zzf_setWaveInfo( in_wave_data, in_max_bit );

  // データチャンク取得
  const WaveInfo& wave_info = in_wave_data.GetWaveInfo();
  const WaveInfo::ChunkOffs* ptr_data_chunk = wave_info.FindChunk( "data" );
  if( !ptr_data_chunk )
  {
    return false;
  }

  const int bits          = m_csc_info.bits;
  const int channel_num   = m_csc_info.channel;
  const int sample_num    = m_csc_info.sample_num;//.wave_data_size / (m_csc_info.bits / 8) / channel_num;
  const int block_num     = (sample_num + CSC_BLOCK_SIZE - 1) / CSC_BLOCK_SIZE; // １チャンネルあたりのブロック数
  const int sample_ex_num = block_num * CSC_BLOCK_SIZE; // ブロック化拡張後のサンプリング数
  const int bit_shift     = 16 - CPL::Clamp( in_max_bit, 5, MAX_BIT );

  // 情報書き込み　※ この時点ではまだ情報は不完全なので、後から修正する
  ptr_csc_stream->SeekSet( 0 );
  ptr_csc_stream->Write( &m_csc_info, sizeof(m_csc_info) - sizeof(CscBlockInfo) );
  // ブロックデータ書き込み
  const int total_block_num = block_num * channel_num;
  // ブロック情報領域書き込み（この時点では情報不足のため後で設定）
  Array::Constant<CscBlockInfo> block_info_table( total_block_num, CscBlockInfo() );
  ptr_csc_stream->Write( &block_info_table[0], block_info_table.GetSize() );

  // WAVEストリーム位置調整
  int wave_data_offset = ptr_data_chunk->offset;
  int wave_data_size   = ptr_data_chunk->size;
  int wave_read_size   = 0;
  ptr_wave_stream->SeekSet( wave_data_offset );
  
  //***** チャンネルごとのバッファを作成 *****//
  Array::Dynamic<short> org_data[ 2 ];

  {
    const int buf_size = (wave_data_size + 3) / 4 * 4; // なんとなく4byteアライメント
    const int sample_num = buf_size / sizeof(short);
    Array::Constant<short> tmp_buf( sample_num );
    ptr_wave_stream->Read( &tmp_buf[0], buf_size );

    for( int ch = 0; ch < channel_num; ++ch )
    {
      org_data[ ch ].Resize( sample_num );

      for( int i = 0; i < sample_num; ++i )
      {
        org_data[ ch ][ i ] = tmp_buf[ i * channel_num + ch ];
      }

//      /***** 前処理 *****/
//      // 最小音量カット
//      if( in_zero_limit != 0 )
//      {
//        zzlf_minCut( org_data[ ch ], in_zero_limit );
//      }

//      // 量子化幅を単純圧縮
//      if( bit_shift > 0 )
//      {
//        for( int i = 0; i < sample_num; ++i )
//        {
//          org_data[ ch ][ i ] = zzf_bitcnv_low( org_data[ ch ][ i ], bit_shift );
//        }
//      }
    }
  }

  // キーデータ書き込み
  U8 tmp_buf[ 2 ][ CSC_BLOCK_SIZE * 2 ];
  int offset_cnt = 0;
  const int block_size_byte = CSC_BLOCK_SIZE * channel_num;
  const int read_byte = bits / 8;
  short*    ptr_mask_addr = nullptr;
  int       tmp_sample_num = 0;
  int       size0, size1;

  for( int block_idx = 0; block_idx < block_num; ++block_idx )
  {
    for( int ch = 0; ch < channel_num; ++ch )
    {
      tmp_sample_num = Min( CSC_BLOCK_SIZE, sample_num - block_idx * CSC_BLOCK_SIZE );
      const int block_head_idx = block_idx * CSC_BLOCK_SIZE;
      short*    ptr_org_data = &org_data[ ch ][ block_head_idx ];

      // タイプ０での圧縮データの取得
      size0 =
        zzf_enc_getCompressBlockBuffer(
          ptr_org_data,
          tmp_buf[ 0 ],
          tmp_sample_num,
          CT_SIMPLE );

      // タイプ１での圧縮データの取得
      size1 =
        zzf_enc_getCompressBlockBuffer(
          ptr_org_data,
          tmp_buf[ 1 ],
          tmp_sample_num,
          CT_SUB1 );

      // データ書き込み
      const int block_info_index = block_idx * channel_num + ch;
      if( size0 <= size1 )
      {
        ptr_csc_stream->Write( tmp_buf[ 0 ], size0 );
        // ブロック情報修正
        block_info_table[ block_info_index ] =
          CscBlockInfo(
            offset_cnt,
            *ptr_org_data, // ブロックの先頭の値
            size0,
            CT_SIMPLE );
        // 次のオフセット
        offset_cnt += size0;
      }
      else
      {
        ptr_csc_stream->Write( tmp_buf[ 1 ], size1 );
        // ブロック情報修正
        block_info_table[ block_info_index ] =
          CscBlockInfo(
            offset_cnt,
            *ptr_org_data, // ブロックの先頭の値
            size1,
            CT_SUB1 );
        // 次のオフセット
        offset_cnt += size1;
      }
    }
  }

  // 残りの情報を設定
  m_csc_info.block_num = total_block_num;
  m_csc_info.dvac_size = ptr_csc_stream->GetSize() - 8;
  // 再書き込み
  ptr_csc_stream->SeekSet( 0 );
  ptr_csc_stream->Write( &m_csc_info, sizeof(m_csc_info) - sizeof(CscBlockInfo) );

  // ブロック情報を暗号化
  Encryption(
    &block_info_table[0], block_info_table.GetSize(),
    m_mask, m_mask.GetLength() );

  // ブロック情報再書き込み
  ptr_csc_stream->Write( &block_info_table[0], sizeof(CscBlockInfo) * total_block_num );

  // データ設定
  SetupDataBuffer();

  return true;
}
#endif


#if (CPL_CSC_VERSION == 2)
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// エンコード（WAVE -> CSC） ※ 改良版
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bool baseCscData::Encode2( CscEncodeInfo& io_enc_info )
{
  Core::Stream_i_ const* ptr_wave_stream =
    dynamic_cast<Core::Stream_i_ const*>(io_enc_info.ptr_wave_data);
  Core::Stream_i_* ptr_csc_stream =
    dynamic_cast<Core::Stream_i_*>(this);

  if( !ptr_wave_stream  ||  !ptr_csc_stream )
  {
    return false;
  }

  // 情報設定
  zzf_setWaveInfo2( *io_enc_info.ptr_wave_data );

  // データチャンク取得
  const WaveInfo& wave_info = io_enc_info.ptr_wave_data->GetWaveInfo();
  const WaveInfo::ChunkOffs* ptr_data_chunk = wave_info.FindChunk( "data" );
  if( !ptr_data_chunk )
  {
    return false;
  }

//  const int bits          = io_enc_info.bits          = m_csc_info2.bits;
  const int channel_num   = io_enc_info.channel_num   = m_csc_info2.channel;
  const int sample_num    = io_enc_info.sample_num    = m_csc_info2.sample_num;//.wave_data_size / (m_csc_info2.bits / 8) / channel_num;
  const int block_num     = io_enc_info.block_num     = (sample_num + CSC_BLOCK_SIZE - 1) / CSC_BLOCK_SIZE; // １チャンネルあたりのブロック数
  const int sample_ex_num = io_enc_info.sample_ex_num = block_num * CSC_BLOCK_SIZE; // ブロック化拡張後のサンプリング数
//  const int bit_shift     = 16 - CPL::Clamp( in_max_bit, 5, MAX_BIT );

  // 情報書き込み　※ この時点ではまだ情報は不完全なので、後から修正する
  ptr_csc_stream->SeekSet( 0 );
  ptr_csc_stream->Write( &m_csc_info2, sizeof(m_csc_info2) );
  // ブロックデータ書き込み
  const int total_block_num = block_num * channel_num;
  // ブロック情報領域書き込み（この時点では情報不足のため後で設定）
  Core::Array::Constant<CscBlockInfo2> block_info_table( total_block_num );
  ptr_csc_stream->Write( &block_info_table[0], block_info_table.GetSize() );

  // WAVEストリーム位置調整
  int wave_data_offset = ptr_data_chunk->offset;
  int wave_data_size   = ptr_data_chunk->size;
  int wave_read_size   = 0;
  ptr_wave_stream->SeekSet( wave_data_offset );
  
  //***** チャンネル別に連続したバッファを作成 *****//
  Core::Array::MultiArray<short> org_data( channel_num, sample_num );
  {
    const int buf_size = (wave_data_size + 3) / 4 * 4; // なんとなく4byteアライメント
    const int tmp_sample_num = buf_size / sizeof(short);
    Core::Array::Constant<short> tmp_buf( tmp_sample_num );
    ptr_wave_stream->Read( &tmp_buf[0], buf_size ); // waveデータ読み込み
    short* ptr_org_data = &org_data[ 0 ][ 0 ]; // 多次元配列クラスは要素参照にオーバーヘッドがあるため、生ポインタへ。

    for( int ch = 0; ch < channel_num; ++ch )
    {
      for( int sample_idx = 0; sample_idx < sample_num; ++sample_idx )
      {
        ptr_org_data[ ch * sample_num + sample_idx ] = tmp_buf[ sample_idx * channel_num + ch ];
      }
    }
  }

  // ブロック単位でのエンコード
  U8 tmp_output_buf[ CSC_BLOCK_SIZE * 4 ];
  int offset_count = 0;
  for( int block_idx = 0; block_idx < block_num; ++block_idx )
  {
    for( int ch = 0; ch < channel_num; ++ch )
    {
      int bidx = block_idx * channel_num + ch;
      int size =
        zzf_encode2_Block(
          io_enc_info,
          block_info_table[ bidx ],
          &org_data[ ch ][ block_idx * CSC_BLOCK_SIZE ],
          block_idx,
          tmp_output_buf );
      //
      block_info_table[ bidx ].block_offset = offset_count; // この値はデータ領域からのオフセット（つまり最初は０となる）
      offset_count += size;
      //
      ptr_csc_stream->Write( tmp_output_buf, size );
    }
  }

  // 残りの情報を設定
  m_csc_info2.block_num = total_block_num;
  m_csc_info2.csc_size  = static_cast<int>(ptr_csc_stream->GetSize());

  // ヘッダ情報の再書き込み
  ptr_csc_stream->SeekSet( 0 );
  ptr_csc_stream->Write( &m_csc_info2, sizeof(m_csc_info2) );

  // ブロックオフセットを暗号化
  Encryption(
    &block_info_table[0],
    block_info_table.GetSize(),
    m_mask,
    static_cast<int>(m_mask.GetLength()) );

  // ブロックオフセット再書き込み
  ptr_csc_stream->Write(
    &block_info_table[0],
    block_info_table.GetSize() );

  // データ設定
  SetupDataBuffer();

  return true;
}
#endif



#if (CPL_CSC_VERSION == 2)
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// ブロック単位でのエンコード
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
int baseCscData::zzf_encode2_Block(
      CscEncodeInfo&  io_enc_info,
      CscBlockInfo2&  io_block_info,
      short_cptr      in_org_buf,
      int             in_block_idx,
      U8              out_ptr_buf[ CSC_BLOCK_SIZE*4 ] )
{
  const int tmp_sample_num =
    Min( CSC_BLOCK_SIZE, io_enc_info.sample_num - in_block_idx * CSC_BLOCK_SIZE );

//  const int block_head_idx = in_block_idx * CSC_BLOCK_SIZE;

  // 単純差分バッファを取得

  // 予測値との差分バッファを取得
  int pre_sub_buf[ CSC_BLOCK_SIZE ];
  zzlf_getSubTable_by_PredictiveValue(
    in_org_buf,
    pre_sub_buf,
    tmp_sample_num );
/*
  // 予測差分バッファの８サンプルごとの最大と最小を求める
  const int tmp_min_max_buf_num = CSC_BLOCK_SIZE / 8;
  short tmp_min_max_buf[ tmp_min_max_buf_num ];
  for( int buf_idx = 0; buf_idx < tmp_min_max_buf_num; ++buf_idx )
  {
    const int smp_idx = buf_idx * 8;

    tmp_min_max_buf[ buf_idx ] =
      zzlf_getMinMax(
        pre_sub_buf + smp_idx,
        pre_sub_buf + smp_idx + 7 );
  }
*/
  // 予測差分バッファの８サンプルごとの必要最大ビット数を求める
  // ※ 先頭はCscBlockInfo2::top_valueであり０なので含めない
  const int bit_buf_num = CSC_BLOCK_SIZE / 8;
  U8 pre_bit_buf[ bit_buf_num ];
  for( int buf_idx = 0; buf_idx < bit_buf_num; ++buf_idx )
  {
    const int smp_idx = buf_idx * 8;

    pre_bit_buf[ buf_idx ] =
      zzlf_getRequiredBit_by_Table(
        pre_sub_buf + smp_idx + 1,
        pre_sub_buf + smp_idx + 1 + 7 );
  }

  // ブロックヘッダ設定
  io_block_info.block_size = 0;
  io_block_info.top_value  = in_org_buf[0];
  io_block_info.compress_type = io_enc_info.flag_lossless ? 0 : 1;
  U8* write_addr = out_ptr_buf;
  for( int unit_idx = 0; unit_idx < bit_buf_num; ++unit_idx )
  {
    // ビット設定
    int bit = pre_bit_buf[ unit_idx ];
    // 
    CscUnitInfo00* ptr_unit = reinterpret_cast<CscUnitInfo00*>(write_addr);
    if( true/*io_enc_info.flag_lossless*/ )
    {
      ptr_unit->type = 0;
      if( bit <= 15 )
      {
        ptr_unit->bits = (bit > 1) ? bit - 1 : bit;
      }
      else
      {
        ptr_unit->bits = 15;
        bit = 16;
      }
      ptr_unit->num  = 1; // とりあえず
    }
    else
    {
      if( bit <= 8 )
      {
        ptr_unit->type = 0;
        ptr_unit->bits = (bit > 1) ? bit - 1 : bit ;
      }
      else
      {
        ptr_unit->type = 1;
        ptr_unit->bits = 9;
      }
    }

    write_addr += sizeof(CscUnitInfo00);

    // データ書き込み
    if( ptr_unit->bits == 15 )
    {
      // 直値書き込み
      for( int i = 0; i < 8; ++i )
      {
        const int smp_idx = unit_idx * 8 + i;
        reinterpret_cast<short*>(write_addr)[ i ] = pre_sub_buf[ smp_idx + 1 ];
      }
    }
    else
    {
      // 予測差分の書き込み
      for( int i = 0; i < 8; ++i )
      {
        const int smp_idx = unit_idx * 8 + i;
        zzlf_setBitArray(
          write_addr,
          bit,
          i,
          pre_sub_buf[ smp_idx + 1 ] );
      }
    }
    write_addr += bit;
  }
  io_block_info.block_size = write_addr - out_ptr_buf;

  return io_block_info.block_size;
}
#endif



#if (CPL_CSC_VERSION == 1)
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// デコード（CSC -> WAVE）
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bool baseCscData::Decode( BaseWaveData& out_wave_data ) const
{
  baseStream const* ptr_csc_stream = dynamic_cast<baseStream const*>(this);
  baseStream*       ptr_wave_stream = dynamic_cast<baseStream*>(&out_wave_data);
  if( !ptr_wave_stream  ||  !ptr_csc_stream )
  {
    return false;
  }

  // 一時バッファに読み込み
  Core::MemoryStream buffer;
  buffer = *ptr_csc_stream;

  // 情報取得
  U8*            ptr_top_addr = (U8*)buffer.GetTopAddr();
  CscHeaderInfo* ptr_info     = (CscHeaderInfo*)ptr_top_addr;
  const int      bits         = ptr_info->bits;
  const int      channel      = ptr_info->channel;
  const int      block_num    = ptr_info->block_num / channel;
  const int      sample_num   = ptr_info->sample_num;
  const int      data_offset  = sizeof(CscHeaderInfo) + sizeof(CscBlockInfo) * (block_num - 1);
  CscBlockInfo*  block_info_table = ptr_info->block_offs;
  // ブロック情報を復号化
  Decryption(
    block_info_table, sizeof(CscBlockInfo) * block_num,
    m_mask, m_mask.GetLength() );


  // WAVE情報の書き込み
  ptr_wave_stream->Release();
  ptr_wave_stream->Resize( ptr_info->wave_size );
  // ヘッダ
  ptr_wave_stream->Write( &WaveInfo::Head( ptr_info->wave_size-8 ), sizeof(WaveInfo::Head) );
  // fmtチャンク
  WaveInfo::ChunkFormat fmt;
  memcpy( fmt.tag, SwS("fmt "), 4 );
  fmt.size       = 16;
  fmt.format     = 0x0001;
  fmt.channels   = ptr_info->channel;
  fmt.samplerate = ptr_info->sample_per_sec;
  fmt.bytepersec = ptr_info->sample_per_sec * (bits / 8) * channel;
  fmt.blockalign = bits / 8 * channel;
  fmt.bitswidth  = bits;
  ptr_wave_stream->Write( &fmt, sizeof(fmt) );

  // バッファ作成
  Array::Constant<U8> pcm_buf( sample_num * channel * (bits / 8) );
  Decode( block_info_table, &pcm_buf[0], 0, sample_num, false, nullptr );


  // データの書き込み
  const int bits_w = bits / 8;
  const int data_size = sample_num * channel * bits_w;
  ptr_wave_stream->Write( SwS("data"), 4 );
  ptr_wave_stream->Write( &data_size, 4 );
  ptr_wave_stream->Write( &pcm_buf[0], pcm_buf.GetSize() );

  //
  out_wave_data.SetupDataBuffer();

  // 
  return true;
}
#endif


#if (CPL_CSC_VERSION == 1)
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// ブロック単位でのデコード（CSC -> WAVE）
// 戻り値：読み込んだバイト数
// ※ in_block_tableは復号化済みのデータを渡すこと
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
int baseCscData::Decode(
    CscBlockInfo const* in_block_table,
    void*               out_ptr_buf,
    int                 in_sample_idx,
    int                 in_sample_num,
    int                 in_flag_loop,
    int*                out_ptr_next_idx ) const
{
//  CPL_OUTPUT( "baseCscData::Decode Start\n" );
  try {
    int loop_sample_cnt = in_sample_num; // 読み込み後、先頭から再読み込みする場合のカウンタ

    if( in_sample_idx + in_sample_num > m_csc_info.sample_num )
    {
      in_sample_num = m_csc_info.sample_num - in_sample_idx;
    }

    baseStream::SpRefCnt my_stream = GetDataStream();
    const int channel_num    = m_csc_info.channel;
    const int block_idx_base = in_sample_idx / CSC_BLOCK_SIZE * channel_num;
    const int block_idx_end  = (in_sample_idx + in_sample_num + CSC_BLOCK_SIZE - 1) / CSC_BLOCK_SIZE * channel_num;
    const int bit_shift      = 16 - CPL::Clamp<int>( m_csc_info.max_bit, 5, 16 );

    short* ptr_write_addr = (short*)out_ptr_buf;
  //  int sample_idx_end = Min( CSC_BLOCK_SIZE, ((in_sample_idx % CSC_BLOCK_SIZE) + in_sample_num) );
    const int sample_idx_end = in_sample_idx + in_sample_num;
    int sample_idx_cnt = in_sample_idx;

    short tmp_buf[ 2 ][ CSC_BLOCK_SIZE ];
    for( int block_idx = block_idx_base; block_idx < block_idx_end; block_idx += channel_num )
    {
      for( int ch = 0; ch < channel_num; ++ch )
      {
        const CscBlockInfo* ptr_block_info = &in_block_table[ block_idx + ch ];
        const int offset     = ptr_block_info->offset;
        const int size       = ptr_block_info->size;
        const int block_type = ptr_block_info->type;

        // 読み込み
        U8 tmp_csc_buf[ CSC_BLOCK_SIZE * 2 ];
        my_stream->SeekSet( offset );
        my_stream->Read( tmp_csc_buf, size );
        const U8* ptr_check_addr = tmp_csc_buf;
        const U8* ptr_end_addr   = ptr_check_addr + size;
        int value            = ptr_block_info->value;
        int idx              = 0;
        int sub_vec          = 0;
        int sub              = 0;
        int debug_count      = 0;

        while( ptr_check_addr < ptr_end_addr )
        {
          KeyInfo* ptr_key_info = (KeyInfo*)ptr_check_addr;
          int num = (ptr_key_info->num + 1) * 8;
          if( idx + num > CSC_BLOCK_SIZE )
          {
            throw 0;
//            num = (CSC_BLOCK_SIZE - idx) / 8;
          }
          ptr_check_addr += sizeof(KeyInfo);

          const int bit = ptr_key_info->bit;
          for( int i = 0; i < num; ++i )
          {
            if( block_type == 0 )
            {
              sub = zzlf_getBitArray( ptr_check_addr, bit, i );
            }
            else
            {
              sub_vec = zzlf_getBitArray( ptr_check_addr, bit, i );
              sub += sub_vec;
            }
            value += sub;
            if( bit_shift > 0 )
            {
              tmp_buf[ ch ][ idx ] = zzf_bitcnv_high( value, bit_shift );
            }
            else
            {
              tmp_buf[ ch ][ idx ] = value;
            }
            ++idx;
          }
          ptr_check_addr += ptr_key_info->bit * (ptr_key_info->num + 1);

          ++debug_count;
        }

        // 後処理（低ビット解像度では、フラットノイズ？が目立つ）
//        zzlf_flatAverage( tmp_buf[ ch ], 2 );

      }

      int idx     = sample_idx_cnt % CSC_BLOCK_SIZE;
      int idx_end = Min( CSC_BLOCK_SIZE, (sample_idx_end - block_idx * CSC_BLOCK_SIZE / channel_num) );
      sample_idx_cnt += idx_end - idx;
  //    sample_idx_cnt = sample_idx_end;
  //    sample_idx_end = block_idx * CSC_BLOCK_SIZE;
  //    if( sample_idx_end < (in_sample_idx + in_sample_num) ){
  //      sample_idx_end = in_sample_idx + in_sample_num;
  //    }

      // WAVE形式に変換
      for(; idx < idx_end; ++idx )
      {
        for( int ch = 0; ch < channel_num; ++ch )
        {
          *ptr_write_addr = tmp_buf[ ch ][ idx ];
          ++ptr_write_addr;
        }
      }
    }

    int total_read_size = (U8*)ptr_write_addr - (U8*)out_ptr_buf;
//    CPL_OUTPUT( SwS("total_read_size(%d)\n"), total_read_size );

//    CPL_OUTPUT( "baseCscData::Decode End\n" );
    if( total_read_size == 0 )
    {
      throw 0;
    }

    int next_sample_idx = sample_idx_cnt;

    if( in_flag_loop )
    {
      loop_sample_cnt -= in_sample_num;

      if( loop_sample_cnt > 0 )
      {
        total_read_size +=
          Decode( in_block_table, ptr_write_addr, 0, loop_sample_cnt, in_flag_loop, &next_sample_idx );
      }
    }

    if( out_ptr_next_idx )
    {
      *out_ptr_next_idx = next_sample_idx;
    }

    return total_read_size;
  }
  catch(...)
  {
    throw Exception( CPL_CSC_DECODE_ERROR, __FILE__, __LINE__ );
  }
}
#endif



#if (CPL_CSC_VERSION == 2)
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// version2 デコード
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
int baseCscData::Decode2( BaseWaveData& out_wave_data ) const
{
  Core::Stream_i_ const*
    ptr_csc_stream  = dynamic_cast<Core::Stream_i_ const*>(this);
  Core::Stream_i_*
    ptr_wave_stream = dynamic_cast<Core::Stream_i_*>(&out_wave_data);
  if( !ptr_wave_stream  ||  !ptr_csc_stream )
  {
    return false;
  }

  // 一時バッファに読み込み
  Core::MemoryStream buffer;
  buffer = *ptr_csc_stream;

  // 情報取得
  U8*               ptr_top_addr = (U8*)buffer.GetTopAddr();
  CscHeaderInfo2ex* ptr_info     = reinterpret_cast<CscHeaderInfo2ex*>(ptr_top_addr);
  const int         bits         = ptr_info->bits;
  const int         channel      = ptr_info->channel;
  const int         block_num    = ptr_info->block_num / channel;
  const int         sample_num   = ptr_info->sample_num;

  // ブロック情報を復号化
  Decryption(
    ptr_info->block_info,
    sizeof(ptr_info->block_info[0]) * block_num,
    m_mask,
    static_cast<int>(m_mask.GetLength()) );


  // WAVE情報の書き込み
  ptr_wave_stream->Release();
  ptr_wave_stream->Resize( ptr_info->wave_size );
  // ヘッダ
  ptr_wave_stream->Write( &WaveInfo::Head( ptr_info->wave_size-8 ), sizeof(WaveInfo::Head) );
  // fmtチャンク
  WaveInfo::ChunkFormat fmt;
  memcpy( fmt.tag, SwS("fmt "), 4 );
  fmt.size       = 16;
  fmt.format     = 0x0001;
  fmt.channels   = ptr_info->channel;
  fmt.samplerate = ptr_info->sample_per_sec;
  fmt.bytepersec = ptr_info->sample_per_sec * (bits / 8) * channel;
  fmt.blockalign = bits / 8 * channel;
  fmt.bitswidth  = bits;
  ptr_wave_stream->Write( &fmt, sizeof(fmt) );

  // バッファ作成
  Core::Array::Constant<U8> pcm_buf( sample_num * channel * (bits / 8) );
  Decode2_Range( ptr_info->block_info, &pcm_buf[0], 0, sample_num, false, nullptr );


  // データの書き込み
  const int bits_w = bits / 8;
  const int data_size = sample_num * channel * bits_w;
  ptr_wave_stream->Write( SwS("data"), 4 );
  ptr_wave_stream->Write( &data_size, 4 );
  ptr_wave_stream->Write( &pcm_buf[0], pcm_buf.GetSize() );

  //
  out_wave_data.SetupDataBuffer();

  return true;
}
#endif



#if (CPL_CSC_VERSION == 2)
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// version2 デコードメイン処理
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
size_t
  baseCscData::Decode2_Range(
    CscBlockInfo2 const*  in_block_info_table,
    void*                 out_ptr_buf,
    int                   in_sample_idx,
    int                   in_sample_num,
    int                   in_flag_loop,
    int*                  out_ptr_next_idx ) const
{
  try {
    int loop_sample_cnt = in_sample_num; // 読み込み後、先頭から再読み込みする場合のカウンタ

    if( in_sample_idx + in_sample_num > m_csc_info2.sample_num )
    {
      in_sample_num = m_csc_info2.sample_num - in_sample_idx;
    }

    Core::Stream_i_::shared_ptr
      my_stream = GetDataStream();
    const int channel_num    = m_csc_info2.channel;
    const int block_idx_base = in_sample_idx / CSC_BLOCK_SIZE * channel_num;
    const int block_idx_end  = (in_sample_idx + in_sample_num + CSC_BLOCK_SIZE - 1) / CSC_BLOCK_SIZE * channel_num;
    const int block_max_num  = m_csc_info2.block_num;

    short* ptr_write_addr = (short*)out_ptr_buf;
  //  int sample_idx_end = Min( CSC_BLOCK_SIZE, ((in_sample_idx % CSC_BLOCK_SIZE) + in_sample_num) );
    const int sample_idx_end = in_sample_idx + in_sample_num;
    int       sample_idx_cnt = in_sample_idx;

    Core::Array::MultiArray<short> tmp_pcm_buf( channel_num, CSC_BLOCK_SIZE + 2 );
    U8                             tmp_csc_buf[ CSC_BLOCK_SIZE * 4 ];

    for( int block_idx = block_idx_base;
         block_idx < block_idx_end;
         block_idx += channel_num )
    {
      for( int ch = 0; ch < channel_num; ++ch )
      {
        const CscBlockInfo2&  block_info = in_block_info_table[ block_idx + ch ];
        const int             offset = block_info.block_offset;
        const int             size   = block_info.block_size;

        // 読み込み
        my_stream->SeekSet( offset );
        my_stream->Read( tmp_csc_buf, size );
//        U8 const* bit_table_addr  = block_info.bits;
        // ※ ブロック最初の３つは前の予測データが不足しているので自前で計算
        short* ptr_pcm_addr = &tmp_pcm_buf[ ch ][ 2 ];
        ptr_pcm_addr[  0 ] = block_info.top_value;
        ptr_pcm_addr[ -1 ] = block_info.top_value;
        ptr_pcm_addr[ -2 ] = block_info.top_value;
        ++ptr_pcm_addr;

        U8 const* data_addr     = reinterpret_cast<U8 const*>(tmp_csc_buf);
        U8 const* data_addr_end = tmp_csc_buf + block_info.block_size;

//      CPL_OUTPUT( SwS("%d\n"), (int)block_info.top_value );

        while( data_addr < data_addr_end )
        {
          CscUnitInfo00 const* ptr_unit_info0 = reinterpret_cast<CscUnitInfo00 const*>(data_addr);
          CscUnitInfo11 const* ptr_unit_info1 = reinterpret_cast<CscUnitInfo11 const*>(data_addr);
          if( block_info.compress_type == 0  &&
              ptr_unit_info0->type     == 0 )
          {
            data_addr += sizeof(CscUnitInfo00);
            //
            const int bit = ptr_unit_info0->bits ? ptr_unit_info0->bits + 1 : 0;
            const int num = ptr_unit_info0->num * 8;

            if( bit > 16 )
            {
              int brk = true;
            }

            zzlf_getDecodeBuf(
              data_addr,
              0, num-1,
              bit,
              ptr_pcm_addr );

            data_addr += bit;
            ptr_pcm_addr += num;
          }
          else
          if( ptr_unit_info0->type == 1 )
          {
            data_addr += sizeof(CscUnitInfo10);
          }

          //
        }
      }

      int idx     = sample_idx_cnt % CSC_BLOCK_SIZE;
      int idx_end = Min( CSC_BLOCK_SIZE, (sample_idx_end - block_idx * CSC_BLOCK_SIZE / channel_num) );
      sample_idx_cnt += idx_end - idx;
  //    sample_idx_cnt = sample_idx_end;
  //    sample_idx_end = block_idx * CSC_BLOCK_SIZE;
  //    if( sample_idx_end < (in_sample_idx + in_sample_num) ){
  //      sample_idx_end = in_sample_idx + in_sample_num;
  //    }

      // WAVE形式に変換
      short* ptr_pcm_data = &tmp_pcm_buf[ 0 ][ 2 ];
      for(; idx < idx_end; ++idx )
      {
        for( int ch = 0; ch < channel_num; ++ch )
        {
          *ptr_write_addr = ptr_pcm_data[ ch * CSC_BLOCK_SIZE + idx ];
          ++ptr_write_addr;
        }
      }
    }

    size_t total_read_size = (U8*)ptr_write_addr - (U8*)out_ptr_buf;
//    CPL_OUTPUT( SwS("total_read_size(%d)\n"), total_read_size );

//    CPL_OUTPUT( "baseCscData::Decode End\n" );
    if( total_read_size == 0 )
    {
      throw 0;
    }

    int next_sample_idx = sample_idx_cnt;

    if( in_flag_loop )
    {
      loop_sample_cnt -= in_sample_num;

      if( loop_sample_cnt > 0 )
      {
        total_read_size +=
          Decode2_Range(
            in_block_info_table,
            ptr_write_addr,
            0,
            loop_sample_cnt,
            in_flag_loop,
            &next_sample_idx );
      }
    }

    if( out_ptr_next_idx )
    {
      *out_ptr_next_idx = next_sample_idx;
    }

    return total_read_size;
  }
  catch(...)
  {
    CPL_THROW( CPL_CSC_DECODE_ERROR );
  }
}
#endif


#if (CPL_CSC_VERSION == 1)
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// WAVE情報設定
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void baseCscData::zzf_setWaveInfo( const BaseWaveData& in_wave_buf, int in_max_bit )
{
  // WAVE情報取得
  const WaveInfo& wave_info = in_wave_buf.GetWaveInfo();
  const WaveInfo::ChunkOffs* ptr_chunk_fmt  = wave_info.FindChunk( "fmt " );
  const WaveInfo::ChunkOffs* ptr_chunk_data = wave_info.FindChunk( "data" );
  if( !ptr_chunk_fmt  ||  !ptr_chunk_data )
  {
    return;
  }

  //
  auto* wfex = in_wave_buf.GetWaveInfo().GetWaveFormatEX();
  // 情報設定
  ::memset( &m_csc_info, 0, sizeof(m_csc_info) );
  *(int*)m_csc_info.type    = 'CACD';
  m_csc_info.bits           = (char)wfex->wBitsPerSample;
  m_csc_info.channel        = (char)wfex->nChannels;
  m_csc_info.sample_per_sec = wfex->nSamplesPerSec;
  m_csc_info.wave_size      = ptr_chunk_data->offset + 8 + ptr_chunk_data->size; // WAVEファイル全体のサイズ
//  m_csc_info.wave_data_size = ptr_chunk_data->size; // WAVEファイルのデータ部分のサイズ
  m_csc_info.sample_num     = ptr_chunk_data->size / (m_csc_info.bits / 8) / m_csc_info.channel;
  m_csc_info.max_bit        = in_max_bit;
}
#endif


#if (CPL_CSC_VERSION == 2)
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// WAVE情報を設定 ver2
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void baseCscData::zzf_setWaveInfo2( BaseWaveData const& in_wave_buf )
{
  // WAVE情報取得
  const WaveInfo& wave_info = in_wave_buf.GetWaveInfo();
  const WaveInfo::ChunkOffs* ptr_chunk_fmt  = wave_info.FindChunk( "fmt " );
  const WaveInfo::ChunkOffs* ptr_chunk_data = wave_info.FindChunk( "data" );
  if( !ptr_chunk_fmt  ||  !ptr_chunk_data )
  {
    return;
  }

  //
  auto* wfex = in_wave_buf.GetWaveInfo().GetWaveFormatEX();
  // 情報設定
  ::memset( &m_csc_info2, 0, sizeof(m_csc_info2) );
  *(int*)m_csc_info2.type    = 'CACD';
  m_csc_info2.bits           = (char)wfex->wBitsPerSample;
  m_csc_info2.channel        = (char)wfex->nChannels;
  m_csc_info2.sample_per_sec = wfex->nSamplesPerSec;
  m_csc_info2.wave_size      = ptr_chunk_data->offset + 8 + ptr_chunk_data->size; // WAVEファイル全体のサイズ
  m_csc_info2.sample_num     = ptr_chunk_data->size / (m_csc_info2.bits / 8) / m_csc_info2.channel;
}
#endif


#if (CPL_CSC_VERSION == 1)
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// 値の設定
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void baseCscData::zzf_setValue(
      MemoryStreamCell& out_csc_buf,
      int               in_index,
      int               in_channel,
      int               in_value )
{
  in_index = in_index * m_csc_info.channel + in_channel;
#ifdef CPL_DEBUG
  int byte_idx = in_index * m_csc_info.bits / 8;
  int size = out_csc_buf.GetSize();
  if( byte_idx >= size )
  {
    int brk = true;
  }
#endif
  switch( m_csc_info.bits )
  {
  case 8:
    ((U8*)out_csc_buf.GetTopAddr())[ in_index ] = in_value + 128;
    break;
  case 16:
    ((short*)out_csc_buf.GetTopAddr())[ in_index ] = in_value;
    break;
  }
}
#endif


#if (CPL_CSC_VERSION == 1)
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// 値の取得
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
int baseCscData::zzf_getValue(
      const MemoryStreamCell& in_csc_buf,
      int                     in_index,
      int                     in_channel )
{
  in_index = in_index * m_csc_info.channel + in_channel;
  switch( m_csc_info.bits )
  {
  case 8:
    return int(((U8*)in_csc_buf.GetTopAddr())[ in_index ]) - 128;

  case 16:
    return ((short*)in_csc_buf.GetTopAddr())[ in_index ];
  }
  return 0;
}
#endif


#if (CPL_CSC_VERSION == 1)
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// 指定バッファのピークポイントのキーフレームリストを取得
// allowed_value : 許容値
// align_check   : ブロックの両端にキーフレームを設定するかどうか
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void baseCscData::zzf_getKeyList(
      Array::Dynamic<short>&  in_buf,
      Array::Dynamic<int>*    out_ptr_key_list,
      int                     in_idx0,
      int                     in_idx1,
      float                   in_allowed_value,
      bool                    in_align_check )
{
  Array::Dynamic<int>& key_table = *out_ptr_key_list;
  key_table.Clear();

  // チェックポイント配列
  const int sample_num = in_buf.GetNum();
  const int block_num = (sample_num + CSC_BLOCK_SIZE - 1) / CSC_BLOCK_SIZE;
  const int sample_ex_num = block_num * CSC_BLOCK_SIZE;
  static Array::Dynamic<U8> check_point( sample_ex_num, 0 );
  check_point.Resize( sample_ex_num );

  // クリア
  for( int idx = in_idx0; idx < in_idx1; ++idx )
  {
    check_point[ idx ] = 0;
  }

//  U8 check[ CSC_BLOCK_SIZE ] = {0};
//  // 先端及び終端にチェックは必ず必要
//  check[ 0            ] = true;
//  check[ CSC_BLOCK_SIZE-1 ] = true;

  // ピークチェックポイントの設定
  int value = in_buf[ in_idx0 ];
  int value_prev;
  int flag = 0;
  const int loop_end = Min( sample_num-1, in_idx1 );
  for( int idx = in_idx0+1; idx < loop_end; ++idx )
  {
    value_prev = value;
    value = in_buf[ idx ];

    int sign = CPL::Sign( value - value_prev );
    if( sign != flag )
    {
      check_point[ idx - 1 ] = true;
//      check[ idx - in_idx0 ] = true;
      flag = sign;
    }
  }
  // 先頭と終端には必ずチェックポイントを設定しておく
  check_point[ 0 ] = true;
  check_point[ sample_ex_num-1 ] = true;

  // ブロックの両端に設定する場合
  if( in_align_check )
  {
    for( int idx = in_idx0; idx < in_idx1; idx += CSC_BLOCK_SIZE )
    {
      check_point[ idx ] = true;
      check_point[ idx + CSC_BLOCK_SIZE - 1 ] = true;
    }
  }

  // キーフレーム化
  value = check_point[ in_idx0 ];
  int old_cp = -1;
  int f_throw = false; // 一つ前のチェックポイントをスルーしたかどうか
  key_table.AddEnd( in_idx0 ); // 先頭はかならず必要
  for( int idx = in_idx0 + 1; idx < in_idx1; ++idx )
  {
    value_prev = value;
    value      = in_buf[ idx ];
    if( check_point[ idx ] )
    {
      if( CPL::Abs(value) >= in_allowed_value )
      {
        if( f_throw ) // 以前のチェックポイントをスルーしていたら
        {
          // サンプリングデータが反転もしくは０になるまで戻る
          if( value < 0 )
          {
            for( int i = idx-1; i > in_idx0; --i )
            {
              if( in_buf[ i ] >= 0 )
              {
                key_table.AddEnd( idx );
                break;
              }
            }
          }
          else
          {
            for( int i = idx-1; i > in_idx0; --i )
            {
              if( in_buf[ i ] <= 0 )
              {
                key_table.AddEnd( idx );
                break;
              }
            }
          }
        }
        key_table.AddEnd( idx );
        f_throw = false;
      }
      else
      {
        f_throw = true;
      }
      old_cp = idx;
    }
  }
}
#endif


#if (CPL_CSC_VERSION == 1)
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// 小音量カット処理
// io_org_buf  : WAVEのデータ部分をさすバッファ
// in_zero_limit : 0.0 - 100.0%
// CheckPoint : Array::Dynamic<int>*[ チャンネル数 ]　※ピークチェックポイント
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void baseCscData::zzf_minCut( Array::Dynamic<short>& io_org_buf, float in_zero_limit )
{
  const int bits    = m_csc_info.bits;
  const int channel = m_csc_info.channel;
  int   max = 1 << bits;
  int   limit = int((float)max * (CPL::Clamp(in_zero_limit, 0.0f, 100.0f) / 100.0f));
  int   sample_num = io_org_buf.GetNum();
//  int   flag = 0;
//  int   value = 0;
//  int   value2 = 0;

  Array::Dynamic<int> key_frame;
  key_frame.Reserve( sample_num );
  
  // ピークチェックポイントの設定
  zzf_getKeyList( io_org_buf, &key_frame, 0, sample_num, 0.0f, true );

  // 小音量のカット
  int  cp_st = 0;
  int  cp_ed = 0;
  int  key_num = key_frame.GetNum();
  int  zero_check = 0; // 検査中のところから一番最近、符号が切り替わったときのインデックス
  int  min_check  = 0; // 符号切り替わり後の、次の切り替わりまでの最小値のインデックス
  int  max_check  = 0; // 符号切り替わり後の、次の切り替わりまでの最大値のインデックス
  for( int key_idx = 1; key_idx < key_num; ++key_idx )
  {
    cp_st = key_frame[ key_idx-1 ];
    cp_ed = key_frame[ key_idx ];
    int val_st = io_org_buf[ cp_st ];
    int val_ed = io_org_buf[ cp_ed ];
    int min = CPL::Min( val_st, val_ed );
    int max = CPL::Max( val_st, val_ed );
    // 最小判定
    if( val_ed < io_org_buf[ min_check ] )
    {
      min_check = cp_ed;
    }
    // 最大判定
    else
    if( val_ed > io_org_buf[ max_check ] )
    {
      max_check = cp_ed;
    }

    // ０ラインクロスチェック
    if( min <= 0  &&  0 <= max )
    {
      // ０クロスするインデックスを検索
      const int flag = CPL::Sign( val_st );
      int tmp_zero_idx;
      for( tmp_zero_idx = cp_st+1; tmp_zero_idx <= cp_ed; ++tmp_zero_idx )
      {
        if( flag != CPL::Sign( io_org_buf[ tmp_zero_idx ] ) )
        {
          break;
        }
      }
      // マイナス分平坦化判定
      if( -limit <= io_org_buf[ min_check ]  &&  io_org_buf[ min_check ] < 0 )
      {
        for( int idx = zero_check; idx <= tmp_zero_idx; ++idx )
        {
          if( io_org_buf[ idx ] < 0 )
          {
            io_org_buf[ idx ] = 0;
          }
        }
      }
      // プラス分平坦化判定
      if( 0 < io_org_buf[ max_check ]   &&  io_org_buf[ max_check ] <= limit )
      {
        for( int idx = zero_check; idx <= tmp_zero_idx; ++idx )
        {
          if( io_org_buf[ idx ] > 0 )
          {
            io_org_buf[ idx ] = 0;
          }
        }
      }
      zero_check = tmp_zero_idx;
      min_check = cp_ed;
      max_check = cp_ed;
    }
/*    // マイナス値平坦化
    if( CPL::Abs(min) <= limit ){
      for( int idx = cp_st; idx < cp_ed; ++idx ){
        if( io_org_buf[ idx ] < 0 ){
          io_org_buf[ idx ] = 0;
        }
      }
    }
    // プラス値平坦化
    if( max <= limit ){
      for( int idx = cp_st; idx < cp_ed; ++idx ){
        if( io_org_buf[ idx ] > 0 ){
          io_org_buf[ idx ] = 0;
        }
      }
    }*/
  }
  // 最後の値の調整
  int value = io_org_buf[ sample_num-1 ];
  if( CPL::Abs(value) < limit )
  {
    io_org_buf[ sample_num-1 ] = 0;
  }
}
#endif


#if (CPL_CSC_VERSION == 1)
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// 指定ブロックの差分バッファを取得
// 戻り値：圧縮後のバッファサイズ
// out_pBuf - 取得するバッファのアドレス。CSC_BLOCK_SIZE * 2バイト以上のバッファを渡すこと。
// type     - 0 : 次のサンプルへの差ベクトルデータ
//            1 : 次のサンプルへの差ベクトルの差のデータ
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
int baseCscData::zzf_enc_getCompressBlockBuffer(
  short_cptr    in_org_buf,
  U8            out_pBuf[ CSC_BLOCK_SIZE*2 ],
  int           in_sample_num,
  COMPRESS_TYPE in_type )
{
  static const int bit_check_tbl[17][2] = {
         0,     0,
        -1,     0,
        -2,     1,
        -4,     3,
        -8,     7,
       -16,    15,
       -32,    31,
       -64,    63,
      -128,   127,
      -256,   255,
      -512,   511,
     -1024,  1023,
     -2048,  2047,
     -4096,  4095,
     -8192,  8191,
    -16384, 16383,
    -32768, 32767,
  };

  if( in_sample_num > CSC_BLOCK_SIZE )
  {
    in_sample_num = CSC_BLOCK_SIZE;
  }

  // データバッファへのポインタ
  short* in_ptr_data_buf;

  // 差分ベクトルバッファを設定
  short sub_buf[ CSC_BLOCK_SIZE ];
//  Array::Constant<short> sub_buf( CSC_BLOCK_SIZE );
  sub_buf[ 0 ] = 0;
  for( int idx = 1; idx < in_sample_num; ++idx )
  {
    sub_buf[ idx ] = in_org_buf[ idx ] - in_org_buf[ idx - 1 ];
  }

  // 差分ベクトルの差分バッファを設定
  short vec_sub_buf[ CSC_BLOCK_SIZE ];
  if( in_type == CT_SIMPLE )
  {
    in_ptr_data_buf = sub_buf;
  }
  else
  if( in_type == 1 )
  {
    vec_sub_buf[ 0 ] = 0;
    for( int idx = 1; idx < in_sample_num; ++idx )
    {
      vec_sub_buf[ idx ] = sub_buf[ idx ] - sub_buf[ idx-1 ];
    }

    in_ptr_data_buf = vec_sub_buf;
  }

  // ビットバッファを設定
  char bit_buf[ CSC_BLOCK_SIZE ];
//  Array::Constant<char> bit_buf( CSC_BLOCK_SIZE );
  bit_buf[ 0 ] = 0;
  int sub_vec = in_ptr_data_buf[ 0 ];
  int bit;
  for( int idx = 1; idx < in_sample_num; ++idx )
  {
    sub_vec = in_ptr_data_buf[ idx ];
    for( bit = 0; bit < MAX_BIT; ++bit )
    {
      if( bit_check_tbl[ bit ][ 0 ] <= sub_vec  &&  sub_vec <= bit_check_tbl[ bit ][ 1 ] )
      {
        break;
      }
    }
//    bits += (bits >= 0) + 1; // マイナスフラグの分と、補正のため＋１
    bit_buf[ idx ] = bit;
  }

  // 8個単位の最大ビット数を求める
  U8 max_bit8[ CSC_BLOCK_SIZE / 8 ] = { 0 };
  for( int idx = 0; idx < in_sample_num; ++idx )
  {
    int max_bit_idx = idx / 8;
    if( max_bit8[ max_bit_idx ] < bit_buf[ idx ] )
    {
      max_bit8[ max_bit_idx ] = bit_buf[ idx ];
    }
  }

  // 同一のものをまとめる
  Array::Static<short2> count_buf( in_sample_num / 8 ); // x:bit  y:count
  int debug_count = 0;
  int check_bit = -1;
  int old_check_bit;
  for( int max_bit_idx = 0;
       max_bit_idx < (in_sample_num / 8);
       ++max_bit_idx )
  {
    old_check_bit = check_bit;
    check_bit = max_bit8[ max_bit_idx ];

    if( old_check_bit == check_bit )
    {
      count_buf.GetEnd()->y++;
      if( count_buf.GetEnd()->y == 15 )
      {
        check_bit = -1; // 
      }
      ++debug_count;
    }
    else
    {
      count_buf.AddEnd( short2(check_bit, 1) );
      ++debug_count;
    }

    if( debug_count > CSC_BLOCK_SIZE/8 )
    {
      throw 0;
    }
  }

  // バッファ設定
  const int key_num = count_buf.GetNum();
//  U8 key_buf[ CSC_BLOCK_SIZE ] = { 0 };
  Array::Constant<U8> key_buf( in_sample_num * 3 ); // 余分目に（本来、in_sample_num * 2を超えてはいけない）
  U8* ptr_key_buf_addr = &key_buf[0]; // 書き込みアドレス
  int data_idx = 0;
  for( int key_idx = 0; key_idx < key_num; ++key_idx )
  {
    short2 cnt_info = count_buf[ key_idx ];

    *(KeyInfo*)ptr_key_buf_addr =
      KeyInfo( static_cast<U8>(cnt_info.x),
               static_cast<U8>(cnt_info.y-1) );
    ptr_key_buf_addr += sizeof(KeyInfo);
    int bit = cnt_info.x;
    int num = cnt_info.y * 8;
    if( bit == 0 )
    {
      data_idx += num;
    }
    else
    {
      for( int i = 0; i < num; ++i )
      {
        zzlf_setBitArray( ptr_key_buf_addr, bit, i, in_ptr_data_buf[ data_idx ] );
        ++data_idx;
      }
      ptr_key_buf_addr += cnt_info.y * bit;
    }
  }

  int data_size = ptr_key_buf_addr - &key_buf[0];

  // バッファへコピー
  ::memcpy( out_pBuf, &key_buf[0], Min(data_size, in_sample_num*2) );

  return data_size;
}
#endif




/*
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// 補間データを作成
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void baseCscData::_enc_compress( Array::Dynamic<short>* io_OrgBufList )
{
  const int bits          = m_info.bits;
  const int channel       = m_info.channel;
//  const int sample_num    = m_info.wave_data_size / (bits / 8) / channel; // １チャンネルあたりのサンプリング数
  const int sample_num    = m_info.sample_num;
  const int block_num     = (sample_num + CSC_BLOCK_SIZE - 1) / CSC_BLOCK_SIZE; // １チャンネルあたりのブロック数
  const int sample_ex_num = block_num * CSC_BLOCK_SIZE; // ブロック化拡張後のサンプリング数

  for( int ch = 0; ch < channel; ++ch ){
    // 領域拡張
    io_OrgBufList[ ch ].Resize( sample_ex_num );
    // 拡張領域の初期化
    for( int idx = sample_num; idx < sample_ex_num; ++idx ){
      io_OrgBufList[ ch ][ idx ] = 0;
    }
  }

  // ＊＊＊＊＊　ＤＳＣファイルの作成　＊＊＊＊＊
  // 情報書き込み　※ この時点ではまだ情報は不完全なので、後から修正する
  m_buffer.Release();
  m_buffer.Write( &m_info, sizeof(m_info) - sizeof(CscBlockInfo) );
  // ブロックデータ書き込み
  const int total_block_num = block_num * channel;
  // ブロック情報領域書き込み（この時点では情報不足のため後で設定）
  Array::Constant<CscBlockInfo> BlockDummy( total_block_num, CscBlockInfo() );
//  m_buffer.Write( &_BlockInfoTbl[ 0 ], sizeof(CscBlockInfo) * total_block_num );
  m_buffer.Write( &BlockDummy[0], BlockDummy.GetSize() );

  // キーデータ書き込み
  int offset_cnt = m_buffer.GetPos();
  for( int block_idx = 0; block_idx < block_num; ++block_idx ){
    for( int ch = 0; ch < channel; ++ch ){
      // キー情報書き込み
      int size = zzf_enc_getCompressBlockBuffer( io_OrgBufList[ ch ], block_idx, &m_buffer );

      // ブロック情報変更
      Info* ptr_info = (Info*)m_buffer.GetTopAddr();
      ptr_info->block_offs[ block_idx * channel + ch ].offset = offset_cnt;
      ptr_info->block_offs[ block_idx * channel + ch ].value  = io_OrgBufList[ ch ][ block_idx * CSC_BLOCK_SIZE ]; // ブロック先頭の値を設定
      ptr_info->block_offs[ block_idx * channel + ch ].size   = size;

      // 次のオフセット設定
      offset_cnt += size;
    }
  }

  // 残りの情報を設定
  m_info.block_num = total_block_num;
  m_info.dsc_size  = m_buffer.GetSize() - 8;

  // 実バッファの方も設定
  Info* ptr_info = (Info*)m_buffer.GetTopAddr();
  ptr_info->block_num = total_block_num;
  ptr_info->dsc_size  = m_info.dsc_size;
}*/

/*
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// 補間処理
// 戻り値　：　補間タイプ
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
short2 baseCscData::_enc_interpolate( Array::Dynamic<short>& in_OrgBuf,
             int idx0, int idx1, bool method,
             Array::Dynamic<short>* out_pErrBuf )
{
  int good_r0 = 0, good_r1 = 0;
  int error_min = 0x7fffffff;
  int error_cnt = _enc_interpolate_Range( in_OrgBuf, idx0, idx1, method, out_pErrBuf, 0, 0 );
  if( error_cnt < error_min ){
    error_min = error_cnt;
  }

  for( int r0 = 0; r0 <= 5; ++r0 ){
    for( int r1 = r0 + 1; r1 <= 6; ++r1 ){
      error_cnt = _enc_interpolate_Range( in_OrgBuf, idx0, idx1, method, out_pErrBuf, r0*30, r1*30 );
      if( error_cnt < error_min ){
        error_min = error_cnt;
        good_r0 = r0;
        good_r1 = r1;
      }
    }
  }
  return short2( good_r0, good_r1 );
}
*/
/*
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// 線形補間
// 戻り値：累積誤差
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
int baseCscData::_enc_interpolate_Linear( Array::Dynamic<short>& in_OrgBuf,
                int idx0, int idx1, bool method,
                    Array::Dynamic<short>* out_pErrBuf )
{
  int error_cnt = 0;
  int value = 0;
  int sub = idx1 - idx0;
  int val_prev = in_OrgBuf[ idx0 ];
  int val_next = in_OrgBuf[ idx1 ];
  int error_val = 0;

  for( int idx = idx0+1; idx <= idx1; ++idx ){
    value = val_prev + (val_next - val_prev) * (idx - idx0) / sub;
    error_val = value - in_OrgBuf[ idx ];
//    error_cnt += CPL::Abs( error_val );
    if( error_cnt < error_val ){
      error_cnt = error_val;
    }
    if( out_pErrBuf ){
      (*out_pErrBuf)[ idx ] = error_val;
    }
  }

  return error_cnt;
}*/

/*
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// cos補間
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
int baseCscData::_enc_interpolate_Range( Array::Dynamic<short>& in_OrgBuf,
                     int idx0, int idx1, bool method,
                     Array::Dynamic<short>* out_pErrBuf,
                     int deg_st, int deg_ed )
{
  int error_cnt = 0;
  int error_val = 0;
  int value = 0;
  int idx_sub = idx1 - idx0;

  if( deg_st == deg_ed ){
    int idx_sub = idx1 - idx0;
    int val_prev = in_OrgBuf[ idx0 ];
    int val_next = in_OrgBuf[ idx1 ];

    for( int idx = idx0+1; idx <= idx1; ++idx ){
      value = val_prev + (val_next - val_prev) * (idx - idx0) / idx_sub;
      error_val = value - in_OrgBuf[ idx ];
      if( error_cnt < error_val ){
        error_cnt = error_val;
      }
      if( out_pErrBuf ){
        (*out_pErrBuf)[ idx ] = error_val;
      }
    }
  }
  else
  {
    int val_st = in_OrgBuf[ idx0 ]; // 始端値
    int val_ed = in_OrgBuf[ idx1 ]; // 終端値
    int val_sub = val_ed - val_st;  // 差値
    float pw_st = CPL::Math::Cos( deg_st );
    float pw_ed = CPL::Math::Cos( deg_ed );
    float pw_sub = pw_ed - pw_st;
    if( pw_sub == 0 ){
      pw_sub = 0.000001f;
    }
  //  float pw_sub_abs = CPL::Abs( pw_sub );
    float pw_correct = val_sub / pw_sub;
    
    for( int idx = idx0+1; idx <= idx1; ++idx ){
      float r = CPL::toRAD( (deg_st + (idx - idx0) * (deg_ed - deg_st)) / idx_sub );
  //    value = base_value + power * cosf( r );
      value = val_st + (cosf(r) - pw_st) * pw_correct;
      error_val = value - in_OrgBuf[ idx ];
      error_cnt += CPL::Abs( error_val );
  //    if( error_cnt < error_val ){
  //      error_cnt = error_val;
  //    }
      if( out_pErrBuf ){
        (*out_pErrBuf)[ idx ] = error_val;
      }
    }
  }

  return error_cnt;
}
*/
/*
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// sin補間
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
int baseCscData::_interpolate_Sin( Array::Dynamic<short>& in_OrgBuf,
               int idx0, int idx1, bool method,
               Array::Dynamic<short>* out_pErrBuf )
{
  int error_cnt = 0;
  int value = 0;
  int idx_sub = idx1 - idx0;
  int val_prev = in_OrgBuf[ idx0 ];
  int val_next = in_OrgBuf[ idx1 ];
  int error_val = 0;
  
  const int r_max = 90;
  int power = val_next - val_prev;
  int base_value = val_prev - power;

  for( int idx = idx0+1; idx <= idx1; ++idx ){
    float r = CPL::toRAD( (idx - idx0) * r_max / idx_sub );
    value = val_prev + power * cosf( r );
    error_val = value - in_OrgBuf[ idx ];
    error_cnt += CPL::Abs( error_val );
    if( out_pErrBuf ){
      (*out_pErrBuf)[ idx ] = error_val;
    }
  }

  return error_cnt;
}*/


/*
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// CSCデータ領域を取得
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
MemoryStreamCell::SmPtr baseCscData::GetDataStream() {
  int offs = (int)(m_info.block_offs + m_info.block_num * m_info.channel + 1);
  int size = m_info.dsc_size - offs;
  m_pDscDataBuf = m_buffer.CreateStreamCell( offs, size );
  return m_pDscDataBuf;
}*/

/*
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// CSC -> WAVE  線形補間
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void baseCscData::_dec_interpolate_Linear( Array::Dynamic<short>& out_Buf, int val_st, int val_ed, int cp_st, int cp_ed, int f_sub ){
  int idx_sub = cp_ed - cp_st;

}*/
/*
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// CSC -> WAVE  COS補間
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void baseCscData::_dec_interpolate_Range( Array::Dynamic<short>& out_Buf, int val_st, int val_ed, int cp_st, int cp_ed, int deg_st, int deg_ed, int f_sub ){
  int value = 0;
  int idx_sub = cp_ed - cp_st;
  int val_sub = val_ed - val_st;  // 差値

  if( deg_st == deg_ed ){
    if( f_sub ){
      for( int idx = cp_st + 1; idx <= cp_ed; ++idx ){
        out_Buf[ idx ] -= val_st + (val_ed - val_st) * (idx - cp_st) / idx_sub;
      }
    }
    else
    {
      for( int idx = cp_st + 1; idx <= cp_ed; ++idx ){
        out_Buf[ idx ] = val_st + (val_ed - val_st) * (idx - cp_st) / idx_sub;
      }
    }
  }
  else
  {
    float pw_st = CPL::Math::Cos( deg_st );
    float pw_ed = CPL::Math::Cos( deg_ed );
    float pw_sub = pw_ed - pw_st;
  //  float pw_sub_abs = CPL::Abs( pw_sub );
    if( pw_sub == 0.0f ){
      pw_sub = 0.000001f;
    }
    float pw_correct = val_sub / pw_sub;
    
    if( f_sub ){
      for( int idx = cp_st+1; idx <= cp_ed; ++idx ){
        float r = CPL::toRAD( (deg_st + (idx - cp_st) * (deg_ed - deg_st)) / idx_sub );
        out_Buf[ idx ] -= val_st + (cosf(r) - pw_st) * pw_correct;
      }
    }
    else
    {
      for( int idx = cp_st+1; idx <= cp_ed; ++idx ){
        float r = CPL::toRAD( (deg_st + (idx - cp_st) * (deg_ed - deg_st)) / idx_sub );
        out_Buf[ idx ] = val_st + (cosf(r) - pw_st) * pw_correct;
      }
    }
  }
}*/





// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// CSC バッファ コンストラクタ
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
CscMemoryStream::CscMemoryStream( str_cptr in_path, Core::StringA const& in_mask )
  : baseCscData( in_mask )
{
  Core::FileStream File( in_path, Core::FileStream::kREAD_BINARY );
  File >> *this;

  SetupDataBuffer();
}

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// CSC バッファ　コピーコンストラクタ
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
CscMemoryStream::CscMemoryStream( const CscMemoryStream& db )
  : baseCscData( db.GetMask() )
  , Core::MemoryStream( db )
  , m_sp_data_stm( db.m_sp_data_stm )
{
#if (CPL_CSC_VERSION == 1)
  m_csc_info = db.m_csc_info;
#elif (CPL_CSC_VERSION == 2)
  m_csc_info2 = db.m_csc_info2;
#endif
}

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// CSC バッファ 構築
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void CscMemoryStream::SetupDataBuffer()
{
#if (CPL_CSC_VERSION == 1)
  ::memcpy( &m_csc_info, GetTopAddr(), sizeof(m_csc_info) );
  
  // 部分ストリームを作成
  int offset = sizeof(CscHeaderInfo) + sizeof(CscBlockInfo) * (m_csc_info.block_num - 1);
  int size   = m_csc_info.dvac_size + 8;
  m_sp_data_stm = CreateStreamCell( offset, size - offset );

#elif (CPL_CSC_VERSION == 2)
  ::memcpy( &m_csc_info2, GetTopAddr(), sizeof(m_csc_info2) );

  // 部分ストリームを作成
  const int block_num = m_csc_info2.block_num;
  const int offset = sizeof(m_csc_info2) + sizeof(CscBlockInfo2) * block_num;
  const int size   = m_csc_info2.csc_size;
  m_sp_data_stm = CreateStreamCell( offset, size - offset );

#endif

}




// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// CSC ファイルストリーム コンストラクタ
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
CscFileStream::CscFileStream
( str_cptr           in_path
, bool               in_flag_read
, _in<Core::StringA> in_mask
) : baseCscData( in_mask )
  , Core::FileStream( in_path, in_flag_read ? kREAD_BINARY : kWRITE_BINARY )
{
  SetupDataBuffer();
}

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// CSC ファイルストリーム　コピーコンストラクタ
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
CscFileStream::CscFileStream( cref<CscFileStream> in_csc_stream )
  : baseCscData( in_csc_stream.GetMask() )
  , Core::FileStream( in_csc_stream )
  , m_ptr_data_stream( in_csc_stream.m_ptr_data_stream ) 
{
#if (CPL_CSC_VERSION == 1)
  m_csc_info = in_csc_stream.m_csc_info;
#elif (CPL_CSC_VERSION == 2)
  m_csc_info2 = in_csc_stream.m_csc_info2;
#endif
}


// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// CSC ファイルストリーム データ設定
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void CscFileStream::SetupDataBuffer()
{
  SeekSet( 0 );

#if (CPL_CSC_VERSION == 1)
  Read( &m_csc_info, sizeof(m_csc_info) );
  
  // 部分ストリームを作成
  int offset = sizeof(CscHeaderInfo) + sizeof(CscBlockInfo) * (m_csc_info.block_num - 1);
  int size   = m_csc_info.dvac_size + 8;
  m_ptr_data_stream = CreateStreamCell( offset, size - offset );

#elif (CPL_CSC_VERSION == 2)
  Read( &m_csc_info2, sizeof(m_csc_info2) );
  
  // 部分ストリームを作成
  const int block_num = m_csc_info2.block_num;
  const int offset = sizeof(m_csc_info2) + sizeof(CscBlockInfo2) * block_num;
  const int size   = m_csc_info2.csc_size;
  m_ptr_data_stream = CreateStreamCell( offset, size - offset );

#endif

}










// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// ビット配列の一要素を取得
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
int zzlf_getBitArray( const U8* in_ptr_buf, int in_bit, int in_index )
{
  switch( in_bit )
  {
  case  0: return 0;//bit_arr_mng<int, 1>( (void*)in_ptr_buf ).get( in_index );
  case  1: return Core::Array::bit_arr_mng<int, 1>( (void*)in_ptr_buf ).get( in_index );
  case  2: return Core::Array::bit_arr_mng<int, 2>( (void*)in_ptr_buf ).get( in_index );
  case  3: return Core::Array::bit_arr_mng<int, 3>( (void*)in_ptr_buf ).get( in_index );
  case  4: return Core::Array::bit_arr_mng<int, 4>( (void*)in_ptr_buf ).get( in_index );
  case  5: return Core::Array::bit_arr_mng<int, 5>( (void*)in_ptr_buf ).get( in_index );
  case  6: return Core::Array::bit_arr_mng<int, 6>( (void*)in_ptr_buf ).get( in_index );
  case  7: return Core::Array::bit_arr_mng<int, 7>( (void*)in_ptr_buf ).get( in_index );
  case  8: return Core::Array::bit_arr_mng<int, 8>( (void*)in_ptr_buf ).get( in_index );
  case  9: return Core::Array::bit_arr_mng<int, 9>( (void*)in_ptr_buf ).get( in_index );
  case 10: return Core::Array::bit_arr_mng<int,10>( (void*)in_ptr_buf ).get( in_index );
  case 11: return Core::Array::bit_arr_mng<int,11>( (void*)in_ptr_buf ).get( in_index );
  case 12: return Core::Array::bit_arr_mng<int,12>( (void*)in_ptr_buf ).get( in_index );
  case 13: return Core::Array::bit_arr_mng<int,13>( (void*)in_ptr_buf ).get( in_index );
  case 14: return Core::Array::bit_arr_mng<int,14>( (void*)in_ptr_buf ).get( in_index );
  case 15: return Core::Array::bit_arr_mng<int,15>( (void*)in_ptr_buf ).get( in_index );
  case 16: return Core::Array::bit_arr_mng<int,16>( (void*)in_ptr_buf ).get( in_index );
  default: return Core::Array::bit_arr_mng<int,16>( (void*)in_ptr_buf ).get( in_index );
  }
}


// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// ビット配列の一要素を設定
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void zzlf_setBitArray( U8* in_ptr_buf, int in_bit, int in_index, int val )
{
  switch( in_bit )
  {
  case  1: Core::Array::bit_arr_mng<int, 1>( (void*)in_ptr_buf ).set( in_index, val ); break;
  case  2: Core::Array::bit_arr_mng<int, 2>( (void*)in_ptr_buf ).set( in_index, val ); break;
  case  3: Core::Array::bit_arr_mng<int, 3>( (void*)in_ptr_buf ).set( in_index, val ); break;
  case  4: Core::Array::bit_arr_mng<int, 4>( (void*)in_ptr_buf ).set( in_index, val ); break;
  case  5: Core::Array::bit_arr_mng<int, 5>( (void*)in_ptr_buf ).set( in_index, val ); break;
  case  6: Core::Array::bit_arr_mng<int, 6>( (void*)in_ptr_buf ).set( in_index, val ); break;
  case  7: Core::Array::bit_arr_mng<int, 7>( (void*)in_ptr_buf ).set( in_index, val ); break;
  case  8: Core::Array::bit_arr_mng<int, 8>( (void*)in_ptr_buf ).set( in_index, val ); break;
  case  9: Core::Array::bit_arr_mng<int, 9>( (void*)in_ptr_buf ).set( in_index, val ); break;
  case 10: Core::Array::bit_arr_mng<int,10>( (void*)in_ptr_buf ).set( in_index, val ); break;
  case 11: Core::Array::bit_arr_mng<int,11>( (void*)in_ptr_buf ).set( in_index, val ); break;
  case 12: Core::Array::bit_arr_mng<int,12>( (void*)in_ptr_buf ).set( in_index, val ); break;
  case 13: Core::Array::bit_arr_mng<int,13>( (void*)in_ptr_buf ).set( in_index, val ); break;
  case 14: Core::Array::bit_arr_mng<int,14>( (void*)in_ptr_buf ).set( in_index, val ); break;
  case 15: Core::Array::bit_arr_mng<int,15>( (void*)in_ptr_buf ).set( in_index, val ); break;
  case 16: Core::Array::bit_arr_mng<int,16>( (void*)in_ptr_buf ).set( in_index, val ); break;
  default: Core::Array::bit_arr_mng<int,16>( (void*)in_ptr_buf ).set( in_index, val ); break;
  }
}

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// short型テーブルの右ビットシフト
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void  zzlf_bitConvertLowTable(
        short*  out_ptr_start,
        short*  out_ptr_end,
        int     in_bit_shift )
{
  short* ptr = out_ptr_start;
  while( ptr <= out_ptr_end )
  {
    *ptr >>= in_bit_shift;
    ++ptr;
  }
}

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// short型テーブルの左ビットシフト
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void  zzlf_bitConvertHighTable(
        short*  out_ptr_start,
        short*  out_ptr_end,
        int     in_bit_shift )
{
  short* ptr = out_ptr_start;
  while( ptr <= out_ptr_end )
  {
    *ptr <<= in_bit_shift;
    ++ptr;
  }
}


// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// 重み慣性による平坦化
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void zzlf_flatInertia( short* io_org_buf, float in_weight )
{
  // 一時バッファ
//  const int num = io_org_buf.GetNum();
//  Array::Dynamic<float> tmp_buf( num, 0 );

  if( in_weight <= 0 )
  {
    in_weight = 0.01f;
  }

  float tmp_buf[ CSC_BLOCK_SIZE ];
  int value_old;
  int value = io_org_buf[ 0 ];
  tmp_buf[ 0 ] = (float)value;
  float vec = 0;
  float w0 = in_weight / (in_weight + 1.0f);
  float w1 = 1.0f / w0;
  for( int idx = 1; idx < CSC_BLOCK_SIZE; ++idx )
  {
    value_old = value;
    value     = io_org_buf[ idx ];
    vec = (vec * w0) + ((value - value_old) / w1);

    tmp_buf[ idx ] = tmp_buf[ idx - 1 ] + vec;
  }

  // コピー
  for( int idx = 0; idx < CSC_BLOCK_SIZE; ++idx )
  {
    io_org_buf[ idx ] = (short)(tmp_buf[ idx ] / in_weight);
  }
}


// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// 近値との平均化
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void zzlf_flatAverage( short* io_org_buf, int in_num )
{
//  const int sample_num = io_org_buf.GetNum();
//  Array::Dynamic<float> tmp_buf( sample_num, 0 );
  float tmp_buf[ CSC_BLOCK_SIZE ];

  for( int idx = 0; idx < CSC_BLOCK_SIZE; ++idx )
  {
    const int j_max = CPL::Min( in_num, CSC_BLOCK_SIZE - idx );
    const int j_min = idx < in_num ? 0 : -in_num;
    float total_value = 0;
    for( int j = j_min; j < j_max; ++j )
    {
      total_value += (float)io_org_buf[ idx + j ];
    }
    tmp_buf[ idx ] = total_value / (float)(j_max - j_min);
  }

  // コピー
  for( int idx = 0; idx < CSC_BLOCK_SIZE; ++idx )
  {
    io_org_buf[ idx ] = (short)tmp_buf[ idx ];
  }
}


// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// 差分量子化テーブルを取得
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void zzlf_getSubTable( short_cptr in_ptr_org_buf, short_ptr out_ptr_sub_buf, int in_num )
{
  out_ptr_sub_buf[0] = in_ptr_org_buf[0];

  for( int i = 1; i < in_num; ++i )
  {
    out_ptr_sub_buf[ i ] = in_ptr_org_buf[ i ] - in_ptr_org_buf[ i-1 ];
  }
}


// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// 予測値との差分量子化テーブルを取得
// ※ in_numは３以上を前提としているので注意
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void zzlf_getSubTable_by_PredictiveValue(
  short_cptr  in_ptr_org_buf,
  int_ptr     out_ptr_sub_buf,
  int         in_num )
{
//  out_ptr_sub_buf[0] = in_ptr_org_buf[0];
  out_ptr_sub_buf[0] = 0;

  int pre_value1 =
    zzlf_predictiveValue( in_ptr_org_buf[0], in_ptr_org_buf[0], in_ptr_org_buf[0] );
  out_ptr_sub_buf[1] = in_ptr_org_buf[1] - pre_value1;

  int pre_value2 =
    zzlf_predictiveValue( in_ptr_org_buf[1], in_ptr_org_buf[0], in_ptr_org_buf[0] );
  out_ptr_sub_buf[2] = in_ptr_org_buf[2] - pre_value2;

//  CPL_OUTPUT(
//    SwS("%d\n%d\n%d\n"),
//    in_ptr_org_buf[0],
//    in_ptr_org_buf[1],
//    in_ptr_org_buf[2] );

  for( int i = 3; i < in_num; ++i )
  {
    int pre_value =
      zzlf_predictiveValue(
        in_ptr_org_buf[ i - 1 ],
        in_ptr_org_buf[ i - 2 ],
        in_ptr_org_buf[ i - 3 ] );

    out_ptr_sub_buf[ i ] = in_ptr_org_buf[ i ] - pre_value;

//    CPL_OUTPUT( SwS("%d\n"), in_ptr_org_buf[ i ] );
  }
}


// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// 指定範囲の最小と最大を返す
// ※ short2::xに最小、short2::yに最大を格納
//    また、in_ptr_start <= in_ptr_endである必要がある
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
short2 zzlf_getMinMax( short_cptr in_ptr_start, short_cptr in_ptr_end )
{
  short2 min_max( 32767, -32768 );

  while( in_ptr_start <= in_ptr_end )
  {
    if( min_max.x >= *in_ptr_start )
    {
      min_max.x = *in_ptr_start;
    }
    if( min_max.y <= *in_ptr_start )
    {
      min_max.y = *in_ptr_start;
    }

    ++in_ptr_start;
  }

  return min_max;
}


// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// 指定範囲の必要最低ビット数を求める
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
int zzlf_getRequiredBit_by_Table( int_cptr in_ptr_start, int_cptr in_ptr_end )
{
  int max_bit = 0;
  while( in_ptr_start <= in_ptr_end )
  {
    int bit = zzlf_getRequiredBit( *in_ptr_start );
    if( max_bit < bit )
    {
      max_bit = bit;
    }
    ++in_ptr_start;
  }

  return max_bit;
}


// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// in_idx_startからin_idx_endまでの範囲の値を設定
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void zzlf_getDecodeBuf(
      u_char_cptr in_data_addr,
      int         in_idx_start,
      int         in_idx_end,
      int         in_bit,
      short_ptr   out_pcm_buf )
{
  int idx = in_idx_start;
  if( in_bit >= 16 )
  {
    // 直値
    while( idx <= in_idx_end )
    {
      out_pcm_buf[ idx ] = reinterpret_cast<short_cptr>(in_data_addr)[ idx ];
      int value = out_pcm_buf[ idx ];

//      CPL_OUTPUT( SwS("%d\n"), value );

      ++idx;
    }
  }
  else
  {
    // 予測差分からの計算
    while( idx <= in_idx_end )
    {
      const int pre_value =
        zzlf_predictiveValue(
          out_pcm_buf[ idx - 1 ],
          out_pcm_buf[ idx - 2 ],
          out_pcm_buf[ idx - 3 ] );
      const int sub_value = zzlf_getBitArray( in_data_addr, in_bit, idx );

      int value = pre_value + sub_value;
//      CPL_OUTPUT( SwS("%d\n"), value );
      if( value >= 0x7FFF || value <= -0x8000 )
      {
        out_pcm_buf[ idx ] = 0x7FFF * CPL::Sign(value);
      }

      out_pcm_buf[ idx ] = pre_value + sub_value;

      ++idx;
    }
  }
}



} // namespace Core
} // namespace CPL


