Skip to content

Commit

Permalink
Allow resuming assets during dead-air (roundware#35)
Browse files Browse the repository at this point in the history
* 🎉 Untested pass at proximity fading

* 🎨 Fix indent

* 🪲 Only mark asset as played after finishing

* ✨ Use deadAir upperBound and rename field

* 🪲 Fix extra time past asset end
  • Loading branch information
loafofpiecrust authored Aug 13, 2019
1 parent b94b773 commit c4e38a2
Show file tree
Hide file tree
Showing 2 changed files with 97 additions and 13 deletions.
83 changes: 79 additions & 4 deletions RWFramework/RWFramework/Playlist/AudioTrack.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ public class AudioTrack {
let tags: [Int]?
let bannedDuration: Double
let startWithSilence: Bool
/**
Whether to fade out the playing asset if it gets filtered out.
Also enables resuming that asset if it quickly passes the filters again.
*/
let fadeOutWhenFiltered: Bool

var playlist: Playlist? = nil
var previousAsset: Asset? = nil
Expand All @@ -43,7 +48,8 @@ public class AudioTrack {
repeatRecordings: Bool,
tags: [Int]?,
bannedDuration: Double,
startWithSilence: Bool
startWithSilence: Bool,
fadeOutWhenFiltered: Bool
) {
self.id = id
self.volume = volume
Expand All @@ -55,6 +61,7 @@ public class AudioTrack {
self.tags = tags
self.bannedDuration = bannedDuration
self.startWithSilence = startWithSilence
self.fadeOutWhenFiltered = fadeOutWhenFiltered
}
}

Expand All @@ -76,7 +83,8 @@ extension AudioTrack {
repeatRecordings: it["repeatrecordings"]?.bool ?? false,
tags: it["tag_filters"]?.array?.map { $0.int! },
bannedDuration: it["banned_duration"]?.double ?? 600,
startWithSilence: it["start_with_silence"]?.bool ?? true
startWithSilence: it["start_with_silence"]?.bool ?? true,
fadeOutWhenFiltered: it["fadeout_when_filtered"]?.bool ?? true
)
}
}
Expand All @@ -94,6 +102,7 @@ extension AudioTrack {
if self.state is WaitingForAsset {
self.fadeInNextAsset()
}
self.state?.onUpdate()
}

/// Plays the next optimal asset nearby.
Expand Down Expand Up @@ -230,6 +239,8 @@ protocol TrackState {
func finish()
func pause()
func resume()
/// Called when the track experiences a parameter update (eg. changed location)
func onUpdate()
}

/**
Expand All @@ -241,6 +252,7 @@ private class LoadingState: TrackState {
func finish () {}
func pause() {}
func resume() {}
func onUpdate() {}
}

private class TimedTrackState: TrackState {
Expand Down Expand Up @@ -291,6 +303,9 @@ private class TimedTrackState: TrackState {
lastResume = Date()
timer?.start()
}

func onUpdate() {
}
}

/// Silence between assets
Expand All @@ -312,6 +327,40 @@ private class DeadAir: TimedTrackState {
}
}

private class ResumableDeadAir: TimedTrackState {
private let track: AudioTrack
/// Last played track that can be resumed if eligible.
private let asset: Asset
private let remainingDurationOfAsset: Double

init(track: AudioTrack, asset: Asset, remainingAssetTime: Double) {
self.track = track
self.asset = asset
self.remainingDurationOfAsset = remainingAssetTime
super.init(duration: Double(track.deadAir.upperBound))
}

override func start() {
super.start()
print("in resumable silence for \(self.timeLeft)")
}

override func goToNextState() {
self.track.fadeInNextAsset()
}

override func onUpdate() {
if track.playlist?.passesFilters(asset, forTrack: track) == true {
print("resuming previous asset")
track.transition(to: FadingIn(
track: track,
asset: asset,
assetDuration: remainingDurationOfAsset
))
}
}
}

/// Fading into the playing asset
private class FadingIn: TimedTrackState {
private static let updateInterval = 0.02
Expand Down Expand Up @@ -411,12 +460,26 @@ private class PlayingAsset: TimedTrackState {
}

override func goToNextState() {
// Tell the playlist we've finished the asset
track.playlist?.recordFinishedPlaying(asset: asset)
// and fade into the next one
track.transition(to: FadingOut(
track: track,
asset: asset,
duration: fadeOutDuration
))
}

override func onUpdate() {
if track.fadeOutWhenFiltered && track.playlist?.passesFilters(asset, forTrack: track) == false {
track.transition(to: FadingOut(
track: track,
asset: asset,
duration: fadeOutDuration,
remainingAssetTime: timeLeft
))
}
}
}

/// Fading out of the playing asset
Expand All @@ -426,18 +489,21 @@ private class FadingOut: TimedTrackState {
private let track: AudioTrack
private let asset: Asset
private let followedByDeadAir: Bool
private let remainingAssetTime: Double?

override var canSkip: Bool { return false }

init(
track: AudioTrack,
asset: Asset,
duration: Double,
followedByDeadAir: Bool = true
followedByDeadAir: Bool = true,
remainingAssetTime: Double? = nil
) {
self.track = track
self.asset = asset
self.followedByDeadAir = followedByDeadAir
self.remainingAssetTime = remainingAssetTime
super.init(duration: duration)
}

Expand All @@ -448,6 +514,7 @@ private class FadingOut: TimedTrackState {
self.track.player.volume -= toAdd
} else {
self.track.player.volume = 0
self.track.player.pause()
self.goToNextState()
}
}
Expand All @@ -466,7 +533,15 @@ private class FadingOut: TimedTrackState {

override func goToNextState() {
if (followedByDeadAir) {
track.transition(to: DeadAir(track: track))
if let remainingTime = self.remainingAssetTime {
track.transition(to: ResumableDeadAir(
track: track,
asset: asset,
remainingAssetTime: remainingTime
))
} else {
track.transition(to: DeadAir(track: track))
}
} else {
self.track.fadeInNextAsset()
}
Expand Down
27 changes: 18 additions & 9 deletions RWFramework/RWFramework/Playlist/Playlist.swift
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,8 @@ extension Playlist {
(asset, self.filters.keep(asset, playlist: self, track: track))
}.filter { (asset, rank) in
rank != .discard
// don't pick anything currently playing on another track
&& !self.currentlyPlayingAssets.contains { $0.id == asset.id }
}

let sortedAssets = filteredAssets.sorted { a, b in
Expand All @@ -226,19 +228,26 @@ extension Playlist {

let next = sortedAssets.first
if let next = next {
var playCount = 1
if let prevEntry = userAssetData[next.id] {
playCount += prevEntry.playCount
}

userAssetData.updateValue(
UserAssetData(lastListen: Date(), playCount: playCount),
forKey: next.id
)
print("picking asset: \(next)")
}
return next
}

func recordFinishedPlaying(asset: Asset) {
var playCount = 1
if let prevEntry = userAssetData[asset.id] {
playCount += prevEntry.playCount
}

userAssetData.updateValue(
UserAssetData(lastListen: Date(), playCount: playCount),
forKey: asset.id
)
}

func passesFilters(_ asset: Asset, forTrack track: AudioTrack) -> Bool {
return self.filters.keep(asset, playlist: self, track: track) != .discard
}

private func updateTrackParams() {
if let tracks = self.tracks, let params = self.currentParams {
Expand Down

0 comments on commit c4e38a2

Please sign in to comment.