Overview

Soundable is a tiny library that uses AVFoundation to manage the playing of sounds in iOS applications in a simple and easy way. You can play single audios, in sequence and in parallel, all is handled by the Soundable library and all they have completion closures when playing finishes.

Requirements

  • iOS 9.0+
  • Xcode 10.0+
  • Swift 4.2+

Install

Cocoapods

Add this line to your Podfile:

pod 'Soundable', '~> 1.0'

Carthage

Add this line to your cartfile:

github "ThXou/Soundable" ~> 1.0

And then follow the official documentation about Adding frameworks to an application.

Setup

Import Soundable in your source file:

import Soundable

Playing Sounds

Single Sounds

Soundable provides multiple ways to play a sound. The first one is by creating a Sound object:

let sound = Sound(fileName: "guitar-chord.wav")
sound.play()

This will play the guitar-chord.wav track located in the main bundle of the application. If you have multiple bundles in your application, use the fileName:bundle: function to create the sound. If you have an URL object instead, you can use:

let sound = Sound(url: url)
sound.play()

The second one is using the Soundable class functions:

Soundable.play(fileName: "guitar-chord.wav")

And the third is for the laziest people. Put the file name in a String object and just tryToPlay:

"guitar-chord.wav".tryToPlay()

This is possible due to a simple String category packed with the library that will try to play an audio file located in the application's main bundle with the specified name.

If you have an URL object and are lazzy too, you can use it like this also:

url.tryToPlay()

All these functions have their respective completion closures that passes an Error object if something wrong have happened in the process:

sound.play { error in
    if let error = error {
        print("error: \(error.localizedDescription)")
    }
}

Multiple Sounds

Playing In Parallel

To play audios in parallel your only have to worry on call the play function in all the audios you want to play in parallel, all the completion closures will be called when the audio finished playing.

Playing In Sequence

Soundable supports the playing of audios in sequence, and as for a single sound, you have multitple ways to play audios in sequence. The first one is the best (IMO):

let sound1 = Sound(fileName: "guitar-chord.wav")
let sound2 = Sound(fileName: "rain.mp3")
let sound3 = Sound(fileName: "water-stream.wav")

let sounds = [sound1, sound2, sound3]
sounds.play()

Or:

[sound1, sound2, sound3].play()

Can you play and array of Sound objects?. Yes. This is thanks to a simple Sequence extension packed with the library that only accepts Sound objects to play.

The second one is using the Soundable class functions, again:

Soundable.play(sounds: [sound1, sound2, sound3])

And the third is using the SoundsQueue object to create a queue of sounds:

let soundsQueue = SoundsQueue(sounds: [sound1, sound2, sound3])
soundsQueue.play()

As for single sounds, you also have the completion closure after all the sound sequence have been played.

Stop Sounds

To stop sounds and queues is as simple as play them. If you created the sound using the Sound object do it like this:

let sound = Sound(fileName: "guitar-chord.wav")
sound.play()
...
sound.stop()

You can stop a specific sound using the Soundable class functions:

Soundable.stop(sound)

You can stop all the sounds currently playing with Soundable, including sound queues:

Soundable.stopAll()

Or you can stop all the sounds in a specific group. I explain to you what is that thing of "Groups" in the next section.

Stop the sounds or sound queues does not trigger the completion closure.

Sound Groups

Sound groups is a feature that allows you to group sounds under the same string key, then you can stop all the sounds in that group and keep playing the rest.

By default all the sounds and sound queues are created under the SoundableKey.DefaultGroupKey key. In this way, you can group for example, game sounds under the "game_sounds" key and then stop only those sounds:

Soundable.stopAll(for: "game_sounds")

All the rest of the sounds keep playing until they reach the end of the track or queue.

You can set the group where a sound will belong to in the groupKey parameter of every play function. For example, when creating a Sound object:

let sound = Sound(fileName: "sprite-walk.wav")
sound.play(groupKey: "game_sounds") { error in
   // Handle error if any
}

Mute sounds

If you don't want to completelly stop the sound but only mute all sounds that are playing (Aka put the volume to 0.0), then use the Soundable mute functions:

// To mute
Soundable.muteAll()
Soundable.muteAll(for: "game_sounds")

// To unmute
Soundable.unmuteAll()
Soundable.unmuteAll(for: "game_sounds")

Alternativelly you can mute/unmute single sounds and queues:

sound.mute()
sound.unmute()

soundsQueue.mute()
soundsQueue.unmute()

Even check for the muting state with the sound and queue's isMuted property.

If the sound or queue finished while muted, the completion closure is called anyway and the mute state of the sound and queue is restored (Aka volume turns to be zero again).

Looped Sounds

Play sounds and sound queues in loop by setting the loopsCount parameter in every play call, as with the groupKey:

let sound = Sound(fileName: "sprite-walk.wav")
sound.play(groupKey: "game_sounds", loopsCount: 2) { error in
   // Handle error if any
}

The sound or sound queue will play a total of loopsCount + 1 times before it triggers the completion closure.

Disabling Sounds

You can enable/disable all the currently playing sounds and sound queues by setting the soundEnabled property of the Soundable class:

Soundable.soundEnabled = false

If disabled, it will stop all the playing sounds and sound queues and will return an error for subsequent attempts of playing sounds with the library. Disabling the sound system will not fire the completion closures.

Setup Audio Session Category

You can setup and activate the shared AVAudioSession category with a single call (no error handler). For example to continue playing the sounds when in the app is in background or the device is locked (background modes required):

import AVFoundation

// Setup the category
Soundable.activateSession(category: .playback)

Or also deactivate the current active session category:

Soundable.deactivateSession()

Handling Audio Interruptions

A playing sound or sound queue can be interrupted due to many reasons. For example when you are playing your sounds and then the user receives a phone call, the operating system stops all the sounds playing arround in order to listen the incoming audio from the call. In this case, Soundable allows you to catch this kind of events and let you react to an audio interruption:

Soundable.observeInterruptions { (type, userInfo) in
    if type == .began {
        print("interruption began")
    } else if type == .ended {
        print("interruption ended")
    }
}

In the closure you will receive the type of interruption, whether if it has .began or .ended, and the userInfo object containing the details about the interruption in order to make a more finest handling.

h2 id="github">GitHub

https://github.com/ThXou/Soundable