본문 바로가기
디지털 파일 검증 연구소 디지털 파일 검증 연구소

WAV 파일 구조 분석|RIFF·fmt·data 청크 확인 방법

디지털 파일 검증 연구소 읽는 시간 약 41분

WAV 파일은 음성 녹음, 음악 편집, 방송 제작, 오디오 분석, 디지털 음원 보관 등 다양한 환경에서 사용되는 오디오 파일 형식이다.

일반적으로 .wav 확장자를 사용하며, Windows 환경에서 널리 사용되는 RIFF(Resource Interchange File Format) 구조를 기반으로 오디오 데이터와 관련 정보를 저장한다.

하지만 WAV 파일은 단순히 소리 데이터를 순서대로 저장한 파일이 아니다.

파일의 시작 부분에는 RIFF 식별자와 전체 크기 정보가 존재하며, 내부에는 오디오 형식을 설명하는 fmt 청크, 실제 오디오 샘플을 담는 data 청크, 그 밖의 메타데이터 청크가 배치될 수 있다.

특히 WAV 파일 구조를 분석할 때는 채널 수, 샘플링 주파수, 비트 깊이, 바이트 전송률, 블록 정렬 값의 관계를 정확히 이해해야 한다.

이번 글에서는 WAV 파일 구조 분석을 중심으로 RIFF 헤더, fmt 청크, data 청크, 오디오 메타데이터와 파일 손상 검증 원리를 설명한다.

또한 Windows PowerShell 5.1을 이용해 실제 WAV 파일의 시그니처와 주요 청크를 읽고, PCM 오디오의 기술 정보를 확인하는 실습을 진행한다.

1. WAV 파일 구조 분석이 필요한 이유

WAV 파일은 대부분의 오디오 편집 프로그램에서 쉽게 열 수 있다.

그러나 확장자가 .wav라고 해서 파일 내부가 항상 올바른 RIFF/WAVE 구조를 갖추고 있거나 정상적으로 재생되는 것은 아니다.

다음과 같은 상황에서는 내부 구조를 확인할 필요가 있다.

  • WAV 파일이 열리지 않는 경우
  • 녹음 파일의 재생 시간이 잘못 표시되는 경우
  • 오디오 재생 중 잡음이나 끊김이 발생하는 경우
  • 채널 수 또는 샘플링 주파수가 예상과 다른 경우
  • 파일 전송 과정에서 일부 데이터가 손상된 것으로 의심되는 경우
  • 녹음 장치가 생성한 파일의 기술 정보를 확인하려는 경우
  • 오디오 데이터와 메타데이터를 구분하려는 경우
  • 원본과 복사본의 무결성을 비교하려는 경우

WAV 구조 분석은 파일 형식 오류, 청크 경계 문제, 오디오 형식 정보 불일치 등을 확인하는 출발점이 된다.

다만 헤더와 청크 구조가 정상이라고 해서 실제 오디오 샘플에 잡음이나 왜곡이 없다는 의미는 아니다.

2. WAV와 RIFF 파일 형식의 관계

WAV는 일반적으로 RIFF 구조를 이용해 오디오 데이터를 저장하는 파일 형식이다.

RIFF는 서로 다른 유형의 데이터를 청크(Chunk) 단위로 구성하는 컨테이너 형식이다.

RIFF 기반 파일은 파일 전체를 식별하는 헤더와 여러 하위 청크로 이루어진다.

대표적인 구조는 다음과 같다.

RIFF Container
│
├── RIFF Header
│
├── fmt  Chunk
│
├── data Chunk
│
└── Optional Chunks

여기서 fmt 와 data는 WAV 파일의 주요 청크다.

주의할 점은 fmt 식별자가 네 글자라는 것이다.

실제 식별자는 f, m, t 뒤에 공백 한 칸이 포함된 fmt 이다.

WAV 파일의 기본 구조를 이해하려면 이처럼 4바이트 청크 식별자를 정확하게 구분해야 한다.

WAV는 항상 무압축 PCM인가?

그렇지는 않다.

WAV는 PCM 오디오를 저장하는 데 널리 사용되지만, 파일 형식 자체가 모든 오디오 데이터를 무압축 PCM으로 제한하지는 않는다.

WAV에는 IEEE 부동소수점 오디오나 다른 인코딩 형식이 저장될 수 있다.

따라서 WAV 확장자만으로 내부 오디오 인코딩을 확정해서는 안 된다.

3. WAV 파일의 전체 구조

일반적인 RIFF/WAVE 파일의 구조는 다음과 같다.

WAV File
│
├── RIFF Header
│   ├── "RIFF"
│   ├── RIFF Chunk Size
│   └── "WAVE"
│
├── fmt  Chunk
│   ├── Audio Format
│   ├── Number of Channels
│   ├── Sample Rate
│   ├── Byte Rate
│   ├── Block Align
│   └── Bits Per Sample
│
├── Optional Chunks
│   ├── LIST
│   ├── bext
│   ├── cue
│   └── Other Chunks
│
└── data Chunk
    └── Audio Sample Data

위 구조는 설명을 위한 예시다.

실제 파일에서는 선택적 청크의 종류와 순서가 달라질 수 있다.

또한 fmt 청크에는 오디오 형식에 따라 추가 필드가 존재할 수 있다.

일반적인 WAV 파일에서 data 청크가 존재하더라도, 내부 데이터가 반드시 16비트 PCM 샘플이라는 뜻은 아니다.

4. WAV 파일 시그니처와 헤더 바이트

일반적인 RIFF/WAVE 파일은 파일 시작 부분에 RIFF 식별자를 포함한다.

첫 12바이트는 다음과 같이 구성된다.

오프셋크기내용
04바이트RIFF
44바이트RIFF 청크 크기
84바이트WAVE

대표적인 헤더 예시

52 49 46 46 24 00 00 00
57 41 56 45

앞의 4바이트는 다음과 같다.

52 49 46 46

ASCII 문자열로 변환하면 다음과 같다.

RIFF

8번 오프셋부터 4바이트는 다음과 같다.

57 41 56 45

ASCII 문자열로 변환하면 다음과 같다.

WAVE

따라서 일반적인 RIFF/WAVE 파일을 식별할 때는 RIFF와 WAVE를 함께 확인하는 것이 중요하다.

다만 이 두 식별자가 존재한다는 사실만으로 전체 WAV 파일의 정상성을 보장할 수는 없다.

5. RIFF 헤더와 파일 크기 계산 원리

RIFF 헤더의 크기 필드는 파일 전체의 길이를 해석하는 데 사용된다.

일반적인 RIFF/WAVE 파일에서는 4번 오프셋부터 4바이트가 RIFF 청크 크기를 나타낸다.

이 값은 리틀엔디언(Little-endian) 방식으로 저장된다.

리틀엔디언 예시

24 00 00 00

이 바이트열은 16진수 0x00000024, 즉 10진수 36을 의미한다.

일반적인 단일 RIFF 청크가 파일 전체를 차지하는 경우 다음과 같이 계산할 수 있다.

전체 파일 크기 = RIFF Chunk Size + 8

예를 들어 RIFF 크기 필드가 36이라면 전체 RIFF 청크 길이는 44바이트다.

하지만 이 계산은 RIFF 청크의 크기를 나타내는 것이며, 실제 파일에 추가 바이트가 존재하는지 여부까지 설명하지는 않는다.

파일 검증 시에는 기록된 RIFF 크기와 실제 파일 길이를 비교해야 한다.

6. WAV 청크의 기본 구조

RIFF 기반 WAV 파일의 하위 청크는 일반적으로 다음과 같은 구조를 가진다.

+---------------------------+
| Chunk ID       4 bytes    |
+---------------------------+
| Chunk Size     4 bytes    |
+---------------------------+
| Chunk Data     variable   |
+---------------------------+
| Padding        optional   |
+---------------------------+

청크 식별자

청크의 종류를 나타내는 4바이트 값이다.

대표적인 예시는 다음과 같다.

fmt 
data
LIST
bext
cue 
fact

fmt 와 cue 처럼 공백이 포함된 식별자도 있다.

청크 크기

청크 데이터의 크기를 나타내는 32비트 리틀엔디언 값이다.

청크 헤더 8바이트는 이 값에 포함되지 않는다.

패딩 바이트

RIFF 청크 데이터의 크기가 홀수인 경우에는 다음 청크가 짝수 바이트 경계에서 시작하도록 패딩 바이트가 추가된다.

패딩 바이트는 청크의 Chunk Size 값에 포함되지 않는다.

따라서 다음 청크의 시작 위치를 계산할 때는 패딩 여부를 고려해야 한다.

7. fmt 청크와 오디오 형식 정보

fmt 청크는 WAV 파일의 오디오 데이터 형식을 설명한다.

일반적인 PCM WAV 파일에서 가장 중요한 청크 중 하나다.

기본 PCM fmt 청크

필드크기의미
AudioFormat2바이트오디오 형식 코드
NumChannels2바이트채널 수
SampleRate4바이트초당 샘플 프레임 수
ByteRate4바이트초당 바이트 수
BlockAlign2바이트샘플 프레임의 바이트 크기
BitsPerSample2바이트샘플당 비트 수

