AVSpeechSynthesizer を使って音声読み上げをする
iOS アプリで音声読み上げをやりたくなったので調べてみたメモです。
AVSpeechSynthesizer というクラスを使うと簡単に読み上げができました。
- AVSpeechSynthesizer を生成
- 読み上げるテキストを引数に AVSpeechUtterance を生成
- voice プロパティに言語指定した Voice を指定(ここでは ja-JP)
- 読み上げ
という順で処理を行います。コードにすると下記の通り。
let talker = AVSpeechSynthesizer()
let utterance = AVSpeechUtterance(string: text)
utterance.voice = AVSpeechSynthesisVoice(language: "ja-JP")
talker.speakUtterance(utterance)
テキストをひとつずつ読み上げていく
ただ上記の処理では複数のテキストを読み上げる場合などに同時に読み上げてしまいます。そこでシングルトンを作って読み上げ処理を1つずつ行うようにしてみました。
やっていることは簡単で、
-
- 読み上げたいテキストをリストに追加
- リストにあるテキストを音声読み上げ(再生は同時にひとつだけ)
- 読み上げ終了、2に戻る
をやっています。
import UIKit
import AVFoundation
final class TtsManager: NSObject, AVSpeechSynthesizerDelegate {
static let sharedInstance = TtsManager()
var texts = [String]()
let talker = AVSpeechSynthesizer()
private override init() {
super.init()
talker.delegate = self
}
func append(text: String) {
texts.append(text)
if texts.count == 1 {
play(texts[0])
}
}
// MARK: AVSpeechSynthesizerDelegate
func speechSynthesizer(synthesizer: AVSpeechSynthesizer, didFinishSpeechUtterance utterance: AVSpeechUtterance) {
if texts.count > 0 {
texts.removeFirst()
if texts.count > 0 {
play(texts[0])
}
} else {
// speech finished
}
}
func clear() {
texts.removeAll()
if talker.speaking {
talker.stopSpeakingAtBoundary(.Immediate)
}
}
private func play(text: String) {
let utterance = AVSpeechUtterance(string: text)
utterance.voice = AVSpeechSynthesisVoice(language: "ja-JP")
talker.speakUtterance(utterance)
}
}