(第2回)英語文字起こしアプリ〜IT業界2年目 ネイティブアプリに挑戦〜

(第2回)英語文字起こしアプリ〜IT業界2年目 ネイティブアプリに挑戦〜

目次

はじめに
再生機能の追加
録音時、再生時の状態制御
再生完了時の状態制御
次回予告

はじめに

前回はプロジェクトの開始〜録音機能の追加までを行いました。
今回は前回の次回予告通り再生機能の追加を行います。

再生機能の追加

まずは前回の録音ボタンと同様に再生ボタンをタッチされた時の処理を追加します。
内容はほとんど前回と同じになります。

class ViewController : UIViewController {
  ・
  ・
  ・
    // 録音・再生クラス
    var avAudioRecorder : AVAudioRecorder!
    var avAudioPlayer : AVAudioPlayer!

    // 状態フラグ
    var isRecording : Bool = false
    var isPlaying : Bool = false

  ・
  ・
  ・

    // 再生ボタンをタッチ
    @objc func TouchPlayingButton() {
        print("PlayingButton is Touched!")
        // 再生を開始するかチェック
        if (!isPlaying) {
            // 再生されていない場合
            // 録音ファイルの保存先取得
            var folderPaths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
            let filePath = folderPaths[0].appendingPathComponent("recorded.m4a")
            
            // 再生クラスの初期化
            avAudioPlayer = try! AVAudioPlayer(contentsOf: filePath)
            avAudioPlayer.play()
            
            // ボタンの表示を変更
            currentStatusImage.image = UIImage(named: "speaker_playing")
            playingButton.setTitle("stop", for: .normal)
            isPlaying = true
        } else {
            // 再生中の場合
            // 再生をストップ
            avAudioPlayer.stop()
            
            // ボタンの表示を変更
            currentStatusImage.image = UIImage(named: "speaker")
            playingButton.setTitle("play", for: .normal)
            isPlaying = false
        }
    }
}

画面中央に表示されるcurrentStatusImageに使用している画像は前回同様にassetsに追加してください。
(上記のコードだと「speaker.png」と「speaker_playing.png」を追加します)

コードの説明ですが、再生に使用するクラスAVAudioPlayerと再生中を表すフラグを宣言します。
AVAudioPlayerはインスタンスの作成時に再生するリソース情報を渡す必要があるので
再生ボタンのタッチ処理時に初期化するようにしています。

これで録音したファイルの再生ができるようになりました。

録音時、再生時の状態制御

ただし、このままだと「録音中に再生」や「再生中に録音」が出来てしまうのでそのあたりの制御を行います。
制御としては「録音が開始されると再生ボタンを非表示」、「録音が停止されると再生ボタンを再表示」という処理を行います。
再生ボタンがタッチされた時も同様に行います。

class ViewController : UIViewController {
  ・
  ・
  ・
    // 録音ボタンをタッチ
    @objc func TouchRecordingButton() {
        print("RecodingButton is Touched!")
        // 録音を開始するかチェック
        if (!isRecording) {
            ・
            ・
            ・
            // 表示を変更
            currentStatusImage.image = UIImage(named: "microphone_recording")
            recordingButton.setTitle("stop", for: .normal)
            isRecording = true
            playingButton.isEnabled = false
            playingButton.isHidden = true
        } else {
            
            ・
            ・
            ・
            // ボタンの表示を変更
            currentStatusImage.image = UIImage(named: "microphone")
            recordingButton.setTitle("record", for: .normal)
            isRecording = false
            playingButton.isEnabled = true
            playingButton.isHidden = false
        }
    }

    // 再生ボタンをタッチ
    @objc func TouchPlayingButton() {
        print("PlayingButton is Touched!")
        // 再生を開始するかチェック
        if (!isPlaying) {
            ・
            ・
            ・
            // ボタンの表示を変更
            currentStatusImage.image = UIImage(named: "speaker_playing")
            playingButton.setTitle("stop", for: .normal)
            isPlaying = true
            recordingButton.isEnabled = false
            recordingButton.isHidden = true
        } else {
            // 再生中の場合
            // 再生をストップ
            avAudioPlayer.stop()
            
            // ボタンの表示を変更
            currentStatusImage.image = UIImage(named: "speaker")
            playingButton.setTitle("play", for: .normal)
            isPlaying = false
            recordingButton.isEnabled = true
            recordingButton.isHidden = false
        }
    }
}

また再生機能は再生されるファイルが存在していないと機能しないので
再生するファイルが存在しているかを確認し、存在していなければアラートを出すようにします。

    // 再生ボタンをタッチ
    @objc func TouchPlayingButton() {
        print("PlayingButton is Touched!")
        // 再生を開始するかチェック
        if (!isPlaying) {
            // 再生されていない場合
            // 録音ファイルの保存先取得
            var folderPaths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
            let filePath = folderPaths[0].appendingPathComponent("recorded.m4a")
                
             // 録音ファイルが存在するかどうか
             if (!FileManager.default.fileExists(atPath: filePath.absoluteString)) {
                 // 存在しない場合

                 // アラート表示
                 let alert = UIAlertController(title: nil, message: "録音ファイルが存在しません", preferredStyle: .alert)
                 let okAction = UIAlertAction(title: "OK", style: .default, handler: nil)
                 alert.addAction(okAction)
                 self.present(alert, animated: true, completion: nil)
                 return
            }

            // 再生クラスの初期化
            avAudioPlayer = try! AVAudioPlayer(contentsOf: filePath)
            avAudioPlayer.play()
            ・
            ・
            ・
       }
   }

再生完了時の状態制御

次に再生は録音と異なり、ボタンで停止しなくてもファイルが最後まで再生されると画面制御を戻すようにします。
再生が完了した場合はAVAudioPlayerがデリゲートに通知を行うのでその通知をViewControllerが受け取れるようにします。
AVAudioPlayerに対応するデリゲートはAVaudioPlayerDelegateになります。

class ViewController: UIViewController, AVAudioPlayerDelegate{
    ・
    ・
    ・
    // 再生ボタンをタッチ
    @objc func TouchPlayingButton() {
        print("PlayingButton is Touched!")
        // 再生を開始するかチェック
        if (!isPlaying) {
            ・
            ・
            ・
           // 再生クラスの初期化
           avAudioPlayer = try! AVAudioPlayer(contentsOf: filePath)
           avAudioPlayer.delegate = self
           avAudioPlayer.play()

            ・
            ・
            ・
        }
    }

    // 再生が停止した時に呼び出されるメソッド
    func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) {
        // ボタンの表示を変更
        currentStatusImage.image = UIImage(named: "speaker")
        playingButton.setTitle("play", for: .normal)
        isPlaying = false
        isPlaying = false
        
        recordingButton.isEnabled = true
        recordingButton.isHidden = false
    }
}

通知を受け取ると通知の内容に応じて特定のメソッドが呼ばれます。
今回の場合、再生が完了したら画面制御の処理を行いたいのでaudioPlayerDidFinishPlayingメソッドを用意しています。
実際にシミュレータで動かしてみると以下のようになります。

次回予告

再生・録音の機能が整ったので、次回は録音したファイルの文字起こしを行ないます。