CrossBridge Lab

技術ネタ、デバイスネタを...

watchOS3で追加されたAVFoundataionを使って音を鳴らす&喋らす

はじめに

今回の記事はiOS その3 Advent Calendar 2016の12/19分となります。

watchOS3でAVFoundataionフレームワークが追加されました。watchOS3ではSceneKit/SpriteKitも追加されており、(楽しいかどうかはおいておいて)AppleWatchで遊ぶゲームアプリが作りやすくなりました。

AVAudioEngineで音声ファイルを鳴らす

プロジェクトに音声ファイルを追加する

まずはファイルをプロジェクトに追加します。注意しなければいけないのは WatchKit Extension に追加するということです。WatchKit Appの方ではないので気をつけましょう。

f:id:crossbridge-lab:20161218232956j:plain

AVAudioEngine、AVAudioPlayerNode、AVAudioFileを使って再生する

使うAVFoundataionのクラスは AVAudioEngineAVAudioPlayerNodeAVAudioFile の3つです。

まずはコードを。

    var audioEngine: AVAudioEngine!
    var audioPlayerNode: AVAudioPlayerNode!
    var audioFile: AVAudioFile!
    
    override func awake(withContext context: Any?) {
        super.awake(withContext: context)
        
        guard let path = Bundle.main.path(forResource: "se", ofType: "caf") else {
            return
        }
        
        audioEngine = AVAudioEngine() // (1)
        audioPlayerNode = AVAudioPlayerNode() // (2)
        let audioPath = URL(fileURLWithPath: path)
        audioFile = try! AVAudioFile(forReading: audioPath) // (3)
        audioEngine.attach(audioPlayerNode) // (4)
        audioEngine.connect(audioPlayerNode,
                            to: audioEngine.mainMixerNode,
                            format: audioFile.processingFormat) // (5)
        audioEngine.prepare() // (6)
        try! audioEngine.start() // (7)
    }
    
    @IBAction func hanldeButton1() {
        audioPlayerNode.scheduleFile(audioFile, at: nil) { () -> Void in // (8)
            print("complete")
        }
        audioPlayerNode.play() // (9)
    }

以下の手順で音声ファイルを再生します。

  1. AVAudioEngineのインスタンスを生成する
  2. AVAudioPlayerNodeのインスタンスを生成する
  3. Bundle内の音声ファイルを指定してAVAudioFileのインスタンスを生成する
  4. AVAudioEngineのattach(_:)メソッドでAVAudioPlayerNodeをアタッチする
  5. AVAudioEngineのconnect(_:to:format:)メソッドでNode同士を接続する
  6. AVAudioEngineのprepare()メソッドとstart()メソッドで処理を開始させる
  7. AVAudioPlayerNodeのscheduleFile(_:at:completionHandler)メソッドで音声ファイルの再生をスケジュールする
  8. AVAudioEngineのstartメソッドで再生する

AudioUnitは使うことができない

なおAPI Diffsの中にAVAudioUnitの文字を見つけて「おっ!」となったのですが、どうやらまだAudioUnitを使うことはできないようです。Preset のクラスはありますが、肝心の本体のクラスがありません。(AVAudioUnitReverbPresetクラスはあるがAVAudioUnitReverbクラスが無い)

残念ながらエフェクトをかけて再生することはできないようです。ただ Preset のクラスが追加されたということは将来的に使えるようになるかもしれませんね。ハードウェアが進化したApple Watch Series3が発売されるときでしょうか?!

AppleWatchに喋らせる

AVSpeechSynthesizer

次にAppleWatchに喋らせる方法です。喋らせるにはAVSpeechSynthesizerクラスを使います。

まずはコードを。

class InterfaceController: WKInterfaceController {
    
    var speechSynthesizer: AVSpeechSynthesizer!
    
    override func awake(withContext context: Any?) {
        super.awake(withContext: context)
        
        AVSpeechSynthesisVoice.speechVoices().forEach {
            print($0.language)
        }
        
        speechSynthesizer = AVSpeechSynthesizer() // (1)
        speechSynthesizer.delegate = self // (2)
    }
    
    @IBAction func handleButton2() {
        let speechUtterance = AVSpeechUtterance(string: "おはよう。こんにちは。こんばんは") // (3)
        speechUtterance.voice = AVSpeechSynthesisVoice(language: "jp-JP")  // (4)
        speechUtterance.rate = AVSpeechUtteranceDefaultSpeechRate
        speechUtterance.pitchMultiplier = 1.0   // [0.5 - 2] Default = 1
        speechSynthesizer.speak(speechUtterance)  // (5)
    }
}

extension InterfaceController: AVSpeechSynthesizerDelegate {
    func speechSynthesizer(_ synthesizer: AVSpeechSynthesizer,
                           didStart utterance: AVSpeechUtterance) {
        print("再生開始")
    }
    
    func speechSynthesizer(_ synthesizer: AVSpeechSynthesizer,
                           didFinish utterance: AVSpeechUtterance) {
        print("再生終了")
    }
}

以下の手順で音声ファイルを再生します。

  1. AVSpeechSynthesizerのインスタンスを生成する
  2. AVSpeechSynthesizerのdelegateを設定する(必須ではない)
  3. 喋らせる文章を指定してAVSpeechUtteranceのインスタンスを生成する
  4. AVSpeechUtteranceのvoiceプロパティにAVSpeechSynthesisVoice(言語)を設定する
  5. AVSpeechSynthesizerのspeakメソッドで喋らせる(再生する)

なお上記のサンプルでは(4)と(5)の間に再生させるスピードrateと、再生するピッチpitchMultiplierを指定しています。これらの値を変更することで早く喋らせたり声の高さを変えることができます。

AVSpeechSynthesizerDelegateクラスのメソッドで再生開始や再生終了のタイミングで処理を行うことができます。

喋らせることができる言語

AVSpeechSynthesisVoiceクラスのspeechVoicesメソッドで一覧を取得することができます。

AVSpeechSynthesisVoice.speechVoices().forEach {
    print($0.language)
}

上記のコードをwatchOS3.1.1で実行すると以下の結果を以下の言語が利用できることがわかります。今後アップデートで言語が増える可能性はあります。

ar-SA
cs-CZ
da-DK
de-DE
el-GR
en-AU
en-GB
en-US
es-ES
es-MX
fi-FI
fr-CA
fr-FR
he-IL
hi-IN
hu-HU
id-ID
it-IT
ja-JP
ko-KR
nl-NL
no-NO
pl-PL
pt-BR
pt-PT
ro-RO
ru-RU
sk-SK
sv-SE
th-TH
tr-TR
zh-CN
zh-HK
zh-TW

まとめ

AVFoundataionが追加されたことで音声ファイルの再生などできることが増えました。増えましたが・・・そんなにAppleWatchで音を鳴らす機会ってあるんですかね(元も子もない)

元も子もない一環でSprikeKitで作ったFlappyBirdもよければ御覧ください。AppleWatchでFlappyBirdをして楽しいかどうか・・・Starをもらえるとこの無意味な鳥も浮かばれます。

github.com

あと、よくよく考えたらwatchOSの記事なのでiOS Advent Calendarでよかったのか!?