본문 바로가기
개발언어/NAudio

[NAuido] 3. Working With Files

by 창용이랑 2021. 7. 8.
728x90

파일 입출력에 관한 내용이다.

 

내가 필요한 것에 가장 가까운 편이라고 할 수 있겠다.

뭐 내가 필요한 것은 여기에다가 편집 기능까지 필요하지만, 그건 또 뒤에 나오겠지.

이 3장에서 다룰 것은 간단하게 얘기하면 파일을 읽는 방법과 파일을 쓰는 방법이다.

 

NAudio File Reader

 

NAudio에는 몇 가지의 FileReader 클래스가 있다. 이들은 각각이 WaveStream 클래스를 상속한(구현한) 클래스들로, 앞의 2강에서 입력으로서 기능했던 MP3FileReader 등을 말한다.

이들은 WaveFormat을 포함하고 있는데, 이는 그 오디오 파일의 속성(SampleRate, bps, channal count 등) 을 알려준다. 또한 reposition 기능을 지원하고 있다.

그리고 전체 오디오의 길이도 알려준다.

 

 

 

위 다섯 가지가 구현되어 있는 메인 리더들이다.

 

그리고 저들을 통합하는 것이 있으니 AudioFileReader 이다.

 

 

AudioFileReader

위 목록 중에서 마지막 Wma 를 제외한 넷을 합친 것이다.

ISampleProvider 인터페이스를 구현하며, 2강에서 봤다시피 볼륨 속성을 지니고 있어 볼륨 조절이 가능하다. 바꿔 말하면 위 네가지는 볼륨 조절 기능이 없어 추가적인 signal chain이 필요하다.

 

리턴은 IEEE float 형태의 PCM으로 보인다. 즉, 32bit이므로 16bit이나 24bit 일 경우 audioFileReader가 리턴하는 것을 그대로 저장하면 용량이 커질 수 있다.

사용하는데에는 매우 편리하지만 저장시에는 약간 유의할 필요가 있을 것 같다. 나는 이쪽을 사용할 듯.

 

 

WaveFileReader

기본적인 wav 파일을 읽는 리더 클래스이다. wav 파일이 그렇듯이 PCM이나 compressed를 다 읽을 수 있다.

file이나 stream에서 입력을 받아들일 수 있지만 완성된 파일이 아니면 reposition은 안된다.(당연하다)

RF64라는 것을 지원하는데 이는 헤더 크기를 변경하여 4GB 이상의 대용량 wav파일을 쓸 수 있게 해주는 것이다. 기존은 32bit라서 4GB가 최고 크기이다.

CueWaveFileReader 라는 클래스를 사용하여 metadate(아티스트, 제작일 등)를 읽어낼 수 있다.

 

 

MP3FileReader

자동으로 PCM으로 변환하는 기능을 제공한다.

디폴트로는 ACM codec을 사용하지만, 다른 것을 사용하도록 변환할 수 있다. 수동으로 추천되는 코덱은 nlayer.codeplax.com 의 오픈소스라고 한다. 아니면 DMO를 쓸 수도 있다고 한다.

기본적으로 윈도우 환경에서는 ACM이 제공되는게 일반적이지만, 경우에 따라 설치되지 않는 경우도 있다고 한다.

다만 이 부분은 중요하면서도 고려하기 꽤 어려운 부분이라는 생각이 들기는 한다...

 

단순히 파일 내용을 전부 읽어서 PCM으로 변환하기만 하는게 아니라, frame 단위로 읽을 수도 있다. 즉 raw frame에 접근할 수 있는 기능을 제공한다. ReadNextFrame 멤버함수를 사용하자.

