본문 바로가기

IT/리눅스

PCM 파형의 음량크기 측정하기(rms dB, dBFS, 파이썬)

반응형

1. 정의 

 

PCM(펄스 코드 변조)은 디지털 오디오 시스템에서 사용되는 일반적인 오디오 샘플링 형식입니다. PCM 음량을 표현하는 데는 RMS dB와 dBFS 두 가지를 많이 이용합니다. 

(1) RMS dB (루트 평균 제곱 데시벨):
RMS dB는 오디오 신호의 진폭(음량)을 측정하는 방법 중 하나로, 소리의 강도를 나타내는데 사용됩니다. 

이것은 특정 시간 동안 오디오 신호의 평균 제곱값의 루트에 로그를 적용하여 얻어진 값입니다. 

RMS dB는 오디오 신호의 상대적인 강도를 표현하며, 보통 음악 및 소리 엔지니어링 분야에서 사용되고 일반적으로 음악의 다이나믹 레인지를 조절하는 데 도움을 줄 수 있습니다.

값의 범위는 -∞ dB에서 0 dB까지이며, 더 큰 값을 가지면 더 높은 음량을 나타냅니다. (하지만 보통 -70 ~ 0이 대부분)

(2) dBFS (데시벨 풀 스케일):
dBFS는 Full Scale의 데시벨을 나타내며, 디지털 오디오에서 사용되는 표준 단위입니다. 

이것은 디지털 오디오 신호의 최대 진폭을 기준으로 한 상대적인 레벨을 나타냅니다. 

0 dBFS는 디지털 오디오의 최대 허용 신호 레벨을 나타냅니다. 음악이나 오디오 트랙을 디지털로 레코딩할 때, 신호가 0 dBFS를 넘지 않도록 주의해야 합니다. dBFS는 음악 프로듀서나 믹서에게 중요한 지표이며, 신호가 오버플로우되지 않도록 하는 데 도움을 줍니다. 

값의 범위는 -∞ dBFS에서 0 dBFS까지입니다. 


2. 파이썬으로 rms dB 값과 dBFS값 생성하기 

 

이제 값을 계산 하는 코드를 확인해 보겠습니다. 

 
import wave
import numpy as np
import matplotlib.pyplot as plt
import math                    
#입력은 pcm data
def calculate_rms_db(audio_data):
    rms = np.sqrt(np.mean(np.square(audio_data)))
    rms_db = 20 * math.log10(rms)
    return rms_db

def calculate_dbfs(audio_data):
    max_amplitude = np.max(np.abs(audio_data))
    dbfs = 20 * math.log10(max_amplitude)
    return dbfs
 
 
 

 

def make_db_level_rmsdb_dbfs(input, interval=1):
   
    print(f"make_db_level_rmsdb_dbfs : \n{input}")
   
    # WAV 파일 열기, 정보 얻기
    with wave.open(input, 'rb') as wav:
        # WAV 파일 속성 가져오기
        num_channels = wav.getnchannels()
        sample_width = wav.getsampwidth()
        sample_rate = wav.getframerate()        
        frames = wav.readframes(wav.getnframes())
       
        data_pcm16 = np.frombuffer(frames, dtype=np.int16)    
        normalized_pcm = data_pcm16 / (2**15 - 1)     #16bit로 정규화 -1 ~ 1 사이값으로 정규화                

        # 전체 RMS dB 값 계산
        rms_db_total = calculate_rms_db(normalized_pcm)
        print("total RMS dB value:", rms_db_total)

        # 전체 dBFS 값 계산
        dbfs_total = calculate_dbfs(normalized_pcm)
        print("total dBFS value:", dbfs_total)
       
 
        # 몇초분량마다 DB를 측정할 것인지 결정, default=1
        db_interval = sample_rate*interval
        db_data_len = (len(normalized_pcm) // db_interval)        
        rms_db_value = np.zeros(db_data_len, dtype=np.float16)
        dbfs_value = np.zeros(db_data_len, dtype=np.float16)        
       
        print(f"db_interval (samples) = {db_interval}")
        print(f"normalized_pcm len= {len(normalized_pcm)}")
        print(f"db_data_len = {db_data_len}")
       
        for i in range(db_data_len):
           
            # 샘플 인덱스를 계산
            original_index = i * db_interval

            # 마지막 샘플은 가능한 최대한의 샘플을 사용합니다.
            if i == db_data_len - 1:
                rms_db_value[i] = calculate_rms_db(normalized_pcm[original_index:])
                dbfs_value[i] = calculate_dbfs(normalized_pcm[original_index:])
                print(f"last index {i}")
            else:                
                rms_db_value[i] = calculate_rms_db(normalized_pcm[original_index:original_index + db_interval])
                dbfs_value[i] = calculate_dbfs(normalized_pcm[original_index:original_index + db_interval])
       
        print(f"rms_db_value : max {max(rms_db_value)} min {min(rms_db_value)}")
        print(f"dbfs_value : max {max(dbfs_value)} min {min(dbfs_value)}")
       
        basename = os.path.basename(input)
       
        #그래프로 출력 해보기
        plt.figure(figsize=(60, 10))    
        # 세로축 범위 고정 (-70에서 0)
        plt.ylim(-70, 0)
        plt.plot(rms_db_value)
        plt.xlabel('Index')
        plt.ylabel('Value')
        plt.savefig(f'{basename}.rms_db_value.png')  # 이미지 저장
        plt.close()
     
        plt.figure(figsize=(60, 10))
        plt.ylim(-70, 0)        
        plt.plot(dbfs_value)
        plt.xlabel('Index')
        plt.ylabel('Value')
        plt.savefig(f'{basename}.dbfs_value.png')  # 이미지 저장
        plt.close()
 
       
        return rms_db_total, dbfs_total, rms_db_value, dbfs_value
 
 
 

 

깔끔하네요~ 

 

반응형