기본 PCM 형식에서 위 필드의 합계는 16바이트다.

그러나 확장 형식에서는 추가 필드가 존재할 수 있다.

AudioFormat의 대표적인 값

값의미
1PCM
3IEEE Float
65534WAVE_FORMAT_EXTENSIBLE

형식 코드가 65534인 경우에는 확장 필드에 저장된 SubFormat을 추가로 확인해야 실제 오디오 형식을 판단할 수 있다.

8. 샘플링 주파수와 채널 수

샘플링 주파수(Sample Rate)는 1초 동안 각 채널의 오디오 신호를 몇 번 샘플링하는지 나타낸다.

대표적인 값은 다음과 같다.

  • 8,000Hz
  • 22,050Hz
  • 44,100Hz
  • 48,000Hz
  • 96,000Hz

예를 들어 48,000Hz는 채널당 1초에 48,000개의 샘플을 기록한다는 의미다.

채널 수

채널 수는 오디오가 몇 개의 독립적인 채널로 구성되는지 나타낸다.

대표적인 예시는 다음과 같다.

채널 수일반적인 구성
1모노
2스테레오
65.1채널 구성에 사용 가능
87.1채널 구성에 사용 가능

다만 채널 수만으로 스피커 배치나 채널 순서를 확정할 수는 없다.

다채널 오디오에서는 채널 마스크 등 추가 정보를 확인해야 할 수 있다.

9. 비트 깊이와 오디오 샘플 표현

비트 깊이는 오디오 샘플을 표현하는 데 사용하는 비트 수와 관련된다.

PCM 오디오에서는 대표적으로 8비트, 16비트, 24비트, 32비트 등의 표현이 사용된다.

대표적인 PCM 표현

비트 깊이일반적인 저장 방식
8비트 PCM부호 없는 정수
16비트 PCM부호 있는 정수
24비트 PCM부호 있는 정수
32비트 PCM부호 있는 정수

IEEE Float 형식에서는 32비트 또는 64비트 부동소수점 표현이 사용될 수 있다.

단, BitsPerSample 값만으로 오디오가 정수 PCM인지 부동소수점인지 확정할 수 없다.

오디오 형식 코드와 필요한 확장 정보를 함께 확인해야 한다.

10. ByteRate와 BlockAlign 계산

기본적인 비압축 PCM에서는 샘플링 주파수, 채널 수, 비트 깊이 사이에 일정한 관계가 존재한다.

BlockAlign

BlockAlign = Channels × BitsPerSample / 8

ByteRate

ByteRate = SampleRate × BlockAlign

예를 들어 다음과 같은 오디오가 있다고 가정하자.

SampleRate    = 48000
Channels      = 2
BitsPerSample = 16

BlockAlign은 다음과 같다.

2 × 16 / 8 = 4 bytes

ByteRate는 다음과 같다.

48000 × 4 = 192000 bytes/sec

이 값은 초당 약 192,000바이트의 오디오 데이터가 필요하다는 의미다.

다만 위 계산은 기본적인 비압축 PCM을 기준으로 한다.

압축 오디오나 특수한 샘플 저장 방식에서는 동일한 계산을 그대로 적용해서는 안 된다.

11. data 청크와 실제 오디오 데이터

data 청크는 실제 오디오 샘플 데이터를 담는 영역이다.

PCM WAV 파일에서는 오디오 샘플이 시간 순서에 따라 저장된다.

다채널 PCM에서는 일반적으로 같은 시점의 채널 샘플이 하나의 샘플 프레임으로 구성된다.

스테레오 PCM 개념 예시

Frame 1
├── Left Sample
└── Right Sample

Frame 2
├── Left Sample
└── Right Sample

Frame 3
├── Left Sample
└── Right Sample

실제 파일에서는 이러한 샘플들이 바이너리 데이터로 저장된다.

data 청크 크기

기본적인 비압축 PCM에서는 다음과 같은 관계를 이용할 수 있다.

Sample Frames = Data Size / BlockAlign

또한 다음과 같이 재생 시간을 추정할 수 있다.

Duration = Data Size / ByteRate

다만 청크 크기가 정상적으로 기록되어 있더라도 실제 샘플 데이터가 손상되었을 수 있다.

따라서 헤더 검증과 오디오 재생 검증은 구분해야 한다.

12. WAV 파일의 메타데이터 청크

WAV 파일에는 기본적인 오디오 형식 정보 외에도 추가 메타데이터가 저장될 수 있다.

대표적인 청크는 다음과 같다.

