Compare commits

..

8 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 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
8 changed files with 98 additions and 34 deletions
+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))
})
})
})
})
}
}
+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)
}
}
+5 -4
View File
@@ -23,13 +23,13 @@ SwiftAudio is available through [CocoaPods](http://cocoapods.org). To install
it, simply add the following line to your Podfile:
```ruby
pod 'SwiftAudio', '~> 0.4.4'
pod 'SwiftAudio', '~> 0.5.0'
```
### Carthage
SwiftAudio supports [Carthage](https://github.com/Carthage/Carthage). Add this to your Cartfile:
```ruby
github "jorgenhenrichsen/SwiftAudio" ~> 0.4.4
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).
@@ -39,7 +39,7 @@ Then follow the rest of Carthage instructions on [adding a framework](https://gi
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.
```
@@ -49,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.
```
@@ -77,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.4'
s.version = '0.5.0'
s.summary = 'Easy audio streaming for iOS'
# This description is used to generate tags and improve search results.
@@ -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
}
}
+26 -10
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)