Skip to content

Sound Control

Song edited this page Aug 5, 2021 · 3 revisions

2021-08-02

by Song

Step 1 - Audio Session 활성화

App Delegate 메소드를 통한 Audio Session 활성화

  • didFinishLaunchingWithOptions 메소드를 통해 AudioSession 설정
  • 배경음과 효과음을 한번에 플레이할 수 있도록 하기 위해 Category는 ambient로 설정
func application(_ application: UIApplication,
                 didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    setAudioSession()
    // 기타 코드 생략
}

private func setAudioSession() {
    try? AVAudioSession.sharedInstance().setCategory(AVAudioSession.Category.ambient)
    try? AVAudioSession.sharedInstance().setActive(true)
}

Step 2 - Sound Station 객체 생성

각 씬에 필요한 Audio Player를 컨트롤 하는 객체 생성

Background Music

  • 여러 씬에 걸쳐 플레이되어야 한다는 특성에 따라 싱글톤 객체로 생성
  • 씬 교체 상황에 따라 MusicPlayer 생성 시 메모리 부하가 생겨 게임 ready 화면 로드에서 버벅임 발생 -> 미리 생성하도록 함
struct MusicStation {

    static let shared = MusicStation()
    private var mainMusicPlayer = AudioPlayerFactory.create(of: Music.main, mode: .music)
    private var gameMusicPlayer = AudioPlayerFactory.create(of: Music.game, mode: .music)

    func play(type: Music) {
        switch type {
        case .main:
            mainMusicPlayer?.currentTime = 0 // 음악을 맨 처음으로 되돌린다
            mainMusicPlayer?.play()
            gameMusicPlayer?.stop()
        case .game:
            gameMusicPlayer?.currentTime = 0
            gameMusicPlayer?.play()
            mainMusicPlayer?.stop()
        }
    }
    
    func stop() {
        mainMusicPlayer?.stop()
        gameMusicPlayer?.stop()
    }
}

Sound Effect

  • BGM과 달리 각 씬에 해당하는 효과음이 정해져 있다.
  1. 메뉴 화면
    • 광고 화면, 아이템 화면에서 각각 효과음 1개씩 필요
    • 하나의 효과음을 컨트롤하는 SingleSoundEffectStation 객체를 각 씬에서 공통적으로 사용
  2. 게임 화면
    • 게임에는 여러 효과음이 동시다발적으로 사용 됨
    • GameSoundEffectStation 객체로 필요한 모든 효과음 컨트롤

Audio Player Factory

  • Factory 메소드를 통해 AudioPlayer 생성 시의 중복 코드 개선
struct AudioPlayerFactory {
    enum Mode {
        case music
        case effect
    }
    
    static func create(of soundType: BundlePathIncludable, mode: Mode) -> AVAudioPlayer? {
        guard let bundlePath = soundType.bundlePath else { return nil }
        let url = URL(fileURLWithPath: bundlePath)
        let audioPlayer = try? AVAudioPlayer(contentsOf: url)
        audioPlayer?.numberOfLoops = mode == .music ? .max : 0
        return audioPlayer
    }
}
  • 각 Sound Enum 타입에 BundlePathIncludable 프로토콜을 적용하여 팩토리 메소드 사용
protocol BundlePathIncludable {
    var bundlePath: String? { get }
}

enum MainSoundEffect: BundlePathIncludable {
    case reward
    case upgrade
    
    var bundlePath: String? {
        switch self {
        case .reward:
            return Bundle.main.path(forResource: "reward", ofType: "wav")
        case .upgrade:
            return Bundle.main.path(forResource: "upgrade", ofType: "wav")
        }
    }
}

Clone this wiki locally