청크주요 용도
LIST정보 목록 등
INFOLIST 내부의 메타데이터 목록 유형
bextBroadcast Wave 메타데이터
cue큐 포인트
fact일부 비PCM 형식의 샘플 수 정보
iXML제작 관련 XML 메타데이터
JUNK예약 또는 정렬 목적의 데이터

INFO는 일반적으로 독립적인 최상위 청크가 아니라 LIST 청크 내부의 목록 유형으로 사용된다.

메타데이터를 확인해야 하는 이유

WAV 파일의 메타데이터에는 다음과 같은 정보가 포함될 수 있다.

  • 제목
  • 제작자
  • 녹음 관련 설명
  • 제작 환경
  • 시간 관련 정보
  • 응용 프로그램 관련 정보

하지만 모든 WAV 파일에 해당 메타데이터가 존재하는 것은 아니다.

또한 메타데이터는 수정될 수 있으므로 실제 녹음 시각이나 제작자를 확정하는 증거로 단독 사용해서는 안 된다.

13. Broadcast Wave Format과 bext 청크

Broadcast Wave Format(BWF)은 방송 및 전문 오디오 제작 환경에서 사용되는 WAV 기반 형식이다.

BWF에서는 bext 청크를 통해 방송 제작에 필요한 정보를 저장할 수 있다.

대표적인 bext 정보

  • Description
  • Originator
  • OriginatorReference
  • OriginationDate
  • OriginationTime
  • TimeReference

이러한 필드는 제작 및 교환 과정에서 파일을 식별하고 관리하는 데 도움을 준다.

다만 bext 청크가 있다고 해서 해당 파일의 제작 이력이 자동으로 검증되는 것은 아니다.

기록된 메타데이터는 신뢰할 수 있는 외부 기록과 함께 해석해야 한다.

14. RF64와 대용량 WAV 파일

일반적인 RIFF/WAVE 파일은 32비트 크기 필드를 사용한다.

이로 인해 단일 RIFF 청크가 표현할 수 있는 크기에 한계가 있다.

대용량 오디오 파일을 다루기 위해 RF64와 같은 확장 형식이 사용될 수 있다.

RF64의 주요 특징

  • RF64 식별자 사용
  • ds64 청크를 이용한 64비트 크기 정보
  • 대용량 오디오 데이터 지원
  • 기존 RIFF/WAVE와 구분되는 크기 해석 규칙

구조 예시

RF64
│
├── ds64
├── fmt 
├── Other Chunks
└── data

따라서 WAV 파일을 분석할 때는 RIFF 기반 일반 WAV인지, RF64 확장 형식인지 구분해야 한다.

이번 글의 PowerShell 실습은 일반적인 RIFF/WAVE 파일을 대상으로 하며 RF64 전체 구조를 분석하지 않는다.

15. WAV 파일과 MP3 파일의 구조적 차이

WAV와 MP3는 모두 오디오 파일에 사용되지만 내부 구조는 다르다.

구분WAVMP3
주요 구조RIFF 청크 기반오디오 프레임 기반
오디오 데이터PCM 등 여러 형식 가능MPEG 오디오 압축
메타데이터RIFF 청크 등ID3 등
대표 확장자.wav.mp3
파일 크기인코딩 방식에 따라 다름비트레이트 등에 따라 다름
주요 분석 대상RIFF·fmt·data프레임 헤더·ID3

WAV가 항상 무압축이라는 설명은 정확하지 않다.

또한 MP3가 반드시 모든 WAV보다 작은 것도 아니다.

파일 크기는 오디오 길이, 채널 수, 샘플링 주파수, 인코딩 방식 및 비트레이트 등에 따라 달라진다.

16. PowerShell 5.1로 WAV 파일 시그니처 확인하기

이번 실습에서는 WAV 파일의 처음 12바이트를 읽어 RIFF와 WAVE 식별자를 확인한다.

① 실습 파일 준비

다음 위치에 WAV 파일이 있다고 가정한다.

C:\FileTest\sample.wav

실제 실행 시에는 본인의 파일 경로로 변경한다.

② PowerShell 코드

$path = "C:\FileTest\sample.wav"

$stream = [System.IO.File]::OpenRead($path)

try {
    $buffer = New-Object byte[] 12
    $offset = 0

    while ($offset -lt 12) {
        $n = $stream.Read(
            $buffer, $offset, 12 - $offset
        )

        if ($n -le 0) {
            throw "Incomplete WAV header."
        }

        $offset += $n
    }

    $riff = [System.Text.Encoding]::ASCII.GetString(
        $buffer, 0, 4
    )

    $wave = [System.Text.Encoding]::ASCII.GetString(
        $buffer, 8, 4
    )

    $riffSize = [BitConverter]::ToUInt32($buffer, 4)

    [PSCustomObject]@{
        RIFF = $riff
        WAVE = $wave
        RIFFSize = $riffSize
        HeaderHex = [BitConverter]::ToString($buffer)
        IsRIFFWAVE = (
            $riff -ceq "RIFF" -and
            $wave -ceq "WAVE"
        )
    }
}
finally {
    $stream.Dispose()
}

