音源ファイルを繰り返し再生する方法


#目次
* 前提確認
* 音源再生の処理
* 音源を繰り返し再生する処理
* 最終的なコード
* まとめ

#前提確認
MacOS Catalina 10.15.4
Xcode 12.1
Swift version 5

お急きの方は[サンプルコード](https://github.com/ShenQC-gif/RepeatSounds/tree/main)をどうぞ。

#音源再生の処理

とりあえず音源をただ再生するだけのコードを書いてみましょう。

音源を再生するには`AVAudioPlayer`クラスのインスタンスに、再生するファイルのURL(ファイルがどこにあるか)を初期値として渡す必要があります。
URLと言っても、ファイル名と拡張子(mp3とか)を`Bundle.main.url`に渡せば勝手に特定してくれます。
ちなみに、`Bundle`とは色んなリソース(音源とか画像とか)を保存している領域のことです。
最後にインスタンメソッドの`play()`を実行すれば、音源ファイルが再生されます。

一つ一つ見ていきましょう。

まずは`AVFoundation`を`import`します。
`AVFoundation`とは音声や画像、動画を制御するためのフレームワークです。

```swift
import AVFoundation
```

次に`AVAudioPlayer`クラスのインスタンスを宣言します。

```swift
import AVFoundation

class SoundPlayer {
    var audioPlayer: AVAudioPlayer! //AVAudioPlayerクラスのインスタンス、この時点では初期化しない
}
```

次にメソッドを用意し、`Bundle.main.url`に音源のファイル名と拡張子を与えてURLを特定させます。

```swift
import AVFoundation

class SoundPlayer {
    var audioPlayer: AVAudioPlayer! 

    func playSound(fileName: String, extentionName: String) {

        let soundURL = Bundle.main.url(forResource: fileName, withExtension: extentionName) //引数としてファイル名と拡張子与えて、Bundle内でURLを特定

     }
        
 }
```

最初に宣言した`AVAudioPlayer`クラスのインスタンスにURLを渡し、`play()`メソッドを実行します。
ただし、URLを渡す処理は`Error`が発生し得るとため、実行するには`do構文`を使う必要があります。(`do構文`については[こちらの記事](https://qiita.com/ShenQC/items/3def8c64b3617b13f106)を参照)

```swift
import AVFoundation

class SoundPlayer {
    var audioPlayer: AVAudioPlayer! 

    func playSound(fileName: String, extentionName: String) {

        let soundURL = Bundle.main.url(forResource: fileName, withExtension: extentionName)

        do {
            audioPlayer = try AVAudioPlayer(contentsOf: soundURL!) //URLを渡す
            audioPlayer?.play()
        } catch {
            print("error")
        }
    }
}
```

さて、これで音源を再生する処理は書けました。
とりあえず`ViewController内`でこの処理を実行しましょう。
今回はルーレットの音源を再生することにします。

```swift:

import UIKit
import AVFoundation

class ViewController: UIViewController {

    private var soundPlayer = SoundPlayer()

    @IBAction func playOneTime(_ sender: Any) {
        soundPlayer.playSound(fileName: "roulette", extentionName: "mp3")
     }
}

class SoundPlayer {
    var audioPlayer: AVAudioPlayer! 

    func playSound(fileName: String, extentionName: String) {

        let soundURL = Bundle.main.url(forResource: fileName, withExtension: extentionName)

        do {
            audioPlayer = try AVAudioPlayer(contentsOf: soundURL!)
            audioPlayer?.play()
        } catch {
            print("error")
        }
    }
}
```

良ければ何か音声ファイルを用意して、ここまで上手くできたか確認してみてください。
上手くできていれば、本題の繰り返し再生の処理をを見てみましょう。

#音源を繰り返し再生する処理

繰り返し再生する方法は、非常に簡単です。
実は`AVAudioFoundation`クラスに`numberOfLoops`という`Int型`のプロパティがあります。
このプロパティが正の数ならその回数分繰り返し、0なら繰り返さず、負の数なら繰り返し再生することができます。
ということで、負の数にしてあげればいいですね。

```swift
import AVFoundation

class SoundPlayer {
    var audioPlayer: AVAudioPlayer! 

    func playSound(fileName: String, extentionName: String, isRepeat: Bool) { //isRepeatで繰り返すかどうか判断
        let soundURL = Bundle.main.url(forResource: fileName, withExtension: extentionName)

        do {
            audioPlayer = try AVAudioPlayer(contentsOf: soundURL!)
            if isRepeat { //isRepeatがtrueなら
                audioPlayer.numberOfLoops = -1 //numberOfLoopsを負の数にして繰り返し再生
                print("Sound is repeated")
            } else { //それ以外なら
                audioPlayer.numberOfLoops = 0 //numberOfLoopsを0にして繰り返さない
                print("Sound is played one time") 
            }
            audioPlayer?.play()
        } catch {
            print("error")
        }
    }
}
```

最後におまけとして、繰り返し再生される音源を`stop()`で止める処理も追加します。

```swift
func stopSound() {
        audioPlayer.stop()
        print("Sound is stopped")
 }
```

#最終的なコード
それではコードをまとめてみましょう。
1度だけ再生する処理と繰り返し再生する処理、再生を停止する処理を実行するボタンを用意しました。

```swift
import UIKit
import AVFoundation

class ViewController: UIViewController {

    private var soundPlayer = SoundPlayer()

    @IBAction func playOneTime(_ sender: Any) {
        soundPlayer.playSound(fileName: "roulette", extentionName: "mp3", isRepeat: false) //1度だけ再生
        }

    @IBAction func repeatSound(_ sender: Any) {
        soundPlayer.playSound(fileName: "roulette", extentionName: "mp3", isRepeat: true) //繰り返し再生
    }

    @IBAction func stopSound(_ sender: Any) {
        soundPlayer.stopSound() //再生を停止
    }

}


class SoundPlayer {
    var audioPlayer: AVAudioPlayer!

    func playSound(fileName: String, extentionName: String, isRepeat: Bool) {
        let soundURL = Bundle.main.url(forResource: fileName, withExtension: extentionName)

        do {
            audioPlayer = try AVAudioPlayer(contentsOf: soundURL!)
            if isRepeat {
                audioPlayer.numberOfLoops = -1
                print("Sound is repeated")
            } else {
                audioPlayer.numberOfLoops = 0
                print("Sound is played one time")
            }
            audioPlayer?.play()
        } catch {
            print("error")
        }
    }

    func stopSound() {
        audioPlayer.stop()
        print("Sound is stopped")
    }
}

```

<img width="400" alt="simulator_screenshot_313E70AA-4478-4E53-BA8C-4C12BAF1C964.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/843253/77024a9a-181a-5597-eb77-2d23ac3e0765.png">


#まとめ

いかがでしたでしょうか。意外とシンプルだったかと思います。
`AVAudioPlayer`についてのReferenceを読めば他にも色々できそうです。
是非音源に関する処理も使えるようになって、にぎやかなアプリを作ってみてください！