fortran66のブログ

fortran について書きます。

CDの音量、絶対聴覚閾値

CDには16bitのPCMの音声データが記録されているわけですが、それは一種の相対値であって再生時の絶対的な音量には任意性があります。それはCD再生機のボリュームつまみで音量を変えられることからわかります。

MPEGの音声圧縮のためには、心理音響解析が必要になりますが、そこには聴覚閾値が重要な要素としてからんできます。聴覚閾値を求めるには静音時の絶対聴覚閾値を定める必要がありますが、ここに不定性による困難が生じます。

更なる問題として、CDの音声データがCD毎に異なる規格化因子で記録されていることがあります。そのため再生装置の音量つまみを一定にしていても、CD毎に再生される音量が異なることになります。ラジオやテレビで流れるときに音が大きいほうが耳に残ってCMの効果があると考えられているため、近年のポップスCDでは全体の平均音量を上げることが習慣的に行われています。

一般的に、クラシック音楽や昔のポップスのデジタル・リマスターCDなどでは平均的な音量が小さく、最近のポップスでは平均的な音量が大きい傾向があります。

ここでは、それを定量的に見ることを考えて、手元に転がっていた種々のCDのPCMデータの平均値と標準偏差を調べます。将来、心理音響解析の聴覚閾値を求めるための、ヒントにすることを考えています。

以下の結果を見ると、聞いた感じの大体の傾向が結果に現れていることが見て取れます。

平均値3000、標準偏差10**7を超えるあたりからなんとなく耳障りになるように思います。

■実行結果

1カラム目はファイル名、2カラム目はPCMデータ数(左右の和)、3カラム目が16ビットPCMデータの整数値としての絶対値の平均、4カラム目はその標準偏差、5,6カラム目はそれぞれPCMデータの絶対値の最小および最大です。
◎クラシック曲
古楽器による協奏曲


古楽器室内楽編曲


○オーケストラ付き声楽曲

◎アナログ音源リマスター版
○サイモン・アンド・ガーファンクル


マッシモ・ラニエリ、デオダート 


ジミー・スミス、オリバー・ネルソン


マルコス・ヴァーリ


○カルロス・リラ


○マリア・トレド、ルイス・ボンファ

◎90年代初頭ポップス CD初期
○Betty Boo


○Jeremy Jordan


○Cathy Denis


○レニー・クラビッツ
http://www.amazon.com/build-this-garden-Single-CD/dp/B000M6TKPI

◎ポップス
○ジャミロ・クワイ


Fatboy Slim


○デオダート (IRMAの糞デジタル化 音が荒い)

◎アニソン
坂本真綾 Hatch Potch


Cowboy Bebop OST1


○Leer Lied ローゼンメイデン


涼宮ハルヒの詰合


田村ゆかり True Romance


○松雪泰子 時を越えて


大森玲子 ニャンダー仮面

ソース・コード

MODULE m_kind
  INTEGER, PARAMETER :: kd = SELECTED_REAL_KIND(10, 99) ! 10digits 
END MODULE m_kind

!========================

MODULE m_wavio
  USE m_kind
  IMPLICIT NONE
  PRIVATE
  PUBLIC :: t_wavfile
!
  TYPE :: t_fmt
     SEQUENCE
     CHARACTER(4) :: chunk_id
     INTEGER(4) :: chunk_size
     INTEGER(2) :: format_id, channels
     INTEGER(4) :: sampling_rate
     INTEGER(4) :: bytes_per_sec
     INTEGER(2) :: block_size, bits_per_sample
  END TYPE t_fmt
!
  TYPE :: t_data
     SEQUENCE
     CHARACTER(4) :: chunk_id
     INTEGER(4) :: chunk_size
     ! pcm data  
  END TYPE t_data
!
  TYPE :: t_riffwav
     SEQUENCE
     CHARACTER(4) :: chunk_id
     INTEGER(4) :: chunk_size
     CHARACTER(4) :: formattag
     TYPE (t_fmt ) :: fmt
     TYPE (t_data) :: dat
  END TYPE t_riffwav
