간단한 음성 녹음을 하는 작업은 아래 포스팅을 통해서 확인한 후 아래 내용을 확인하면 좀 더 이해가 잘 될것입니다.
https://apple-document.tistory.com/180
AVAudioRecorder 이용해 음성 인식
사용자가 말하는 것을 인식하기 위해서는 AVAudioRecorder에서 제공하는 메서드를 통해서 확인할 수 있습니다.
오디오 레벨 데이터 받기
AVAudioRecorder에는 아래처럼 오디오의 레벨(세기) 미러링을 관리할 수 있는 변수와 메서드들이 존재합니다.
var isMeteringEnabled: Bool
이 속성은 오디오 레벨 미터링 데이터를 생성할지 여부를 나타내는 Bool 값입니다. true로 설정하면 녹음기가 오디오 레벨 미터링 데이터를 생성하도록 활성화합니다.
func updateMeters()
이 메서드는 오디오 녹음기의 모든 채널에 대해 average 및 peak 파워 값을 갱신합니다. 이 메서드를 호출하면 averagePower(forChannel:) 및 peakPower(forChannel:) 메서드에서 반환되는 값이 최신 오디오 데이터로 갱신됩니다.
func averagePower(forChannel: Int) -> Float
이 메서드는 지정된 오디오 채널의 average power를 decibels full-scale(dBFS) 단위로 반환합니다. average power 값은 오디오 신호의 평균적인 강도를 나타내며, updateMeters() 메서드를 호출한 이후의 데이터를 기반으로 합니다.
func peakPower(forChannel: Int) -> Float
이 메서드는 지정된 오디오 채널의 peak power를 decibels full-scale(dBFS) 단위로 반환합니다. peak power 값은 오디오 신호의 순간적인 최대 강도를 나타내며, updateMeters() 메서드를 호출한 이후의 데이터를 기반으로 합니다.
시나리오
그러면 이제 위에서 updateMeters() 메서드를 주기적으로 실행하여 audio recorder의 입력되는 음성 데이터를 최신 값으로 갱신하고 다음으로 averagePower(forChannel: Int)를 통해서 현재 audio recorder로 음성이 녹음되고 있는지 여부를 확인하면 되겠습니다.
averagePower 값 확인해보기
averagePower(forChannel:_)로부터 받은 데이터의 값은 주변 소음이 없을 경우 -50에서 -60 사이의 값이 나오는 것으로 확인하였습니다. 하지만 그 외에 유의미한 소리가 발생하게 되는 경우에는 -50 이상의 소리가 나오는 것을 확인하였습니다.
측정 방법
측정 방법은 다음과 같습니다. audio recorder로 음성 데이터를 받기 위해서는 반드시 AVAudioRecorder의 isMeteringEnabled 의 값을 true 로 설정해야지 측정이 가능합니다.
var recorder: AVAudioRecorder!
func recordAudio() {
do {
recorder = try AVAudioRecorder(url: captureURL, settings: [
AVFormatIDKey: Int(kAudioFormatMPEG4AAC),
AVSampleRateKey: 44100,
AVNumberOfChannelsKey: 1,
AVEncoderAudioQualityKey: AVAudioQuality.high.rawValue
])
recorder.delegate = self
recorder.isMeteringEnabled = true
recorder.record()
animationTimer = Timer.scheduledTimer(withTimeInterval: 0.2, repeats: true, block: { [unowned self] _ in
guard self.recorder != nil else { return }
self.recorder.updateMeters()
let power = self.recorder.averagePower(forChannel: 0)
print("\(power)")
})
} catch {
print(error.localizedDescription)
}
}
위에서 주변 소음이 없을 경우 -50에서 -60 사이의 값이 나오는 것으로 확인하였으므로 아래와 같은 공식을 만들 수 있겠습니다.
let power = min(1, max(0, 1 - abs(Double(self.recorder.averagePower(forChannel: 0)) / 50) ))
사용자가 말하는 동안만 녹음
이제 포스팅의 본 목적인 사용자가 말하는 동안에만 녹음을 하도록 만드는 기능을 만들어보겠습니다.
animationTimer에 0.2초마다 반복하는 Timer를 만들어 반복할 때마다 현 시점에서 측정된 audio recorder의 음성 세기 측정값을 audioPower에 저장합니다.
animationTimer = Timer.scheduledTimer(withTimeInterval: 0.2, repeats: true, block: { [unowned self] _ in
guard self.recorder != nil else { return }
self.recorder.updateMeters()
audioPower = max(0, 1 - abs(Double(self.recorder.averagePower(forChannel: 0)) / 50) )
})
다음으로 1.6초마다 반복하는 Timer를 만들어 반복하 때마다 현 시점에서 측정된 audio recorder의 음성 세기 측정값을 저장하는데 이전에 저장된 데이터의 값이 존재하지 않다면 prevAudioPower에 현 시점의 데이터를 저장합니다.
이후 preAudioPower에 1.6초마다 측정한 데이터와 0.2초마다 측정한 데이터의 소리가 무의미한 정도의 크기라면 녹음을 멈춥니다. 마지막으로 이전 값을 저장하는 prevAudioPower에 가장 최근에 측정한 power 값을 저장합니다.
recordingTimer = Timer.scheduledTimer(withTimeInterval: 1.6, repeats: true, block: { [unowned self] _ in
guard self.recorder != nil else { return }
self.recorder.updateMeters()
let power = max(0, 1 - abs(Double(self.recorder.averagePower(forChannel: 0)) / 50) )
if self.prevAudioPower == nil {
self.prevAudioPower = power
return
}
if let preAudioPower = self.prevAudioPower, preAudioPower < 0.25 && power < 0.175 {
self.stopRecordAudio()
}
self.prevAudioPower = power
})
이렇게 설정하게 된다면 이제 0.2초마다 음성 세기를 측정하고 1.6초동안 유의미한 음성 데이터가 측정되지 않는다면 음성 녹음을 끝낼 수 있게 됩니다.
https://github.com/iOS-Developer-KR/SwiftUIRepository2/tree/main/RepeatingRecordedAudioTest
'SwiftUI' 카테고리의 다른 글
View의 데이터 RefreshAction으로 새로고침하기 (Refreshable modifier) (0) | 2024.06.05 |
---|---|
confirmationDialog 로 하단 메뉴 나타나게하기 (0) | 2024.06.03 |
AVFoundation 이용한 음성 녹음하기 (0) | 2024.05.30 |
Sendable 프로토콜 (0) | 2024.05.25 |
AsyncPublisher (.value로 비동기적으로 데이터 처리) (0) | 2024.05.25 |