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
깔끔하네요~