!
  TYPE :: t_wavfile
     INTEGER :: unit = 10
     INTEGER :: ipos
     CHARACTER(:), ALLOCATABLE :: fn
     TYPE (t_riffwav) :: riff
   CONTAINS
     PROCEDURE :: openfile
     PROCEDURE :: read_pcm_all
     PROCEDURE :: closefile
     ! FINAL      :: close_file
  END TYPE t_wavfile

 CONTAINS
  !------------------------------------------------
  SUBROUTINE openfile(this, fn)
    CLASS(t_wavfile), INTENT(IN OUT) :: this
    CHARACTER(*), INTENT(IN) :: fn
    INTEGER :: io

    this%fn = fn
    OPEN(this%unit, FILE = this%fn, ACCESS = 'STREAM', IOSTAT = io, STATUS = 'OLD', FORM = 'UNFORMATTED')
    IF (io /= 0) STOP 'end of file encountered'

    ASSOCIATE (riff => this%riff, fmt => this%riff%fmt, dat => this%riff%dat)
! RIFF-WAVE chunk
     READ(this%unit) riff
     IF (riff%chunk_id  /= 'RIFF') STOP 'this is not RIFF file'
     IF (riff%formattag /= 'WAVE') STOP 'this RIFF file is not in WAVE format'
! fmt chunk
     IF ( fmt%chunk_id  /= 'fmt '  ) STOP 'fmt chunk not found'
     IF ( fmt%format_id /=  1      ) STOP 'Unknown WAVE format' ! 1 Linear PCM
     IF ( fmt%bits_per_sample /= 16) STOP 'Not 16bit data'
     SELECT CASE ( fmt%channels )
      CASE (1)
      !   WRITE(*, '(a, i3, a, i6, a)') 'Monoral', fmt%bits_per_sample, 'bit Sampling rate', fmt%sampling_rate, 'Hz '
      CASE (2)
      !   WRITE(*, '(a, i3, a, i6, a)') 'Stereo' , fmt%bits_per_sample, 'bit Sampling rate', fmt%sampling_rate, 'Hz '
      CASE DEFAULT
         STOP 'Wave channels must be 1 or 2'
     END SELECT
! data chunk
     IF (dat%chunk_id /= 'data') THEN
       DO      
         INQUIRE(this%unit, POS = this%ipos)
         this%ipos = this%ipos + dat%chunk_size  ! skip non-data chunk
         READ(this%unit, POS = this%ipos, IOSTAT = io) dat
         IF (io == -1) STOP 'end of file encounterd while searching for a data chunk'
         IF (dat%chunk_id == 'data') EXIT
        END DO
     END IF
! now POS is at the beginning of PCM data 
    END ASSOCIATE

    RETURN
  END SUBROUTINE openfile
  !------------------------------------------------
  SUBROUTINE closefile(this)           
    CLASS(t_wavfile), INTENT(IN) :: this

    CLOSE(this%unit)

    RETURN
  END SUBROUTINE closefile
  !------------------------------------------------
  SUBROUTINE read_pcm_all(this, pcm16)
    CLASS(t_wavfile), INTENT(IN) :: this
    INTEGER(2), ALLOCATABLE, INTENT(OUT) :: pcm16(:)
     
    ALLOCATE( pcm16( this%riff%dat%chunk_size / 2 ) )
    READ(this%unit) pcm16

    RETURN 
  END SUBROUTINE read_pcm_all

END MODULE m_wavio

!==============================================

PROGRAM test
  USE m_kind
  USE m_wavio
  IMPLICIT NONE
  TYPE (t_wavfile), ALLOCATABLE :: wavfile
  INTEGER(2), ALLOCATABLE :: pcm16(:)
  INTEGER :: i, j
  REAL :: ave, dev
  CHARACTER(256) :: fn
  
  ALLOCATE( wavfile )
  PRINT *, 'Average sound intensity and standard deviation'
  DO i = 1, 99
    WRITE(fn, '(a, i2.2, a)') 'Deodato_', i, '.wav'
    CALL wavfile%openfile(TRIM(fn))
    CALL wavfile%read_pcm_all(pcm16)
    ave = SUM(  ABS(REAL(INT(pcm16, 4))) ) / SIZE(pcm16)
    dev = SUM( (ABS(REAL(INT(pcm16, 4))) - ave)**2 ) / SIZE(pcm16)
    PRINT '(a, i10, a, f9.1, es12.2, 2i9)', TRIM(fn), SIZE(pcm16), ':', ave, dev, &
                                       MINVAL(ABS(INT(pcm16, 4))), MAXVAL(ABS(INT(pcm16, 4)))
  END DO
  DEALLOCATE( wavfile, pcm16 )

  STOP
END PROGRAM test