③ 출력 예시

RIFF       : RIFF
WAVE       : WAVE
RIFFSize   : 123456
HeaderHex  : 52-49-46-46-...
IsRIFFWAVE : True

위 결과는 설명을 위한 예시다.

④ 코드 해석

처음 4바이트는 RIFF 식별자를 나타낸다.

4번 오프셋부터 4바이트는 RIFF 청크 크기다.

8번 오프셋부터 4바이트는 WAVE 형식 식별자를 나타낸다.

⑤ 실습의 한계

이 코드는 헤더를 확인할 뿐, 모든 청크의 존재 여부나 실제 오디오 데이터의 정상성을 검증하지 않는다.

또한 RF64 확장 형식은 대상으로 하지 않는다.

17. PowerShell 5.1로 WAV 청크 목록 분석하기

이번에는 파일의 최상위 청크를 순서대로 읽어 청크 식별자, 크기, 시작 위치를 확인한다.

RIFF의 홀수 크기 청크 패딩 규칙도 반영한다.

① PowerShell 코드

$path = "C:\FileTest\sample.wav"

$stream = [System.IO.File]::OpenRead($path)

try {
    $reader = New-Object System.IO.BinaryReader($stream)

    $riff = [System.Text.Encoding]::ASCII.GetString(
        $reader.ReadBytes(4)
    )

    $riffSize = $reader.ReadUInt32()

    $wave = [System.Text.Encoding]::ASCII.GetString(
        $reader.ReadBytes(4)
    )

    if ($riff -cne "RIFF" -or $wave -cne "WAVE") {
        throw "Not a standard RIFF/WAVE file."
    }

    $riffEnd = [uint64]$riffSize + 8

    if ($riffEnd -gt [uint64]$stream.Length) {
        throw "RIFF size exceeds file length."
    }

    while ([uint64]$stream.Position -lt $riffEnd) {

        $start = [uint64]$stream.Position
        $remaining = $riffEnd - $start

        if ($remaining -lt 8) {
            throw "Incomplete chunk header."
        }

        $idBytes = $reader.ReadBytes(4)
        $chunkId = [System.Text.Encoding]::ASCII.GetString(
            $idBytes
        )

        $chunkSize = [uint64]$reader.ReadUInt32()
        $dataOffset = [uint64]$stream.Position

        $padding = $chunkSize % 2
        $next = $dataOffset + $chunkSize + $padding

        if ($next -gt $riffEnd) {
            throw "Chunk exceeds RIFF boundary."
        }

        [PSCustomObject]@{
            Offset = $start
            ChunkID = $chunkId
            DataSize = $chunkSize
            DataOffset = $dataOffset
            Padding = $padding
        }

        $stream.Position = [long]$next
    }
}
finally {
    $stream.Dispose()
}

② 출력 예시

Offset ChunkID DataSize DataOffset Padding
------ ------- -------- ---------- -------
12     fmt     16       20         0
36     LIST    24       44         0
68     data    192000   76         0

위 출력은 구조를 설명하기 위한 예시다.

실제 청크 구성과 오프셋은 파일마다 다를 수 있다.

③ 확인 가능한 내용

  • 최상위 청크 식별자
  • 청크 헤더 시작 위치
  • 청크 데이터 크기
  • 데이터 시작 오프셋
  • 홀수 크기 청크의 패딩 여부
  • 청크가 RIFF 경계를 초과하는지 여부

④ 코드의 한계

이 코드는 LIST 청크 내부의 하위 청크를 분석하지 않는다.

또한 각 청크의 의미와 오디오 코덱을 완전히 해석하지 않는다.

청크 크기와 위치가 정상이어도 실제 오디오 샘플이 손상되었을 가능성은 남아 있다.

18. PowerShell 5.1로 PCM 오디오 형식 정보 확인하기

이번에는 fmt 청크에서 오디오 형식, 채널 수, 샘플링 주파수, 바이트 전송률, 블록 정렬, 비트 깊이를 읽는다.

이 예제는 기본 PCM 형식의 WAV 파일을 대상으로 한다.

① PowerShell 코드

$path = "C:\FileTest\sample.wav"

$stream = [System.IO.File]::OpenRead($path)

