[swift] 마이크로부터 입력된 소리의 데시벨(DB) 읽기

이번에는 Swift를 사용하여 마이크로부터 입력된 소리의 데시벨(DB)을 읽는 방법에 대해 알아보겠습니다.

AVFoundation 프레임워크 사용하기

AVFoundation 프레임워크는 오디오와 비디오 컨텐츠를 캡처, 재생, 편집하는 데 사용되는 Apple의 프레임워크입니다. 우리는 이 프레임워크를 사용하여 마이크로부터 입력된 오디오를 처리하고 데시벨을 계산할 수 있습니다.

먼저, AVFoundation을 프로젝트에 추가해야 합니다. 프로젝트 네비게이터에서 프로젝트 파일을 선택하고, 오른쪽 패널에서 “Build Phases” 탭을 클릭한 다음 “Link Binary With Libraries”를 찾아서 AVFoundation을 추가하세요.

다음으로, AVAudioEngine 클래스를 사용하여 데시벨을 읽을 준비를 해야 합니다. 다음 코드를 참고하세요:

import AVFoundation

class SoundMeter {

    let engine = AVAudioEngine()
    let mic = AVAudioInputNode()
    var meter: AVAudioEngineManualRenderingBlock?

    init() {
        let inputFormat = engine.inputNode.inputFormat(forBus: 0)
        engine.attach(mic)
        engine.connect(mic, to: engine.mainMixerNode, format: inputFormat)
        engine.prepare()
        try? engine.start()
    }

    func startMetering(completion: @escaping (Float) -> Void) {
        let bufferSize: AVAudioFrameCount = 512
        let renderFormat: AVAudioFormat = mic.outputFormat(forBus: 0)
        meter = AVAudioEngineManualRenderingBlock(
            format: renderFormat,
            maximumFrameCount: bufferSize
        ) { [weak self] _, _, frameCount, _ in
            guard let self = self else { return .noData }
            let buffer = AVAudioPCMBuffer(pcmFormat: renderFormat, frameCapacity: frameCount)!
            let inputBlock: AVAudioEngineManualRenderingBlock = {
                _, obuf, fcnt, _ in
                let ptr = obuf.bindMemory(to: Float.self, capacity: Int(fcnt) * 2)
                for i in 0..<Int(fcnt) {
                    ptr[i] = 0
                }
                return .success
            }

            let status = self.mic.render(
                into: buffer,
                frameCount: frameCount,
                pull: inputBlock
            )
            guard status == .success else { return .noData }
            let samples = Array(UnsafeBufferPointer(start: buffer.floatChannelData![0], count: Int(frameCount)))
            let rms = sqrt(samples.reduce(0) { sum, sample in sum + sample*sample } / Float(frameCount))
            let db = 20 * log10(rms)
            completion(db)
            return .success
        }
        try! mic.setManualRenderingInputPCMFormat(renderFormat, sampleRateConverterQuality: .medium)
        try! mic.connect(to: engine.mainMixerNode, format: inputFormat)
        mic.installTap(onBus: 0, bufferSize: bufferSize, format: inputFormat) { buffer, _ in
            try? self.meter?.produce(buffer: buffer, at: buffer.framePosition)
        }
    }

    func stopMetering() {
        mic.removeTap(onBus: 0)
        meter = nil
    }
}

위의 코드는 AVAudioEngine을 사용하여 데시벨을 읽는 SoundMeter 클래스를 정의합니다. 이 클래스를 이용하면 데시벨 값을 측정할 수 있습니다.

사용 예시

SoundMeter 클래스를 이용하여 데시벨을 읽는 예시 코드입니다:

let meter = SoundMeter()

meter.startMetering { decibels in
    print("데시벨: \(decibels)")
}

// 데시벨 측정을 멈추려면 다음 코드를 실행합니다:
// meter.stopMetering()

위의 코드에서는 startMetering(completion:) 메서드를 호출하여 데시벨 값을 지속적으로 가져옵니다. 데시벨 값을 얻을 때마다 completion 블록이 호출되고, 이 블록에서 적절하게 데시벨 값을 처리하면 됩니다.

결론

이제 Swift를 사용하여 마이크로부터 입력된 소리의 데시벨을 읽는 방법을 알아보았습니다. AVFoundation 프레임워크를 활용하여 손쉽게 오디오 데이터를 처리하고 데시벨 값을 계산할 수 있습니다.