Compare commits

...

29 Commits

Author SHA1 Message Date
Jørgen Henrichsen 04296fa681 Update podspec. Update readme.
Bumpe versions to 0.5.0.
2018-11-18 23:42:40 +01:00
Jørgen Henrichsen 252ed947d2 Merge pull request #34 from jorgenhenrichsen/optional-time-pitch-algo
Optional time pitch algo
2018-11-18 23:35:11 +01:00
Jørgen Henrichsen b5fdb5c54e Merge branch 'master' into optional-time-pitch-algo 2018-11-18 22:59:00 +01:00
Jørgen Henrichsen 1b4c0b0d3b Update Readme. Update podspec.
Verision 0.4.4
2018-11-16 11:54:49 +01:00
Jørgen Henrichsen e0aa2a09a9 Merge pull request #35 from Alex601t/issue26
Fixes #26
2018-11-16 11:52:44 +01:00
Alexander ab0eb4f8eb Merge pull request #1 from jorgenhenrichsen/Alex601t-issue26
Observe loadedTimeRanges for AVPlayerItem
Use items duration.seconds if available.
2018-11-16 10:58:49 +03:00
Jørgen Henrichsen 99e7c65bbc Use items duration.seconds if available. 2018-11-15 20:35:07 +01:00
Jørgen Henrichsen 9072259631 Observe loadedTimeRanges for AVPlayerItem 2018-11-14 16:38:55 +01:00
Alexander eb9af1007a Issue 26:
Handling player current item duration from loaded time ranges
2018-11-14 16:34:58 +03:00
Jørgen Henrichsen 69d3a9c0c0 Test TimePitchAlgorithms in the AudioPlayer. 2018-11-14 10:06:14 +01:00
Jørgen Henrichsen 43821c68a9 Made DefaultAudioItem not conform to TimePitching.
New DefaultAudioItemTimePitching can be used instead.
2018-11-14 10:05:20 +01:00
Jørgen Henrichsen 610ff4c7f3 Made getter for wrapper internal.
Makes testing easier.
2018-11-14 10:04:12 +01:00
Jørgen Henrichsen 1fc533214f Update Readme.
- Remove timePitch algorithm from DefaultAudioItem inits
- Add audioTimePitchAlgorithm in the list of configurations
2018-11-09 13:50:53 +01:00
Jørgen Henrichsen 71f22c3e25 Time pitch algorithm is no longer required from AudioItems.
New protocol TimePitcher can be conformed to if AudioItems need to give their own Time Pitch Algorithm.
2018-11-09 13:45:48 +01:00
Jørgen Henrichsen 8a5e6d18cc Update Readme
Bump carthage install version
2018-11-08 23:13:02 +01:00
Jørgen Henrichsen a7f53bfec9 Update Readme. Update podspec.
Bump versions to 0.4.3
2018-11-08 23:11:49 +01:00
Jørgen Henrichsen 7b10f0476b Merge pull request #33 from jorgenhenrichsen/unsubscribe-observers
Unregister observer before removing AVPlayerItem.
2018-11-08 23:09:07 +01:00
Jørgen Henrichsen 618da75339 Unregister observer before removing AVPlayerItem.
Should fix a problem where the AVPlayerWrapper's item was deinitialized while observers where observing the item.
2018-11-08 17:59:24 +01:00
Jørgen Henrichsen 0694f5d8a0 Update Readme. Update podspec.
Bumping versions to 0.4.2
2018-11-06 22:41:13 +01:00
Jørgen Henrichsen 61268b45eb Merge pull request #32 from jorgenhenrichsen/queue-clearing
Queue functions
2018-11-06 22:40:07 +01:00
Jørgen Henrichsen 962e64fe24 Merge branch 'queue-clearing' of github.com:jorgenhenrichsen/SwiftAudio into queue-clearing 2018-11-06 22:31:05 +01:00
Jørgen Henrichsen 0f9b2656a1 Test remove upcoming/next items and clear queue on stop. 2018-11-06 22:30:15 +01:00
Jørgen Henrichsen 5e871fc7e2 Merge branch 'master' into queue-clearing 2018-11-06 21:30:27 +01:00
Jørgen Henrichsen fdf8fc9482 Added tests for removePreviousItems() 2018-11-06 21:27:07 +01:00
Jørgen Henrichsen a9eb713964 Remove upcoming items. Clear queue on reset 2018-11-04 15:21:24 +01:00
Jørgen Henrichsen a0efa5f408 Update README
Bump Carthage install release version
2018-11-02 19:26:03 +01:00
Jørgen Henrichsen 74cafd4c42 Merge pull request #31 from jorgenhenrichsen/feature/support-carthage
Feature/support carthage
2018-11-02 19:18:23 +01:00
Jørgen Henrichsen 53780ac03e Update Readme
Added info about installing via Carthage.
2018-11-02 18:24:31 +01:00
Jørgen Henrichsen ba438c8ede Set SwiftAudio scheme as shared. 2018-11-02 17:53:37 +01:00
15 changed files with 270 additions and 42 deletions
@@ -0,0 +1,71 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0930"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "1AB61EB02FDF0033DCB1F8416419F110"
BuildableName = "SwiftAudio.framework"
BlueprintName = "SwiftAudio"
ReferencedContainer = "container:Pods.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "1AB61EB02FDF0033DCB1F8416419F110"
BuildableName = "SwiftAudio.framework"
BlueprintName = "SwiftAudio"
ReferencedContainer = "container:Pods.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
+4 -4
View File
@@ -17,10 +17,10 @@ class AudioController {
let audioSessionController = AudioSessionController.shared
let sources: [AudioItem] = [
DefaultAudioItem(audioUrl: "https://p.scdn.co/mp3-preview/67b51d90ffddd6bb3f095059997021b589845f81?cid=d8a5ed958d274c2e8ee717e6a4b0971d", artist: "Bon Iver", title: "33 \"GOD\"", albumTitle: "22, A Million", sourceType: .stream, pitchAlgorithmType: .lowQualityZeroLatency, artwork: #imageLiteral(resourceName: "22AMI")),
DefaultAudioItem(audioUrl: "https://p.scdn.co/mp3-preview/081447adc23dad4f79ba4f1082615d1c56edf5e1?cid=d8a5ed958d274c2e8ee717e6a4b0971d", artist: "Bon Iver", title: "8 (circle)", albumTitle: "22, A Million", sourceType: .stream, pitchAlgorithmType: .lowQualityZeroLatency, artwork: #imageLiteral(resourceName: "22AMI")),
DefaultAudioItem(audioUrl: "https://p.scdn.co/mp3-preview/6f9999d909b017eabef97234dd7a206355720d9d?cid=d8a5ed958d274c2e8ee717e6a4b0971d", artist: "Bon Iver", title: "715 - CRΣΣKS", albumTitle: "22, A Million", sourceType: .stream, pitchAlgorithmType: .lowQualityZeroLatency, artwork: #imageLiteral(resourceName: "22AMI")),
DefaultAudioItem(audioUrl: "https://p.scdn.co/mp3-preview/bf9bdd403c67fdbe06a582e7b292487c8cfd1f7e?cid=d8a5ed958d274c2e8ee717e6a4b0971d", artist: "Bon Iver", title: "____45_____", albumTitle: "22, A Million", sourceType: .stream, pitchAlgorithmType: .lowQualityZeroLatency, artwork: #imageLiteral(resourceName: "22AMI"))
DefaultAudioItem(audioUrl: "https://p.scdn.co/mp3-preview/67b51d90ffddd6bb3f095059997021b589845f81?cid=d8a5ed958d274c2e8ee717e6a4b0971d", artist: "Bon Iver", title: "33 \"GOD\"", albumTitle: "22, A Million", sourceType: .stream, artwork: #imageLiteral(resourceName: "22AMI")),
DefaultAudioItem(audioUrl: "https://p.scdn.co/mp3-preview/081447adc23dad4f79ba4f1082615d1c56edf5e1?cid=d8a5ed958d274c2e8ee717e6a4b0971d", artist: "Bon Iver", title: "8 (circle)", albumTitle: "22, A Million", sourceType: .stream, artwork: #imageLiteral(resourceName: "22AMI")),
DefaultAudioItem(audioUrl: "https://p.scdn.co/mp3-preview/6f9999d909b017eabef97234dd7a206355720d9d?cid=d8a5ed958d274c2e8ee717e6a4b0971d", artist: "Bon Iver", title: "715 - CRΣΣKS", albumTitle: "22, A Million", sourceType: .stream, artwork: #imageLiteral(resourceName: "22AMI")),
DefaultAudioItem(audioUrl: "https://p.scdn.co/mp3-preview/bf9bdd403c67fdbe06a582e7b292487c8cfd1f7e?cid=d8a5ed958d274c2e8ee717e6a4b0971d", artist: "Bon Iver", title: "____45_____", albumTitle: "22, A Million", sourceType: .stream, artwork: #imageLiteral(resourceName: "22AMI"))
]
init() {
+29
View File
@@ -1,5 +1,6 @@
import Quick
import Nimble
import AVFoundation
@testable import SwiftAudio
@@ -151,6 +152,34 @@ class AudioPlayerTests: QuickSpec {
expect(audioPlayer.currentItem).toNot(beNil())
})
})
context("when setting the timePitchAlgorithm", {
beforeEach {
audioPlayer.audioTimePitchAlgorithm = .timeDomain
}
context("then loading an item", {
beforeEach {
try? audioPlayer.load(item: Source.getAudioItem(), playWhenReady: false)
}
it("should have the applied timePitchAlgorithm", closure: {
expect(audioPlayer.wrapper.currentItem?.audioTimePitchAlgorithm).to(equal(AVAudioTimePitchAlgorithm.timeDomain))
})
})
context("then loading a timepitching item", {
beforeEach {
let item = DefaultAudioItemTimePitching(audioUrl: Source.path, artist: nil, title: nil, albumTitle: nil, sourceType: .file, artwork: nil, audioTimePitchAlgorithm: AVAudioTimePitchAlgorithm.spectral)
try? audioPlayer.load(item: item, playWhenReady: false)
}
it("should have the applied timePitchAlgorithm", closure: {
expect(audioPlayer.wrapper.currentItem?.audioTimePitchAlgorithm).to(equal(AVAudioTimePitchAlgorithm.spectral))
})
})
})
})
}
}
+12
View File
@@ -170,6 +170,18 @@ class QueueManagerTests: QuickSpec {
expect(manager.current).to(equal(self.dummyItems.first))
})
})
context("then removing previous items", {
beforeEach {
manager.removePreviousItems()
}
it("should have no previous items", closure: {
expect(manager.previousItems.count).to(equal(0))
})
it("should have current index zero", closure: {
expect(manager.currentIndex).to(equal(0))
})
})
})
context("adding more items", {
@@ -98,6 +98,26 @@ class QueuedAudioPlayerTests: QuickSpec {
expect(audioPlayer.nextItems.count).to(equal(0))
})
})
context("then removing upcoming items", {
beforeEach {
audioPlayer.removeUpcomingItems()
}
it("should be empty", closure: {
expect(audioPlayer.nextItems.count).to(equal(0))
})
})
context("then stopping", {
beforeEach {
audioPlayer.stop()
}
it("should be empty", closure: {
expect(audioPlayer.nextItems.count).to(equal(0))
})
})
})
})
@@ -124,6 +144,26 @@ class QueuedAudioPlayerTests: QuickSpec {
})
})
context("then removing all previous items", {
beforeEach {
audioPlayer.removePreviousItems()
}
it("should be empty", closure: {
expect(audioPlayer.previousItems.count).to(equal(0))
})
})
context("then stopping", {
beforeEach {
audioPlayer.stop()
}
it("should be empty", closure: {
expect(audioPlayer.previousItems.count).to(equal(0))
})
})
})
})
}
+2 -2
View File
@@ -14,7 +14,7 @@ struct Source {
static let url: URL = URL(fileURLWithPath: Source.path)
static func getAudioItem() -> AudioItem {
return DefaultAudioItem(audioUrl: Source.path, sourceType: .file, pitchAlgorithmType: .lowQualityZeroLatency)
return DefaultAudioItem(audioUrl: Source.path, sourceType: .file)
}
}
@@ -23,6 +23,6 @@ struct ShortSource {
static let url: URL = URL(fileURLWithPath: Source.path)
static func getAudioItem() -> AudioItem {
return DefaultAudioItem(audioUrl: ShortSource.path, sourceType: .file, pitchAlgorithmType: .lowQualityZeroLatency)
return DefaultAudioItem(audioUrl: ShortSource.path, sourceType: .file)
}
}
+12 -3
View File
@@ -18,20 +18,28 @@ iOS 10.0+
## Installation
### CocoaPods
SwiftAudio is available through [CocoaPods](http://cocoapods.org). To install
it, simply add the following line to your Podfile:
```ruby
pod 'SwiftAudio', '~> 0.4.0'
pod 'SwiftAudio', '~> 0.5.0'
```
### Carthage
SwiftAudio supports [Carthage](https://github.com/Carthage/Carthage). Add this to your Cartfile:
```ruby
github "jorgenhenrichsen/SwiftAudio" ~> 0.5.0
```
Then follow the rest of Carthage instructions on [adding a framework](https://github.com/Carthage/Carthage#adding-frameworks-to-an-application).
## Usage
### AudioPlayer
To get started playing some audio:
```swift
let player = AudioPlayer()
let audioItem = DefaultAudioItem(audioUrl: "someUrl", sourceType: .stream, pitchAlgorithmType: .lowQualityZeroLatency)
let audioItem = DefaultAudioItem(audioUrl: "someUrl", sourceType: .stream)
player.load(item: audioItem, playWhenReady: true) // Load the item and start playing when the player is ready.
```
@@ -41,7 +49,7 @@ Implement `AudioPlayerDelegate` to get notified about useful events and updates
The `QueuedAudioPlayer` is asubclass of `AudioPlayer` that maintains a queue of audio tracks.
```swift
let player = QueuedAudioPlayer()
let audioItem = DefaultAudioItem(audioUrl: "someUrl", sourceType: .stream, pitchAlgorithmType: .lowQualityZeroLatency)
let audioItem = DefaultAudioItem(audioUrl: "someUrl", sourceType: .stream)
player.add(item: audioItem, playWhenReady: true) // Since this is the first item, we can supply playWhenReady: true to immedietaly start playing when the item is loaded.
```
@@ -69,6 +77,7 @@ Current options for configuring the `AudioPlayer`:
- `volume`
- `isMuted`
- `rate`
- `audioTimePitchAlgorithm`: This value decides the `AVAudioTimePitchAlgorithm` used for each `AudioItem`. Implement `TimePitching` in your `AudioItem`-subclass to override individually for each `AudioItem`.
### Audio Session
Remember to activate an audio session with an appropriate category for your app. This can be done with `AudioSessionController`:
+1 -1
View File
@@ -8,7 +8,7 @@
Pod::Spec.new do |s|
s.name = 'SwiftAudio'
s.version = '0.4.0'
s.version = '0.5.0'
s.summary = 'Easy audio streaming for iOS'
# This description is used to generate tags and improve search results.
@@ -88,7 +88,11 @@ class AVPlayerWrapper: AVPlayerWrapperProtocol {
if let seconds = currentItem?.duration.seconds, !seconds.isNaN {
return seconds
}
return 0
else if let seconds = currentItem?.loadedTimeRanges.first?.timeRangeValue.duration.seconds,
!seconds.isNaN {
return seconds
}
return 0.0
}
weak var delegate: AVPlayerWrapperDelegate? = nil
@@ -144,7 +148,7 @@ class AVPlayerWrapper: AVPlayerWrapperProtocol {
}
}
public func load(from url: URL, playWhenReady: Bool) {
func load(from url: URL, playWhenReady: Bool) {
reset(soft: true)
_playWhenReady = playWhenReady
_state = .loading
@@ -165,12 +169,13 @@ class AVPlayerWrapper: AVPlayerWrapperProtocol {
// MARK: - Util
private func reset(soft: Bool) {
playerItemObserver.stopObservingCurrentItem()
playerTimeObserver.unregisterForBoundaryTimeEvents()
playerItemNotificationObserver.stopObservingCurrentItem()
if !soft {
avPlayer.replaceCurrentItem(with: nil)
}
playerTimeObserver.unregisterForBoundaryTimeEvents()
playerItemNotificationObserver.stopObservingCurrentItem()
}
}
@@ -21,8 +21,8 @@ protocol AVPlayerWrapperProtocol {
var reasonForWaitingToPlay: AVPlayer.WaitingReason? { get }
var rate: Float { get set }
var rate: Float { get set }
var delegate: AVPlayerWrapperDelegate? { get set }
@@ -35,7 +35,7 @@ protocol AVPlayerWrapperProtocol {
var isMuted: Bool { get set }
var automaticallyWaitsToMinimizeStalling: Bool { get set }
func play()
+29 -11
View File
@@ -20,14 +20,19 @@ public protocol AudioItem {
func getTitle() -> String?
func getAlbumTitle() -> String?
func getSourceType() -> SourceType
func getPitchAlgorithmType() -> AVAudioTimePitchAlgorithm
func getArtwork(_ handler: @escaping (UIImage?) -> Void)
}
public struct DefaultAudioItem: AudioItem {
/// Make your `AudioItem`-subclass conform to this protocol to control which AVAudioTimePitchAlgorithm is used for each item.
public protocol TimePitching {
func getPitchAlgorithmType() -> AVAudioTimePitchAlgorithm
}
public class DefaultAudioItem: AudioItem {
public var audioUrl: String
public var artist: String?
@@ -38,17 +43,14 @@ public struct DefaultAudioItem: AudioItem {
public var sourceType: SourceType
public var pitchAlgorithmType: AVAudioTimePitchAlgorithm
public var artwork: UIImage?
public init(audioUrl: String, artist: String? = nil, title: String? = nil, albumTitle: String? = nil, sourceType: SourceType, pitchAlgorithmType: AVAudioTimePitchAlgorithm, artwork: UIImage? = nil) {
public init(audioUrl: String, artist: String? = nil, title: String? = nil, albumTitle: String? = nil, sourceType: SourceType, artwork: UIImage? = nil) {
self.audioUrl = audioUrl
self.artist = artist
self.title = title
self.albumTitle = albumTitle
self.sourceType = sourceType
self.pitchAlgorithmType = pitchAlgorithmType
self.artwork = artwork
}
@@ -71,13 +73,29 @@ public struct DefaultAudioItem: AudioItem {
public func getSourceType() -> SourceType {
return sourceType
}
public func getPitchAlgorithmType() -> AVAudioTimePitchAlgorithm {
return pitchAlgorithmType
}
public func getArtwork(_ handler: @escaping (UIImage?) -> Void) {
handler(artwork)
}
}
/// An AudioItem that also conforms to the `TimePitching`-protocol
public class DefaultAudioItemTimePitching: DefaultAudioItem, TimePitching {
public var pitchAlgorithmType: AVAudioTimePitchAlgorithm
public override init(audioUrl: String, artist: String?, title: String?, albumTitle: String?, sourceType: SourceType, artwork: UIImage?) {
self.pitchAlgorithmType = AVAudioTimePitchAlgorithm.lowQualityZeroLatency
super.init(audioUrl: audioUrl, artist: artist, title: title, albumTitle: albumTitle, sourceType: sourceType, artwork: artwork)
}
public init(audioUrl: String, artist: String?, title: String?, albumTitle: String?, sourceType: SourceType, artwork: UIImage?, audioTimePitchAlgorithm: AVAudioTimePitchAlgorithm) {
self.pitchAlgorithmType = audioTimePitchAlgorithm
super.init(audioUrl: audioUrl, artist: artist, title: title, albumTitle: albumTitle, sourceType: sourceType, artwork: artwork)
}
public func getPitchAlgorithmType() -> AVAudioTimePitchAlgorithm {
return pitchAlgorithmType
}
}
+27 -11
View File
@@ -28,7 +28,12 @@ public protocol AudioPlayerDelegate: class {
public class AudioPlayer: AVPlayerWrapperDelegate {
private var wrapper: AVPlayerWrapperProtocol
private var _wrapper: AVPlayerWrapperProtocol
/// The wrapper around the underlying AVPlayer
var wrapper: AVPlayerWrapperProtocol {
return _wrapper
}
public let nowPlayingInfoController: NowPlayingInfoController
public let remoteCommandController: RemoteCommandController
@@ -44,6 +49,12 @@ public class AudioPlayer: AVPlayerWrapperDelegate {
*/
public var automaticallyUpdateNowPlayingInfo: Bool = true
/**
Controls the time pitch algorithm applied to each item loaded into the player.
If the loaded `AudioItem` conforms to `TimePitcher`-protocol this will be overriden.
*/
public var audioTimePitchAlgorithm: AVAudioTimePitchAlgorithm = AVAudioTimePitchAlgorithm.lowQualityZeroLatency
/**
Default remote commands to use for each playing item
*/
@@ -84,7 +95,7 @@ public class AudioPlayer: AVPlayerWrapperDelegate {
*/
public var bufferDuration: TimeInterval {
get { return wrapper.bufferDuration }
set { wrapper.bufferDuration = newValue }
set { _wrapper.bufferDuration = newValue }
}
/**
@@ -92,7 +103,7 @@ public class AudioPlayer: AVPlayerWrapperDelegate {
*/
public var timeEventFrequency: TimeEventFrequency {
get { return wrapper.timeEventFrequency }
set { wrapper.timeEventFrequency = newValue }
set { _wrapper.timeEventFrequency = newValue }
}
/**
@@ -100,22 +111,22 @@ public class AudioPlayer: AVPlayerWrapperDelegate {
*/
public var automaticallyWaitsToMinimizeStalling: Bool {
get { return wrapper.automaticallyWaitsToMinimizeStalling }
set { wrapper.automaticallyWaitsToMinimizeStalling = newValue }
set { _wrapper.automaticallyWaitsToMinimizeStalling = newValue }
}
public var volume: Float {
get { return wrapper.volume }
set { wrapper.volume = newValue }
set { _wrapper.volume = newValue }
}
public var isMuted: Bool {
get { return wrapper.isMuted }
set { wrapper.isMuted = newValue }
set { _wrapper.isMuted = newValue }
}
public var rate: Float {
get { return wrapper.rate }
set { wrapper.rate = newValue }
set { _wrapper.rate = newValue }
}
// MARK: - Init
@@ -128,11 +139,11 @@ public class AudioPlayer: AVPlayerWrapperDelegate {
public init(avPlayer: AVPlayer = AVPlayer(),
nowPlayingInfoController: NowPlayingInfoController = NowPlayingInfoController(),
remoteCommandController: RemoteCommandController = RemoteCommandController()) {
self.wrapper = AVPlayerWrapper(avPlayer: avPlayer)
self._wrapper = AVPlayerWrapper(avPlayer: avPlayer)
self.nowPlayingInfoController = nowPlayingInfoController
self.remoteCommandController = remoteCommandController
self.wrapper.delegate = self
self._wrapper.delegate = self
self.remoteCommandController.audioPlayer = self
}
@@ -158,7 +169,12 @@ public class AudioPlayer: AVPlayerWrapperDelegate {
wrapper.load(from: URL(fileURLWithPath: item.getSourceUrl()), playWhenReady: playWhenReady)
}
wrapper.currentItem?.audioTimePitchAlgorithm = item.getPitchAlgorithmType()
if let item = item as? TimePitching {
wrapper.currentItem?.audioTimePitchAlgorithm = item.getPitchAlgorithmType()
}
else {
wrapper.currentItem?.audioTimePitchAlgorithm = audioTimePitchAlgorithm
}
self._currentItem = item
self.updateMetaValues(item: item)
@@ -259,7 +275,7 @@ public class AudioPlayer: AVPlayerWrapperDelegate {
// MARK: - Private
private func reset() {
func reset() {
self._currentItem = nil
}
@@ -27,6 +27,7 @@ class AVPlayerItemObserver: NSObject {
private struct AVPlayerItemKeyPath {
static let duration = #keyPath(AVPlayerItem.duration)
static let loadedTimeRanges = #keyPath(AVPlayerItem.loadedTimeRanges)
}
var isObserving: Bool = false
@@ -53,11 +54,13 @@ class AVPlayerItemObserver: NSObject {
self.isObserving = true
self.observingItem = item
item.addObserver(self, forKeyPath: AVPlayerItemKeyPath.duration, options: [.new], context: &AVPlayerItemObserver.context)
item.addObserver(self, forKeyPath: AVPlayerItemKeyPath.loadedTimeRanges, options: [.new], context: &AVPlayerItemObserver.context)
}
}
private func stopObservingCurrentItem() {
func stopObservingCurrentItem() {
observingItem?.removeObserver(self, forKeyPath: AVPlayerItemKeyPath.duration, context: &AVPlayerItemObserver.context)
observingItem?.removeObserver(self, forKeyPath: AVPlayerItemKeyPath.loadedTimeRanges, context: &AVPlayerItemObserver.context)
self.isObserving = false
self.observingItem = nil
}
@@ -73,8 +76,12 @@ class AVPlayerItemObserver: NSObject {
if let duration = change?[.newKey] as? CMTime {
self.delegate?.item(didUpdateDuration: duration.seconds)
}
default:
break
case AVPlayerItemKeyPath.loadedTimeRanges:
if let ranges = change?[.newKey] as? [NSValue], let duration = ranges.first?.timeRangeValue.duration {
self.delegate?.item(didUpdateDuration: duration.seconds)
}
default: break
}
}
+11
View File
@@ -203,9 +203,20 @@ class QueueManager<T> {
self._items[_currentIndex] = item
}
/**
Remove all previous items in the queue.
If no previous items exist, no action will be taken.
*/
public func removePreviousItems() {
guard currentIndex > 0 else { return }
_items.removeSubrange(0..<_currentIndex)
_currentIndex = 0
}
/**
Remove upcoming items.
If no upcoming items exist, no action will be taken.
*/
public func removeUpcomingItems() {
let nextIndex = _currentIndex + 1
@@ -30,6 +30,9 @@ public class QueuedAudioPlayer: AudioPlayer {
*/
public override func stop() {
super.stop()
}
override func reset() {
queueManager.clearQueue()
}
@@ -158,6 +161,13 @@ public class QueuedAudioPlayer: AudioPlayer {
queueManager.removeUpcomingItems()
}
/**
Remove all previous items, those returned by `previous()`
*/
public func removePreviousItems() {
queueManager.removePreviousItems()
}
// MARK: - AVPlayerWrapperDelegate
override func AVWrapper(itemPlaybackDoneWithReason reason: PlaybackEndedReason) {