try {
    $reader = New-Object System.IO.BinaryReader($stream)

    $riff = [System.Text.Encoding]::ASCII.GetString(
        $reader.ReadBytes(4)
    )

    $riffSize = $reader.ReadUInt32()

    $wave = [System.Text.Encoding]::ASCII.GetString(
        $reader.ReadBytes(4)
    )

    if ($riff -cne "RIFF" -or $wave -cne "WAVE") {
        throw "Not a RIFF/WAVE file."
    }

    $riffEnd = [uint64]$riffSize + 8

    if ($riffEnd -gt [uint64]$stream.Length) {
        throw "Invalid RIFF size."
    }

    $found = $false

    while ([uint64]$stream.Position -lt $riffEnd) {

        $remaining = $riffEnd - [uint64]$stream.Position

        if ($remaining -lt 8) {
            throw "Incomplete chunk header."
        }

        $id = [System.Text.Encoding]::ASCII.GetString(
            $reader.ReadBytes(4)
        )

        $size = [uint64]$reader.ReadUInt32()
        $start = [uint64]$stream.Position
        $next = $start + $size + ($size % 2)

        if ($next -gt $riffEnd) {
            throw "Invalid chunk size."
        }

        if ($id -ceq "fmt ") {

            if ($size -lt 16) {
                throw "fmt chunk is too short."
            }

            $format = $reader.ReadUInt16()
            $channels = $reader.ReadUInt16()
            $sampleRate = $reader.ReadUInt32()
            $byteRate = $reader.ReadUInt32()
            $blockAlign = $reader.ReadUInt16()
            $bitsPerSample = $reader.ReadUInt16()

            if ($format -ne 1) {
                throw "This example supports PCM format 1 only."
            }

            if ($channels -eq 0 -or
                $sampleRate -eq 0 -or
                $blockAlign -eq 0) {
                throw "Invalid PCM format fields."
            }

            $expectedAlign = (
                [uint64]$channels * $bitsPerSample
            ) / 8

            $expectedByteRate = (
                [uint64]$sampleRate * $blockAlign
            )

            [PSCustomObject]@{
                AudioFormat = $format
                Channels = $channels
                SampleRate = $sampleRate
                ByteRate = $byteRate
                BlockAlign = $blockAlign
                BitsPerSample = $bitsPerSample
                ExpectedBlockAlign = $expectedAlign
                ExpectedByteRate = $expectedByteRate
                BlockAlignMatches = (
                    $blockAlign -eq $expectedAlign
                )
                ByteRateMatches = (
                    $byteRate -eq $expectedByteRate
                )
            }

            $found = $true
            break
        }

        $stream.Position = [long]$next
    }

    if (-not $found) {
        throw "fmt chunk not found."
    }
}
finally {
    $stream.Dispose()
}

② 출력 예시

AudioFormat        : 1
Channels           : 2
SampleRate         : 48000
ByteRate           : 192000
BlockAlign         : 4
BitsPerSample      : 16
ExpectedBlockAlign : 4
ExpectedByteRate   : 192000
BlockAlignMatches  : True
ByteRateMatches    : True

위 결과는 예시다.

③ 결과 해석

AudioFormat 값이 1이면 기본 PCM 형식을 의미한다.

Channels는 채널 수를 나타낸다.

SampleRate는 초당 샘플 프레임 수를 나타낸다.

BitsPerSample은 샘플의 비트 깊이를 나타낸다.

BlockAlignMatches와 ByteRateMatches는 헤더에 기록된 값이 기본 PCM 계산식과 일치하는지 확인한다.

④ 코드의 한계

이 코드는 기본 PCM 형식 코드 1을 대상으로 한다.

WAVE_FORMAT_EXTENSIBLE, IEEE Float 및 압축 오디오 형식은 지원하지 않는다.

또한 헤더 필드의 일관성을 점검할 뿐, 실제 오디오 샘플의 품질이나 재생 가능성을 검증하지 않는다.

19. WAV 파일 손상 시 확인할 주요 항목

WAV 파일이 정상적으로 재생되지 않는 경우 다음 항목을 확인할 수 있다.

점검 항목확인 목적
RIFF컨테이너 식별
WAVEWAV 형식 식별
RIFF Size전체 RIFF 청크 경계
fmt오디오 형식 정보
AudioFormat오디오 인코딩 유형
Channels채널 수
SampleRate샘플링 주파수
BlockAlign샘플 프레임 크기
ByteRate바이트 전송률
data실제 오디오 데이터 영역
Chunk Padding청크 정렬 규칙
SHA-256기준 파일과 동일성 비교

대표적인 손상 상황

① RIFF 헤더 이상

파일 시작 부분의 식별자나 크기 정보가 손상되면 재생 프로그램이 파일을 올바르게 인식하지 못할 수 있다.