이 기능을 이용해서 mp3 파일을 일부만 잘라낼(trim) 수도 있다. 원하는 시간대로 이동, frame 단위로 읽어서 그만큼만 쓰면 되니까. 다만 정확하게 ms 단위로 잘라낼 수는 없다.
 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public void TrimMp3File()
{
    using (var mp3FileReader = new Mp3FileReader("demo.mp3",
                                                 (wf) => new DmoMp3FrameDecompressor(wf)))
    using (var writer = File.Create("extract.mp3"))
    {
        var startPosition = TimeSpan.FromSeconds(60);
        var endPosition = TimeSpan.FromSeconds(90);
        // skip over the first 60 seconds
        mp3FileReader.CurrentTime = startPosition;
        while (mp3FileReader.CurrentTime < endPosition)
        {
            var frame = mp3FileReader.ReadNextFrame();
            if (frame == nullbreak;
            writer.Write(frame.RawData, 0, frame.RawData.Length);
        }
    }
}


Colored by Color Scripter
cs

 

읽어낼 시 table of contents 를 작성하는데, 이는 파일의 길이를 읽어들여서 이 오디오 파일의 전체 길이 등을 알아내기 위한 작업이다. 

그러다 보니, MP3의 목적과는 약간 엇나가게도 네트워크 스트리밍에서는 이 클래스를 쓸 수 없다. 나와는 큰 상관없지만.

또 ID3tag (v1,v2)를 찾아낼 수 있는 기능을 지원하지만 그 내용은 해석기능을 제공하지 않는다. 그게 필요하다면 taglib-sharp 라는 외부 라이브러리를 이용해야 한다.

 

 

MediaFoundationReader

vista부터 제공되는 media foundation 기능을 활용하는 클래스.

media foundation이 제공하는 모든 걸 읽을 수 있다. AAC나 WMA는 물론이고, mp4까지 읽을 수 있다. mp4를 읽을 경우 오디오 파일만 뽑아내는 형식이 된다.

역시 PCM을 리턴한다.

단점으로는 metadata에 엑세스할수 없다는 점.

 

 

NAudio File Writer

 

위의 reader와 반대되는 클래스이다. 이전 장에서는 출력이 사운드카드였다면, 여기서는 그 출력을 파일로 하는 것이다.

대체로 유의해야 하는 점은 dispose (소멸자)를 호출해야 한다는 점인데, using 문 안에서 writer를 쓰면 그 부분은 비교적 쉽게 해결된다.

 

주로 사용되는 클래스는 아래 네 가지이다.

WaveFileWriter

AiffFileWriter

WmaFileWriter

MediaFoundationEncoder

 

WaveFileWriter

쓰는 방식은 꽤 여러가지가 존재한다.

 

 

가장 기본적인 방식은 위처럼 reader에서 받은 데이터를 (혹은 signal chain의 결과로 나온 데이터를. IWaveProvider이기만 하면 된다) createWaveFile 로 출력하는 거다. 다만 이 경우 용량이 크므로,  createWaveFile16 함수도 있다.

다만 이 경우 무제한으로 생성되는 provider는 사용해선 안된다. 그 경우에는 다른 방법으로 생성해야 한다.

 

일반 io 함수인 Write 함수나, WriteSample 함수도 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
public void CreateBlankWavFile()
{
    var format = new WaveFormat(160001);
    using (var writer = new WaveFileWriter("blank.wav", format))
    {
        var oneSecondSilence = new byte[format.AverageBytesPerSecond];
        for (int n = 0; n < 10; n++)
        {
            writer.Write(oneSecondSilence, 0, oneSecondSilence.Length);
        }
    }
}


Colored by Color Scripter
cs

 

 

위는 빈 소리를 (10초) 만드는 코드이다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public void CreateToneWaveFileEvenBetter()
{
    var sampleRate = 44100;
    var channels = 1;
    var seconds = 15;
    var signalGenerator = new SignalGenerator(sampleRate, channels);
    signalGenerator.Type = SignalGeneratorType.Sin;
    signalGenerator.Frequency = 1000;
    signalGenerator.Gain = 0.25;
    var offsetProvider = new OffsetSampleProvider(signalGenerator);
    offsetProvider.TakeSamples = sampleRate * channels * seconds;
    WaveFileWriter.CreateWaveFile16("tone15.wav", offsetProvider);
}


Colored by Color Scripter
cs

 

위는 특정 시그널을 만들어서 집어넣는 함수로, 위 코드는 개선버전이 있는(효과는 동일하지만 코드가 깔끔해진다) 안 좋은 코드이지만 writesample을 보여주려고 넣었다.

 

 

wav 파일 합치기

 

여러 개의 wav 파일을 합친다. 단 여러개의 wav 파일의 포맷(WaveFormat 클래스 내용 전체) 는 반드시 동일해야 한다.

sample rate가 다르다거나 하면 합칠수 없다는 뜻이다. 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
public static void Concatenate(string outputFile, IEnumerable<string> sourceFiles)
{
    var buffer = new byte[44100 * 2];
    WaveFileWriter waveFileWriter = null;


    try
    {
        foreach (var sourceFile in sourceFiles)
        {
            using (var reader = new WaveFileReader(sourceFile))
            {
                if (waveFileWriter == null)
                {
                    // first time in create new Writer
                    waveFileWriter = new WaveFileWriter(outputFile, reader.WaveFormat);
                }
                else
                {
                    if (!reader.WaveFormat.Equals(waveFileWriter.WaveFormat))
                    {
                        throw new InvalidOperationException(
                            "Can't concatenate WAV Files that don't share the same format");
                    }
                }


                int read;
                while ((read = reader.Read(buffer, 0, buffer.Length)) > 0)
                {
                    waveFileWriter.Write(buffer, 0, read);
                }
            }
        }
    }
    finally
    {
        if (waveFileWriter != null)
        {
            waveFileWriter.Dispose();
        }
    }
}


Colored by Color Scripter
cs

 

 

뒤에 mp3도 섞어서 합치고 하는 걸 만들겠지만 일단은 이렇다.

 

 

출처 : https://m.blog.naver.com/luku756/221894351254

'개발언어 > NAudio' 카테고리의 다른 글

[NAudio] 6. Recording Audio  (0) 2021.07.08
[NAudio] 5. Working With Codecs  (0) 2021.07.08
[NAuidio] 4. Changing Wave Formats  (0) 2021.07.08
[NAudio] 2. Audio Playback  (0) 2021.07.08
[NAudio] 1. Introducing NAudio  (0) 2021.07.08