티스토리 뷰

iOS

iOS Speech - 라이브 오디오 변환 예시 앱 번역

꿀벌의달콤한여행 2021. 7. 7. 21:58

https://developer.apple.com/documentation/speech/recognizing_speech_in_live_audio

 

Apple Developer Documentation

 

developer.apple.com

위의 글을 번역했습니다~ 몇몇군데는 굉장한 의역이 들어가있을 수 있어서... 적당히 읽고 이상한 문장은 원문을 보시는 게 좋습니다 +_+.

이 예시 앱은 애플에서 제공하였고, 라이브 음성을 실시간으로 인식해서 글로 바꿔주는 앱입니다.

번역 시작.

Configure the Microphone Using AVFoundation

이 앱은 디바이스의 마이크와 소통하기 위해 AV Foundation을 사용한다. 특히, 앱은 앱의 오디오 상호작용과 시스템의 나머지 부분을 관리하기 위해 AVAudioSession의 싱글톤 객체를 설정한다. 그리고 AVAudioEngine 객체를 설정하여 마이크의 input을 검색한다.

private let audioEngine = AVAudioEngine()

Start Recording Button을 탭하면, 앱은 AVAudioSession의 싱글톤 객체를 받아서, 녹음을 위해 설정하고, active한 세션으로 만든다. 세션을 Activating 하는 것은 시스템이 앱이 마이크 리소스를 필요로 한다는 것을 알게 한다. 만약 사용자가 전화 등을 이유로 리소스가 사용 불가능하다면, setActive(_:options:) 메소드는 예외를 던진다.

// Configure the audio session for the app.
let audioSession = AVAudioSession.sharedInstance()
try audioSession.setCategory(.record, mode: .measurement, options: .duckOthers)
try audioSession.setActive(true, options: .notifyOthersOnDeactivation)
let inputNode = audioEngine.inputNode

세션이 일단 Active되면, 이 앱은 AVAudioInputNode 객체를 오디오엔진으로부터 검색해서 지역변수 inputNode에 저장하고 있다. inputNode는 현재 오디오 인풋의 통로(path)를 나타낸다(디바이스에 내장된 마이크나 연결된 헤드셋 등을 의미함).

녹화를 시작하기 위해, 앱은 inputNode에 탭을 설치하고 오디오 엔진을 시작한다. 오디오 엔진은 내부 버퍼에 샘플들을 수집하기 시작한다. 버퍼가 꽉 차면, 오디오 엔진은 제공된 코드 블록을 실행한다. 이 앱에서는 코드 블록이 request 오브젝트의 append(_:) 메소드에 샘플들을 직접 전달한다. 이는 오디오 샘플을 축적해두었다가(accumulate) 그것들을 speech recognition system에 전달한다.

// Configure the microphone input.
let recordingFormat = inputNode.outputFormat(forBus: 0)
inputNode.installTap(onBus: 0, bufferSize: 1024, format: recordingFormat) { (buffer: AVAudioPCMBuffer, when: AVAudioTime) in
    self.recognitionRequest?.append(buffer)
}

audioEngine.prepare()
try audioEngine.start()

Create the Speech Recognition Request

실시간 오디오에서 음성을 인식하기 위해, SFSpeechAudioBufferRecognitionRequest 오브젝트를 생성하고 설정한다. 이것이 인식 결과를 받으면, 앱은 그것에 맞춰 텍스트뷰를 업데이트 할 것이다. 이 앱은 request 오브젝트의 shouldReportPartialResults 프로퍼티를 true로 설정하여, 음성 인식 시스템이 결과가 인식되는 즉시 그 결과를 리턴하도록 한다.

// Create and configure the speech recognition request.
recognitionRequest = SFSpeechAudioBufferRecognitionRequest()
guard let recognitionRequest = recognitionRequest else { fatalError("Unable to create a SFSpeechAudioBufferRecognitionRequest object") }
recognitionRequest.shouldReportPartialResults = true

음성 인식 처리를 시작하기 위해, 앱은 자신의 SFSpeechRecognizer 오브젝트에 recognitionTask(with:resultHandler:) 메소드를 호출한다. 이 메소드는 음성 인식 시스템을 설정하고 오디오의 비동기적 처리를 시작하기 위해 인자로 전달된 request 오브젝트 안에 있는 정보를 사용한다. 이 메소드가 호출되고 잠시 뒤에, 앱은 오디오 샘플을 request 오브젝트에 붙이기(appending) 시작한다. Stop Recording Button을 탭하면 앱은 샘플을 더하는 것을 중지하고 음성 인식 프로세스를 종료시킨다.

앞서 설정한 request 오브젝트의 shouldReportPartialResults 프로퍼티가 true였기 때문에, recognitionTask(with:resultHandler:) 메소드는 부분적인 결과물들을 전달하기 위해 주기적으로 코드 블록을 실행한다. 앱은 코드 블록을 사용하여 result 오브젝트의 bestTranscription 프로퍼티 안에 있는 텍스트를 사용해서 텍스트 뷰를 업데이트 한다. 만약 결과 대신 에러를 받게 되면, 앱은 인식 프로세스를 모두 중단한다.

// Create a recognition task for the speech recognition session.
// Keep a reference to the task so that it can be canceled.
recognitionTask = speechRecognizer.recognitionTask(with: recognitionRequest) { result, error in
    var isFinal = false

    if let result = result {
        // Update the text view with the results.
        self.textView.text = result.bestTranscription.formattedString
        isFinal = result.isFinal
        print("Text \(result.bestTranscription.formattedString)")
    }

    if error != nil || isFinal {
        // Stop recognizing speech if there is a problem.
        self.audioEngine.stop()
        inputNode.removeTap(onBus: 0)


        self.recognitionRequest = nil
        self.recognitionTask = nil


        self.recordButton.isEnabled = true
        self.recordButton.setTitle("Start Recording", for: [])
    }
}

Responding to Availability Changes for Speech Recognition

음성 인식 서비스의 가용성은 언제든 달라질 수 있다. 몇몇 언어에 대해서는, 음성 인식이 애플 서버에 있어서 인터넷 연결을 필요로 한다. 만약 인터넷 연결이 끊기면 앱은 그것에 대응해야만 한다.

음성 인식 서비스의 가용성이 변할 때마다, SFSpeechRecognizer 오브젝트는 델리게이트에 이것을 알려준다. speechRecognizer(_:availabilityDidChange:) 메소드를 구현하고 델리게이트를 설정함으로써 이 가용성 변화에 대해 대응할 수 있다. 이 앱에서 서비스가 불가능해지면 이 메소드는 Start Recording Button을 disable 시키고 타이틀을 바꾼다. 서비스가 다시 사용 가능해지면, 이 메소드는 버튼을 살리고 타이틀을 복구시킨다.

public func speechRecognizer(_ speechRecognizer: SFSpeechRecognizer, availabilityDidChange available: Bool) {
    if available {
        recordButton.isEnabled = true
        recordButton.setTitle("Start Recording", for: [])
    } else {
        recordButton.isEnabled = false
        recordButton.setTitle("Recognition Not Available", for: .disabled)
    }
}

 

댓글