② fmt 청크 누락

오디오 데이터를 해석하는 데 필요한 형식 정보를 확인하지 못할 수 있다.

③ data 청크 누락 또는 손상

실제 오디오 샘플을 읽지 못하거나 재생 중 오류가 발생할 수 있다.

④ 청크 크기 불일치

청크 크기가 실제 파일 경계를 벗어나면 파일이 잘렸거나 구조가 손상되었을 가능성이 있다.

⑤ 오디오 형식 필드 불일치

기본 PCM에서 ByteRate와 BlockAlign 값이 계산식과 일치하지 않으면 헤더 오류 가능성을 검토할 수 있다.

다만 이러한 항목은 손상의 단서일 뿐, 모든 재생 오류의 원인을 확정하는 기준은 아니다.

20. WAV 구조 검증과 SHA-256 무결성 검증

WAV 파일의 구조가 정상인지 확인하는 작업과 원본 파일이 동일한지 확인하는 작업은 다르다.

WAV 구조 검증

RIFF 헤더, 청크 경계, 오디오 형식 필드 등을 확인한다.

오디오 메타데이터 검증

채널 수, 샘플링 주파수, 비트 깊이, 제작 관련 정보 등을 확인한다.

SHA-256 무결성 검증

파일 전체의 해시값을 계산하고 신뢰할 수 있는 기준값과 비교한다.

PowerShell 코드

$path = "C:\FileTest\sample.wav"

Get-FileHash -LiteralPath $path -Algorithm SHA256 |
    Format-List Algorithm, Hash, Path

검증 방식 비교

구분구조 분석메타데이터 분석SHA-256
확인 대상RIFF·청크 구조오디오 형식·제작 정보파일 전체
주요 목적파일 형식 점검기술 정보 확인바이트 동일성 비교
오디오 품질 확인불가능불가능불가능
기준 파일과 동일성 확인불가능불가능가능
안전성 보장불가능불가능단독으로는 불가능

신뢰할 수 있는 기준 해시값과 계산 결과가 일치하면 파일이 동일한 바이트 내용을 가질 가능성이 매우 높다고 판단할 수 있다.

하지만 해시값이 일치한다고 해서 파일 내용이 안전하거나 오디오 품질이 좋다는 의미는 아니다.

21. WAV 파일 분석 시 보안과 개인정보 주의사항

① 녹음 파일의 개인정보

WAV 파일에는 음성 자체뿐 아니라 제작 관련 메타데이터가 포함될 수 있다.

공개 또는 공유 전에 개인 식별 정보나 불필요한 제작 정보가 포함되어 있는지 확인해야 한다.

② 메타데이터의 신뢰성

제작자, 녹음 날짜, 설명 등의 메타데이터는 수정할 수 있다.

따라서 이러한 정보만으로 파일의 출처를 확정해서는 안 된다.

③ 비정상적인 청크 크기

출처가 불분명한 WAV 파일에는 잘못된 크기 필드나 예상하지 못한 청크가 포함될 수 있다.

분석 프로그램은 파일 경계와 메모리 사용량을 제한하고, 비정상적인 입력을 안전하게 처리해야 한다.

④ 원본 보존

파일 손상 분석이나 증거 보존이 목적이라면 원본을 수정하지 않고 복사본을 사용해야 한다.

오디오 편집 프로그램에서 다시 저장하면 샘플 데이터 또는 메타데이터가 변경되어 해시값이 달라질 수 있다.

22. 자주 묻는 질문

Q1. WAV 파일의 Magic Number는 무엇인가요?

일반적인 RIFF/WAVE 파일은 파일 시작 부분의 RIFF와 8번 오프셋의 WAVE 식별자를 함께 확인한다.

Q2. WAV 파일은 항상 무압축인가요?

아니다. WAV에는 PCM 외에도 여러 오디오 형식이 저장될 수 있다.

Q3. fmt 청크는 무엇을 저장하나요?

오디오 형식 코드, 채널 수, 샘플링 주파수, 바이트 전송률, 블록 정렬, 비트 깊이 등의 정보를 저장한다.

Q4. data 청크에는 무엇이 들어 있나요?

오디오 형식에 따라 PCM 샘플이나 다른 방식으로 인코딩된 오디오 데이터가 들어갈 수 있다.

Q5. WAV 파일의 재생 시간은 어떻게 계산하나요?

기본 비압축 PCM에서는 일반적으로 data 크기를 ByteRate로 나누어 재생 시간을 계산할 수 있다.

Q6. WAV 파일의 RIFF 크기와 실제 파일 크기가 다르면 손상인가요?

반드시 그렇지는 않다. 파일 뒤에 추가 데이터가 존재할 수 있으므로 차이의 원인을 확인해야 한다.

Q7. WAV와 RF64는 무엇이 다른가요?

일반적인 RIFF/WAVE는 32비트 크기 필드를 사용하지만 RF64는 ds64 등을 이용해 대용량 파일의 크기를 표현할 수 있다.

Q8. WAV 파일에도 CRC-32가 있나요?

일반적인 RIFF/WAVE 청크 헤더에는 PNG와 같은 공통 CRC-32 필드가 존재하지 않는다.

Q9. WAV 파일의 헤더가 정상이라면 음질도 정상인가요?

아니다. 헤더와 청크 구조가 정상이어도 오디오 샘플에 잡음, 클리핑, 누락 또는 왜곡이 존재할 수 있다.

마무리

WAV 파일은 RIFF 기반 청크 구조를 사용해 오디오 데이터와 관련 정보를 저장하는 형식이다.

RIFF와 WAVE 식별자는 파일의 기본 형식을 확인하는 데 사용되며, fmt 청크는 오디오 기술 정보를, data 청크는 실제 오디오 데이터를 담는다.

또한 LIST, bext, cue 등의 청크를 통해 추가 메타데이터가 저장될 수 있다.

WAV 파일 구조를 분석할 때는 청크의 크기와 경계, 리틀엔디언 바이트 순서, 홀수 크기 청크의 패딩 규칙, PCM 오디오 필드의 계산 관계를 함께 확인해야 한다.

하지만 WAV 파일 구조 분석은 파일의 형식과 내부 배치를 점검하는 작업이지, 실제 오디오 품질이나 원본 동일성, 보안상 안전성을 보장하는 과정은 아니다.

따라서 구조 분석, 오디오 재생 테스트, 메타데이터 검토, SHA-256 무결성 검증을 목적에 맞게 구분해 활용해야 한다.


핵심 내용 3줄 요약

  • WAV는 일반적으로 RIFF 기반 청크 구조를 사용하며 fmt 와 data 청크가 오디오 형식과 실제 데이터를 담당한다.
  • PCM WAV에서는 채널 수, 샘플링 주파수, 비트 깊이, BlockAlign, ByteRate의 관계를 확인할 수 있다.
  • WAV 구조가 정상이어도 오디오 품질과 원본 동일성은 별도 검증이 필요하다.

공식 및 기술 참고자료

  1. Microsoft — Multimedia Programming Interface and Data Specifications (RIFF 및 WAVE 관련 규격)
    https://www.aelius.com/njh/wavemetatools/doc/riffmci.pdf
  2. Microsoft Learn — WAVEFORMATEX Structure
    https://learn.microsoft.com/windows/win32/api/mmeapi/ns-mmeapi-waveformatex
  3. Microsoft Learn — WAVEFORMATEXTENSIBLE Structure
    https://learn.microsoft.com/windows/win32/api/mmreg/ns-mmreg-waveformatextensible
  4. European Broadcasting Union — EBU Tech 3285 (Broadcast Wave Format)
    https://tech.ebu.ch/docs/tech/tech3285.pdf
  5. Library of Congress — WAVE Audio File Format
    https://www.loc.gov/preservation/digital/formats/fdd/fdd000001.shtml
  6. Microsoft Learn — File.OpenRead Method
    https://learn.microsoft.com/dotnet/api/system.io.file.openread
  7. Microsoft Learn — BinaryReader Class
    https://learn.microsoft.com/dotnet/api/system.io.binaryreader
  8. Microsoft Learn — Get-FileHash (PowerShell 5.1)
    https://learn.microsoft.com/powershell/module/microsoft.powershell.utility/get-filehash?view=powershell-5.1

※ 본문의 PowerShell 코드는 RIFF/WAVE 구조를 읽기 전용으로 분석하는 학습용 예시다. 실제 Windows PowerShell 5.1 환경에서의 실행 검증은 별도로 필요하며, 완전한 오디오 디코더나 전문 파일 검증 도구를 대체하지 않는다.

디지털 파일 검증 연구소

디지털 파일 검증 연구소
디지털 파일 검증 연구소는 파일 형식, 확장자, 무결성 확인, 데이터 손상 예방 및 디지털 자료 관리 방법을 다루는 정보 사이트입니다. 파일 오류의 원인과 점검 절차를 이해하기 쉽게 정리하고, 공식 문서와 검증 가능한 자료를 바탕으로 실생활에 도움이 되는 해결 방법을 안내합니다.
함께 보면 좋은 글

댓글 0

첫 댓글을 남겨보세요.