WAV 사운드 파일 로더 만들기 및 PCM Data 로드 - 2 - Whitmem
WAV 사운드 파일 로더 만들기 및 PCM Data 로드 - 2
Sound Development
2024-09-04 01:55 게시 d11a38f1d7cd545ae43a

0
0
59
이 페이지는 외부 공간에 무단 복제할 수 없으며 오직 있는 그대로 게시되며 부정확한 내용을 포함할 수 있습니다. 법률이 허용하는 한 가이드 라인에 맞춰 게시 내용을 인용하거나 출처로 표기할 수 있습니다.
This page is not to be distributed to external services; it is provided as is and may contain inaccuracies.
이전 게시물 WAV 사운드 파일 구조, 웨이브 파일 구조 - 1 에서는 WAV 파일 구조에 대해서 알아보았다. 이번에는 C#에서 이 파일 구조를 바탕으로 파일을 직접 저수준으로 읽어오고 각 데이터 형으로 변환하는 방법에 대해서 알아본다.
WavFile 클래스
기본적으로 WavFile 클래스는 Wav 확장자를 가진 파일을 저수준으로 가져오고, 상기 구조에서 존재하는 모든 내용을 클래스 멤버 변수에 담는 역할을 수행한다. 우선 전역으로 존재해야하는 멤버 변수는 구조와 동일하게 맞추어준다.
클래스에 존재해야하는 멤버 변수
String chunkID; int chunkSize; String format; String FMT; int FMTSize; int audioFormat; int channelSize; int sampleRate; int byteRate; int blockAlign; int bitsPerSample; String data; int dataSize; byte[] pcmData;
그리고 이 멤버 변수들에 대해서 getter 을 구현한다.
추후 load() 기능이 아니라 내보내기 기능까지 구현하고자 하는 경우, setter 도 별개 구현을 해야할 것으로 보인다. 하지만 이 게시물에서는 로더를 만드는 것이 주요 목표이기 때문에, 별도 setter 은 구현하지 않았다.
public String getChunkID() { return chunkID; } public int getChunkSize() { return chunkSize; } public String getFormat() { return format; } public String getFMT() { return FMT; } public int getFMTSize() { return FMTSize; } public int getAudioFormat() { return audioFormat; } public int getChannelSize() { return channelSize; } public int getSampleRate() { return sampleRate; } public int getByteRate() { return byteRate; } public int getBlockAlign() { return blockAlign; } public int getBitsPerSample() { return bitsPerSample; } public String getData() { return data; } public int getDataSize() { return dataSize; } public byte[] getPCMData() { return pcmData; }
다음으로 load() 부분을 구현해야하는데, wav 파일을 바이너리 읽기 모드로 열어, 각각 바이트 스트림을 읽어주고, 원하는 데이터형으로 변환하는 작업이 포함된다. 예를 들어 String 데이터를 읽는 경우 지정된 길이만큼 읽어오고, Encoding을 통하여 String 으로 변환한다. 기본적으로 UTF-8 이 아니라, ANSI 인코딩으로 읽어오거나, C#의 경우 시스템 기본 인코딩인 Encoding.Default.로 읽어와도 무방하다.
상기 사진은 load() 에서 파일 이름을 매개 변수로 받아, 파일 스트림을 열고, 버퍼를 직접 원하는 길이만큼 가져오고, 원하는 멤버 변수에 담는 역할을 수행한다.
1. 파일 스트림 열기
FileStream fileStream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read);
먼저 우리가 작업할 wav 파일은 일반 텍스트 파일이 아니기 때문에 파일 스트림으로 바이너리 모드로 파일을 열어야 한다. 이렇게 열어진 파일은 read 메서드를 사용해서 파일 바이너리를 순차적으로 읽어올 수 있다.
2. 청크 영역 읽기
먼저 첫 번째 청크 영역은 chunkID, chunkSize 으로 구성된다. 청크 ID를 의미하는 스트링과 청크 영역 이후의 크기를 의미하는 chunkSIze로 구분된다. 각각 4바이트 String과 4바이트 int 방식이다.
따라서 먼저, 버퍼를 열고 4바이트 만큼은 String 으로 가져와 chunkID 라는 String 형 멤버 변수에 담아준다.
다음 4바이트 만큼은 Int32 형으로 로드한다.
3. 파일 포맷 영역 읽기
다음으로 Audio 파일의 포맷 정보와 채널 정보, 샘플 정보, 레이트 정보가 담긴 정보를 순차적으로 가져와야한다. FMT 스트링 4바이트, FMT Size 4바이트, 오디오 포맷(PCM여부) 2바이트, 채널 크기 2바이트, sample Rate 4 바이트, byteRate 4바이트, blockAlign 2바이트, bitsPerSample 2바이트로 구분된다.
유의 할 점은 4바이트 스트링은 위 처럼 ANSI나 UTF-8 로 디코딩하여 읽으면 되고, 4바이트의 경우는 32비트인 ToInt32로, 2바이트의 경우는 16비트인 ToInt16으로 해독하면 된다.
buffer = new byte[4]; bufferSize = fileStream.Read(buffer, 0, buffer.Length); FMT = Encoding.Default.GetString(buffer); buffer = new byte[4]; bufferSize = fileStream.Read(buffer, 0, buffer.Length); FMTSize = BitConverter.ToInt32(buffer, 0); buffer = new byte[2]; bufferSize = fileStream.Read(buffer, 0, buffer.Length); audioFormat = BitConverter.ToInt16(buffer, 0); buffer = new byte[2]; bufferSize = fileStream.Read(buffer, 0, buffer.Length); channelSize = BitConverter.ToInt16(buffer, 0); buffer = new byte[4]; bufferSize = fileStream.Read(buffer, 0, buffer.Length); sampleRate = BitConverter.ToInt32(buffer, 0); buffer = new byte[4]; bufferSize = fileStream.Read(buffer, 0, buffer.Length); byteRate = BitConverter.ToInt32(buffer, 0); buffer = new byte[2]; bufferSize = fileStream.Read(buffer, 0, buffer.Length); blockAlign = BitConverter.ToInt16(buffer, 0); buffer = new byte[2]; bufferSize = fileStream.Read(buffer, 0, buffer.Length); bitsPerSample = BitConverter.ToInt16(buffer, 0);
4. 데이터 영역 읽기
이제 실제 오디오 데이터가 존재하는 PCM 영역을 읽어 바이너리 모드로 가져와야 한다. 이 데이터는 추후 short 방식으로 변환하고, 수치 계산을 통해 double 형으로 변환한 뒤 오디오 작업에 사용할 수 있는 진폭 정보가 담긴 실제 파형 데이터를 의미하기도 한다. 먼저 4바이트는 data 스트링, 4바이트는 data 길이, 마지막 모든 데이터가 비로소 pcmData의 바이너리 데이터이다.
buffer = new byte[4]; bufferSize = fileStream.Read(buffer, 0, buffer.Length); data = Encoding.Default.GetString(buffer); buffer = new byte[4]; bufferSize = fileStream.Read(buffer, 0, buffer.Length); dataSize = BitConverter.ToInt32(buffer, 0); pcmData = new byte[dataSize]; bufferSize = fileStream.Read(buffer, 0, buffer.Length);
이렇게 만들어진 WavFile 클래스를 활용해서 메인 폼 창에서 파일을 로드하고 오디오 속성을 가져오는 소프트웨어를 개발할 수 있다. 이번 장에서는 오디오 속성 정보를 가져오고, 다음 장에서 실제 그래프에 파형 데이터를 출력해보려고 한다.
프로그램 레이아웃은 상기와 같다. 파일 선택을 누르면 내부적으로 OpenFileDialog를 통해 파일 열기 창을 띄운다.
그리고 openFileDialog에서 선택한 fileName 값을 load 메서드에 넘긴다. load 메서드에서는 WavFile 클래스의 인스턴스를 생성해서 파일을 load한다. 그런 뒤 richTextBox에 주요 속성 정보를 getter 로 가져와 표시한다.
private void load(String fileName) { WavFile wv = new WavFile(); wv.load(fileName); richTextBox1.Clear(); richTextBox1.AppendText("청크 ID : "); richTextBox1.AppendText(wv.getChunkID() + ""); richTextBox1.AppendText("\n"); richTextBox1.AppendText("청크 영역 크기 : "); richTextBox1.AppendText(wv.getChunkSize() +""); richTextBox1.AppendText("\n"); richTextBox1.AppendText("FMT 바이너리 스트링 : "); richTextBox1.AppendText(wv.getFMT()+ ""); richTextBox1.AppendText("\n"); richTextBox1.AppendText("FMT 영역 크기 : "); richTextBox1.AppendText(wv.getFMTSize()+ ""); richTextBox1.AppendText("\n"); richTextBox1.AppendText("채널 개수 : "); richTextBox1.AppendText(wv.getChannelSize()==2?"스테레오": wv.getChannelSize()==1?"모노":"다수" + ""); richTextBox1.AppendText("\n"); richTextBox1.AppendText("SampleRate : "); richTextBox1.AppendText(wv.getSampleRate()+ ""); richTextBox1.AppendText("\n"); richTextBox1.AppendText("BitsPerSample : "); richTextBox1.AppendText(wv.getBitsPerSample() + ""); richTextBox1.AppendText("\n"); richTextBox1.AppendText("ByteRate : "); richTextBox1.AppendText(wv.getByteRate() + ""); richTextBox1.AppendText("\n"); richTextBox1.AppendText("BlockAlign : "); richTextBox1.AppendText(wv.getBlockAlign() + ""); richTextBox1.AppendText("\n"); richTextBox1.AppendText("AUDIO FORMAT(PCM:1) : "); richTextBox1.AppendText(wv.getAudioFormat() + ""); richTextBox1.AppendText("\n"); richTextBox1.AppendText("데이터 영역 크기 : "); richTextBox1.AppendText(wv.getDataSize() + ""); richTextBox1.AppendText("\n"); }
결과 예시
파일의 청크 ID, 영역 크기, FMT 스트링, FMT 영역 크기, 채널 개수, Sample Rate, BitsPerSample, ByteRate, BlockAlign, AudioFormat, 실제 데이터 영역 크기가 모두 제대로 가져와졌음을 확인할 수 있다. 다음에는 가져와진 PCM 데이터를 활용해서 파형 데이터로 변환하는 방법을 알아보고 이를 그래프로 출력해본다.
댓글 0개
댓글은 일회용 패스워드가 발급되며 사이트 이용 약관에 동의로 간주됩니다.
확인
Whitmemit 개인 일지 블로그는 개인이 운영하는 정보 공유 공간으로 사용자의 민감한 개인 정보를 직접 요구하거나 요청하지 않습니다. 기본적인 사이트 방문시 처리되는 처리 정보에 대해서는 '사이트 처리 방침'을 참고하십시오. 추가적인 기능의 제공을 위하여 쿠키 정보를 사용하고 있습니다. Whitmemit 에서 처리하는 정보는 식별 용도로 사용되며 기타 글꼴 및 폰트 라이브러리에서 쿠키 정보를 사용할 수 있습니다.
이 자료는 모두 필수 자료로 간주되며, 사이트 이용을 하거나, 탐색하는 경우 동의로 간주합니다.