fortran66のブログ

fortran について書きます。

MPEG1用のCRC16ルーチン

CRCについてはNumerical Recipesの第二版以降にある、Less numerical Algorithmsの章にある原理の説明を読むのがよかろうかと思います。

もう細部はすっかり忘れてしまいました。覚えているのは、1.割り算の余りを比較する。2.余りが違ければ元の数もちがう。3.2のModuloをとることで割り算の桁下がりを避ける。4.割り算の多項式をうまく選ぶと、たとえ偶然同じ余りが出るにしても、狂っているビット同士は近くには無い(以下のCRC16なら8?ビット以内には無い)。ということくらい。

MPEG1のCRC16の生成多項式の定義は、IBMのBISYNCHというものと共通のようです。生成多項式はx^16+x^15+x^2+1、初期値はZ'FFFF'です。

MPEG1でのCRCの利用は、あくまでデータの一部に対するエラーチェックのみで、訂正機能は持ちません。MPEG1/Layer3(MP3)はISDNによる64Kbps,2ch(計128kbps)のストリーミングを念頭に置いたものですが、CRCエラーも伝送エラーを想定しているものと思われます。CRCエラーがある場合はデータの再送信を要求することが考えられます。

CRCエンコードされるのは主に音量のスケールファクターを定義する部分なので、エラーがある場合プレーヤは音声をミュートにすることで、機器を壊すような大音量が出ることを避けることができます。

ただ、世間に流通しているMP3ファイルでCRCが付いているものをほとんど見たことがありません。また、プレーヤ側でもCRCエラーを無視することが多いようです。

■実行結果

■ソース・コード

10年位前のプログラムを書き直したので見かけ上は動作してますが、まだチェックをしっかりやってないので、余り信じないでくださいw あくまでOOPの形式の練習ということで。

初期値付のスカラー要素割付によるインスタンス生成をやってみました。わざわざALLOCATABLEにしなくてもいいんですが、まぁ練習だからw

MODULE m_crc
   IMPLICIT NONE
   PRIVATE
   PUBLIC :: t_crc
 
   TYPE :: t_crc
     INTEGER(2) :: generator = Z'8005' !B'1000000000000101'  ! x^16+x^15+x^2+1
     INTEGER(2) :: crc
    CONTAINS
     PROCEDURE :: crc16
     PROCEDURE :: set
     PROCEDURE :: get
   END TYPE t_crc
 
 CONTAINS
  !--------------------------------------------------------------------------
   SUBROUTINE set(this, init)
     CLASS(t_crc), INTENT(IN OUT) :: this
     INTEGER, INTENT(IN) :: init
     
     this%crc = init
    
     RETURN
   END SUBROUTINE set
  !--------------------------------------------------------------------------
   INTEGER(2) FUNCTION get(this)
     CLASS(t_crc), INTENT(IN OUT) :: this
   
     get = this%crc
   
     RETURN
   END FUNCTION get
  !--------------------------------------------------------------------------
   SUBROUTINE crc16(this, n, in)
     CLASS(t_crc), INTENT(IN OUT) :: this
     INTEGER, INTENT(IN) :: n, in
     INTEGER :: i, ibit1, ibit2
     
     DO i = n - 1, 0, -1
       ibit1    = IBITS(in,        i, 1)
       ibit2    = IBITS(this%crc, 15, 1)
       this%crc = ISHFT(this%crc, 1)   
       IF ( IEOR(ibit1, ibit2) == 1 ) this%crc = IEOR(this%crc, this%generator)
     END DO

     RETURN
   END SUBROUTINE crc16
  !--------------------------------------------------------------------------
END MODULE m_crc
!===========================================================================
PROGRAM test
  USE m_crc
  IMPLICIT NONE
  INTEGER :: i
  TYPE (t_crc), ALLOCATABLE :: crc
 
  ALLOCATE( crc, SOURCE = t_crc(Z'8005', Z'FFFF') ) ! generate instance
!  ALLOCATE( crc )
!  CALL crc%set( Z'FFFF' )
  PRINT '(B16.16)', crc%get()
  DO i = 1, Z'000F'
   CALL crc%crc16(8, i)
   PRINT '(B16.16)', crc%get()
  END DO
  
  STOP
END PROGRAM test