Skip to content

Commit

Permalink
Merge pull request #1495 from ryanfrancesconi/develop
Browse files Browse the repository at this point in the history
Minor AKPlayer update. Optional Auto-Fade on stop.
  • Loading branch information
aure committed Sep 12, 2018
2 parents 89a568b + 969426b commit 3bfe529
Show file tree
Hide file tree
Showing 5 changed files with 49 additions and 12 deletions.
Expand Up @@ -7,7 +7,6 @@
//

public class AKDynamicPlayer: AKPlayer {

/// The time pitch node - disabled by default
public private(set) var timePitchNode: AKTimePitch?

Expand All @@ -18,6 +17,10 @@ public class AKDynamicPlayer: AKPlayer {
}

set {
if newValue == rate {
return
}

// timePitch is only installed if it is requested. This saves resources.
if timePitchNode != nil && newValue == 1 {
removeTimePitch()
Expand All @@ -42,6 +45,9 @@ public class AKDynamicPlayer: AKPlayer {
}

set {
if newValue == pitch {
return
}
// timePitch is only installed if it is requested. This saves resources.
if timePitchNode != nil && newValue == 0 {
removeTimePitch()
Expand Down Expand Up @@ -76,25 +82,28 @@ public class AKDynamicPlayer: AKPlayer {
internal override func connectNodes() {
guard let processingFormat = processingFormat else { return }

AKLog(processingFormat)

if let timePitchNode = timePitchNode, let faderNode = faderNode {
AudioKit.connect(playerNode, to: timePitchNode.avAudioNode, format: processingFormat)
AudioKit.connect(timePitchNode.avAudioNode, to: faderNode.avAudioNode, format: processingFormat)
AudioKit.connect(faderNode.avAudioNode, to: mixer, format: processingFormat)
timePitchNode.bypass() // bypass timePitch by default to save CPU
AKLog(audioFile?.url.lastPathComponent ?? "URL is nil", processingFormat, "Connecting timePitch and fader")

} else if let timePitchNode = timePitchNode, faderNode == nil {
AudioKit.connect(playerNode, to: timePitchNode.avAudioNode, format: processingFormat)
AudioKit.connect(timePitchNode.avAudioNode, to: mixer, format: processingFormat)
timePitchNode.bypass()
AKLog(audioFile?.url.lastPathComponent ?? "URL is nil", processingFormat, "Connecting timePitch")

} else if let faderNode = faderNode {
// if the timePitchNode isn't created connect the player directly to the faderNode
AudioKit.connect(playerNode, to: faderNode.avAudioNode, format: processingFormat)
AudioKit.connect(faderNode.avAudioNode, to: mixer, format: processingFormat)
AKLog(audioFile?.url.lastPathComponent ?? "URL is nil", processingFormat, "Connecting fader")

} else {
AudioKit.connect(playerNode, to: mixer, format: processingFormat)
AKLog(audioFile?.url.lastPathComponent ?? "URL is nil", processingFormat, "Connecting player to mixer")
}
}

Expand Down
Expand Up @@ -7,7 +7,6 @@
//

extension AKPlayer {

// Fills the buffer with data read from audioFile
internal func updateBuffer(force: Bool = false) {
if !isBuffered { return }
Expand Down Expand Up @@ -129,5 +128,4 @@ extension AKPlayer {
AKLog("Faded Buffer")
}
}

}
Expand Up @@ -6,8 +6,8 @@
// Copyright © 2018 AudioKit. All rights reserved.
//

/// The Fader is also used for the gain stage of the player
extension AKPlayer {

internal func createFader() {
// AKLog("Creating fader AKBooster")
faderNode = AKBooster()
Expand Down Expand Up @@ -40,7 +40,6 @@ extension AKPlayer {
userInfo: nil,
repeats: false)
}

}

internal func resetFader(_ state: Bool) {
Expand Down Expand Up @@ -101,7 +100,7 @@ extension AKPlayer {
}
}

private func fadeOutWithTime(_ time: Double) {
@objc internal func fadeOutWithTime(_ time: Double) {
guard let faderNode = faderNode else { return }

if time > 0 {
Expand All @@ -112,5 +111,4 @@ extension AKPlayer {
// AKLog("Fading out to", Fade.minimumGain, ", shape:", fade.outRampType.rawValue)
}
}

}
Expand Up @@ -7,7 +7,6 @@
//

extension AKPlayer {

/// Play entire file right now
@objc public func play() {
play(from: startTime, to: endTime, at: nil, hostTime: nil)
Expand Down Expand Up @@ -44,6 +43,8 @@ extension AKPlayer {
hostTime: UInt64? = nil) {
let refTime = hostTime ?? mach_absolute_time()
let avTime = AVAudioTime.secondsToAudioTime(hostTime: refTime, time: scheduledTime)

// Note, final play command is in AKPlayer.swift for subclass override
play(from: startingTime, to: endingTime, at: avTime, hostTime: refTime)
}

Expand All @@ -62,8 +63,23 @@ extension AKPlayer {
play(from: pauseTime)
AKLog("Resuming at \(pauseTime)")
}

/// Stop playback and cancel any pending scheduled playback or completion events
@objc public func stop() {
guard stopEnvelopeTime > 0 else {
stopCompletion()
return
}

fadeOutWithTime(stopEnvelopeTime)
faderTimer = Timer.scheduledTimer(timeInterval: stopEnvelopeTime,
target: self,
selector: #selector(stopCompletion),
userInfo: nil,
repeats: false)
}

@objc private func stopCompletion() {
playerNode.stop()
faderNode?.stop()
completionTimer?.invalidate()
Expand Down
20 changes: 18 additions & 2 deletions AudioKit/Common/Nodes/Playback/Players/Player/AKPlayer.swift
Expand Up @@ -44,7 +44,6 @@ import AVFoundation
Please note that pre macOS 10.13 / iOS 11 the completionHandler isn't sample accurate. It's pretty close though.
*/
public class AKPlayer: AKNode {

/// How the player should handle audio. If buffering, it will load the audio data into
/// an internal buffer and play from RAM. If not, it will play the file from disk.
/// Dynamic buffering will only load the audio if it needs to for processing reasons
Expand All @@ -59,19 +58,21 @@ public class AKPlayer: AKNode {
if newValue != start { needsUpdate = true }
}
}

public var end: Double = 0 {
willSet {
if newValue != end { needsUpdate = true }
}
}

var needsUpdate: Bool = false
}

public struct Fade {
public init() {}

/// a constant
public static var minimumGain: Double = 0.000_2
public static var minimumGain: Double = 0.0002

/// the value that the booster should fade to, settable
public var maximumGain: Double = 1
Expand Down Expand Up @@ -120,6 +121,16 @@ public class AKPlayer: AKNode {
/// Holds characteristics about the fade options.
public var fade = Fade()

/// Optional Auto-Fade to audio on stop(). Useful for eliminating clicks with a short 0.1 second fade.
/// Default is 0, or off.
public var stopEnvelopeTime: Double = 0 {
didSet {
if faderNode == nil {
createFader()
}
}
}

// MARK: - Nodes

/// The underlying player node
Expand Down Expand Up @@ -310,6 +321,7 @@ public class AKPlayer: AKNode {
}

// MARK: - Public Options

/// true if the player is buffering audio rather than playing from disk
public var isBuffered: Bool {
return isNormalized || isReversed || buffering == .always
Expand Down Expand Up @@ -348,6 +360,7 @@ public class AKPlayer: AKNode {
}

// MARK: - Initialization

public override init() {
super.init(avAudioNode: mixer, attach: false)
}
Expand Down Expand Up @@ -463,6 +476,9 @@ public class AKPlayer: AKNode {
/// Placed in main class to be overriden in subclasses if needed.
public func play(from startingTime: Double, to endingTime: Double, at audioTime: AVAudioTime?, hostTime: UInt64?) {
// AKLog(startingTime, "to", endingTime, "at", audioTime, "hostTime", hostTime)

faderTimer?.invalidate()

preroll(from: startingTime, to: endingTime)
schedule(at: audioTime, hostTime: hostTime)
playerNode.play()
Expand Down

0 comments on commit 3bfe529

Please sign in to comment.