Skip to content

Instantly share code, notes, and snippets.

@josipbernat
Created November 3, 2017 12:07
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save josipbernat/0991ba1de73c02da839883408fe0f428 to your computer and use it in GitHub Desktop.
Save josipbernat/0991ba1de73c02da839883408fe0f428 to your computer and use it in GitHub Desktop.
AVAudioPlayer fade in and fade out operation
//
// FadeOperation.swift
// SomeProject
//
// Created by Josip Bernat on 03/11/2017.
// Copyright © 2017 Josip's Home. All rights reserved.
//
import Foundation
import AVFoundation
class FadeOperation: Operation {
let player: AVAudioPlayer
let fade: Fade
fileprivate let fadeStep = 0.01 //0.01 step gives us fine fade in / out.
enum Fade: Int {
case anIn = 0
case out
}
init(player: AVAudioPlayer, fade: Fade) {
self.player = player
self.fade = fade
super.init()
}
override var isAsynchronous: Bool { return true }
override var isExecuting: Bool { return state == .executing }
override var isFinished: Bool { return state == .finished }
var state = State.ready {
willSet {
willChangeValue(forKey: state.keyPath)
willChangeValue(forKey: newValue.keyPath)
}
didSet {
didChangeValue(forKey: state.keyPath)
didChangeValue(forKey: oldValue.keyPath)
}
}
enum State: String {
case ready = "Ready"
case executing = "Executing"
case finished = "Finished"
fileprivate var keyPath: String { return "is" + self.rawValue }
}
override func start() {
if self.isCancelled {
state = .finished
} else {
state = .ready
main()
}
}
override func main() {
if self.isCancelled {
state = .finished
}
else {
state = .executing
if fade == .anIn {
fadeIn()
}
else {
fadeOut()
}
}
}
func fadeIn() {
if isCancelled {
state = .finished
return
}
if player.volume == 1.0 {
player.volume = 0.0
}
player.prepareToPlay()
player.play()
commitFadeIn()
}
deinit {
print("FadeOperation deinit")
}
func commitFadeIn() {
if isCancelled {
state = .finished
return
}
if player.volume < 1.0 {
// Fade
player.volume += Float(fadeStep)
if player.volume > 1.0 {
player.volume = 1.0
}
DispatchQueue.global().asyncAfter(deadline: .now() + fadeStep, execute: { [weak self] in
if self?.isCancelled == false {
self?.commitFadeIn()
}
else {
self?.state = .finished
}
})
}
else {
state = .finished
}
}
func fadeOut() {
if isCancelled {
state = .finished
return
}
commitFadeOut()
}
@objc fileprivate func commitFadeOut() {
if isCancelled {
state = .finished
return
}
if player.volume > 0.0 {
// Fade
player.volume -= Float(fadeStep)
if player.volume < 0.0 {
player.volume = 0.0
}
DispatchQueue.global().asyncAfter(deadline: .now() + fadeStep, execute: { [weak self] in
if self?.isCancelled == false {
self?.commitFadeOut()
}
else {
self?.state = .finished
}
})
}
else {
// Stop and get the sound ready for playing again
player.pause()
state = .finished
}
}
}
// This example is using one OperationQueue per player so
// cancel all operations if user clicks play / pause fast.
func play() {
queue.cancelAllOperations()
queue.addOperation(FadeOperation.init(player: player, fade: .anIn))
}
func pause() {
queue.cancelAllOperations()
queue.addOperation(FadeOperation.init(player: player, fade: .out))
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment