Compare commits

...

20 Commits

Author SHA1 Message Date
Tanha 6276e97c4c Release 1.3.0 2019-11-25 20:58:29 -08:00
Tanha 09142ce2d4 update to Swift 4.2 2019-11-25 20:57:54 -08:00
Tanha 90bc2262ec Release 1.2.0 2019-11-25 15:12:55 -08:00
Tanha 9594449215 nit 2019-11-25 15:05:49 -08:00
Tanha 6187c9f438 fix playing on seek 2019-11-25 14:30:30 -08:00
Tanha b28e815545 rename clean up 2019-11-25 13:49:06 -08:00
Tanha 17be73bbe8 fix fatal bug seeking 2019-11-25 13:44:54 -08:00
tanhakabir cd35f38db1 Merge pull request #13 from tanhakabir/fix_ui_seeking_bug
Fix ui when seeking to show "Loading" when not enough buffers ready
2019-11-25 10:44:28 -08:00
cendolinside123 3c752d581d Fix miniplayer on background (#12)
* 1. fix AVAudioSession configuration

* 1. fix seek bar didn't update on miniplayer on lockscreen when playing a song
2. setup miniplayer lockscreen seek bar speed (when slide  rate slider)

* setup project example to playing on the background
2019-11-25 10:44:00 -08:00
Tanha 1f20a48a20 fix example app bug 2019-11-25 10:37:11 -08:00
Tanha 3a585c1f43 fix playing status bug in disk engine 2019-11-25 10:30:06 -08:00
Tanha 5ac5b93ac4 separate enum to another file 2019-11-24 23:27:51 -08:00
Tanha b484f0bfb6 fix playing status when seeking 2019-11-24 23:24:25 -08:00
Tanha 0aeb8b0f88 change boolean playing status to enum 2019-11-21 01:46:42 -08:00
Tanha 8e7357860c shouldnot be forcing play 2019-11-21 01:24:45 -08:00
Tanha 936de8c996 minor fix 2019-11-20 23:10:03 -08:00
Tanha e986be9db5 clean up example app 2019-11-20 22:27:31 -08:00
Tanha 876d517f3d Release 1.1.1 2019-11-20 22:15:51 -08:00
Tanha 0a12c68274 Fix fatal error when seeking on streamed audio 2019-11-20 22:15:32 -08:00
cendolinside123 873e537301 fix seek bar on example app's player (#9) 2019-11-20 22:09:11 -08:00
19 changed files with 161 additions and 61 deletions
+9 -2
View File
@@ -14,6 +14,7 @@
79D8DF73FA7CDD6E266BAE71D46E035F /* Pods-SwiftAudioPlayer_Tests-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 50C71346CE708A211A5AFAC20BAE48CB /* Pods-SwiftAudioPlayer_Tests-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; };
831B263D357A5FA2DDC7B1AE4B374092 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5A16F4CFC63FAC439D7A04994F579A03 /* Foundation.framework */; };
8F93DB166237195ED222EE55B6404625 /* Pods-SwiftAudioPlayer_Example-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 3B0B76CB1439F4D361322144E5A65C3A /* Pods-SwiftAudioPlayer_Example-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; };
A41AA0D2238BB9B600A467E1 /* SAPlayingStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = A41AA0D1238BB9B600A467E1 /* SAPlayingStatus.swift */; };
A4681FC6220113880018AB51 /* SAPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = A4681F8D2200E00E0018AB51 /* SAPlayer.swift */; };
A4681FC72201138B0018AB51 /* SAPlayerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = A4681F912200E1950018AB51 /* SAPlayerDelegate.swift */; };
A4681FC82201138E0018AB51 /* SAPlayerPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = A4681F8F2200E1450018AB51 /* SAPlayerPresenter.swift */; };
@@ -95,6 +96,7 @@
99925F09FC9C6EA4B9C0508F4E2D1FE2 /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
A19C8F889C787C19BE4123C1896AF501 /* Pods-SwiftAudioPlayer_Example-resources.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-SwiftAudioPlayer_Example-resources.sh"; sourceTree = "<group>"; };
A39F2A138CF40C1051CA9E227429A86D /* SwiftAudioPlayer.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = SwiftAudioPlayer.modulemap; sourceTree = "<group>"; };
A41AA0D1238BB9B600A467E1 /* SAPlayingStatus.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SAPlayingStatus.swift; sourceTree = "<group>"; };
A4523BC8220A0B3C0079C4BC /* Credited_LICENSE */ = {isa = PBXFileReference; lastKnownFileType = text; path = Credited_LICENSE; sourceTree = "<group>"; };
A4681F802200D0500018AB51 /* Log.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Log.swift; sourceTree = "<group>"; };
A4681F822200D9150018AB51 /* AudioEngine.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioEngine.swift; sourceTree = "<group>"; };
@@ -278,6 +280,7 @@
A4681F932200E2020018AB51 /* Engine */ = {
isa = PBXGroup;
children = (
A41AA0D1238BB9B600A467E1 /* SAPlayingStatus.swift */,
A4FBA6B8221BAF8700D5A353 /* SAAudioAvailabilityRange.swift */,
A4681F822200D9150018AB51 /* AudioEngine.swift */,
A4681F942200E2220018AB51 /* AudioDiskEngine.swift */,
@@ -484,6 +487,9 @@
042ACE071BA515F4DE0E0C8007C3F0EE = {
LastSwiftMigration = 1010;
};
E50DAD13FFD3FC8036073A58BF8423D4 = {
LastSwiftMigration = 1010;
};
};
};
buildConfigurationList = 2D8E8EC45A3A1A1D94AE762CB5028504 /* Build configuration list for PBXProject "Pods" */;
@@ -510,6 +516,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
A41AA0D2238BB9B600A467E1 /* SAPlayingStatus.swift in Sources */,
A4681FDC220113D70018AB51 /* AudioDownloadWorker.swift in Sources */,
A4681FD8220113C60018AB51 /* AudioDataManager.swift in Sources */,
A4681FD1220113AF0018AB51 /* AudioParsable.swift in Sources */,
@@ -669,7 +676,7 @@
SKIP_INSTALL = YES;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) ";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 4.0;
SWIFT_VERSION = 4.2;
TARGETED_DEVICE_FAMILY = "1,2";
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
@@ -701,7 +708,7 @@
SKIP_INSTALL = YES;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) ";
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
SWIFT_VERSION = 4.0;
SWIFT_VERSION = 4.2;
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
VERSIONING_SYSTEM = "apple-generic";
@@ -213,12 +213,12 @@
607FACCF1AFB9204008FA782 = {
CreatedOnToolsVersion = 6.3.1;
DevelopmentTeam = R2392A68YQ;
LastSwiftMigration = 0900;
LastSwiftMigration = 1010;
};
607FACE41AFB9204008FA782 = {
CreatedOnToolsVersion = 6.3.1;
DevelopmentTeam = R2392A68YQ;
LastSwiftMigration = 0900;
LastSwiftMigration = 1010;
TestTargetID = 607FACCF1AFB9204008FA782;
};
};
@@ -477,8 +477,7 @@
MODULE_NAME = ExampleApp;
PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.$(PRODUCT_NAME:rfc1034identifier)";
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_SWIFT3_OBJC_INFERENCE = Default;
SWIFT_VERSION = 4.0;
SWIFT_VERSION = 4.2;
};
name = Debug;
};
@@ -493,8 +492,7 @@
MODULE_NAME = ExampleApp;
PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.$(PRODUCT_NAME:rfc1034identifier)";
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_SWIFT3_OBJC_INFERENCE = Default;
SWIFT_VERSION = 4.0;
SWIFT_VERSION = 4.2;
};
name = Release;
};
@@ -515,8 +513,7 @@
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.$(PRODUCT_NAME:rfc1034identifier)";
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_SWIFT3_OBJC_INFERENCE = Default;
SWIFT_VERSION = 4.0;
SWIFT_VERSION = 4.2;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/SwiftAudioPlayer_Example.app/SwiftAudioPlayer_Example";
};
name = Debug;
@@ -534,8 +531,7 @@
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.$(PRODUCT_NAME:rfc1034identifier)";
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_SWIFT3_OBJC_INFERENCE = Default;
SWIFT_VERSION = 4.0;
SWIFT_VERSION = 4.2;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/SwiftAudioPlayer_Example.app/SwiftAudioPlayer_Example";
};
name = Release;
+1 -1
View File
@@ -15,7 +15,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
return true
}
@@ -32,7 +32,9 @@
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<color key="maximumTrackTintColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<connections>
<action selector="scrubberSeeked:" destination="vXZ-lx-hvc" eventType="valueChanged" id="jDA-wR-wxk"/>
<action selector="scrubberSeeked:" destination="vXZ-lx-hvc" eventType="touchUpInside" id="hTi-fq-lrl"/>
<action selector="scrubberSeeked:" destination="vXZ-lx-hvc" eventType="touchUpOutside" id="mFP-SW-38w"/>
<action selector="scrubberStartedSeeking:" destination="vXZ-lx-hvc" eventType="touchDown" id="UXg-Wf-fKv"/>
</connections>
</slider>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="jUc-tP-CC5">
@@ -57,7 +59,7 @@
</connections>
</button>
<slider opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" value="1" minValue="0.10000000000000001" maxValue="32" translatesAutoresizingMaskIntoConstraints="NO" id="vfk-OJ-S3T">
<rect key="frame" x="14" y="564" width="347" height="31"/>
<rect key="frame" x="14" y="464" width="347" height="31"/>
<connections>
<action selector="rateChanged:" destination="vXZ-lx-hvc" eventType="valueChanged" id="FDJ-jA-bm8"/>
</connections>
@@ -88,7 +90,7 @@
</connections>
</button>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="rate: 1.0" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="yUQ-mI-ozK">
<rect key="frame" x="157" y="535" width="61" height="21"/>
<rect key="frame" x="157" y="435" width="61" height="21"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
@@ -125,7 +127,7 @@
<constraint firstItem="Urj-Dv-41y" firstAttribute="centerY" secondItem="j3w-gr-HzF" secondAttribute="centerY" id="Fvd-7V-Rr8"/>
<constraint firstItem="1IX-z5-wWx" firstAttribute="leading" secondItem="joK-xi-MCo" secondAttribute="leading" id="GeX-7f-jzu"/>
<constraint firstItem="0QE-3F-a4G" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="jUc-tP-CC5" secondAttribute="trailing" constant="8" symbolic="YES" id="JP5-yW-eVB"/>
<constraint firstItem="yUQ-mI-ozK" firstAttribute="top" secondItem="w2a-RA-zmI" secondAttribute="bottom" constant="200" id="K1K-8N-SpD"/>
<constraint firstItem="yUQ-mI-ozK" firstAttribute="top" secondItem="w2a-RA-zmI" secondAttribute="bottom" constant="100" id="K1K-8N-SpD"/>
<constraint firstItem="vfk-OJ-S3T" firstAttribute="leading" secondItem="lTK-Hd-Tl2" secondAttribute="leading" id="NOY-IO-NIJ"/>
<constraint firstItem="tFH-sY-Xu9" firstAttribute="centerY" secondItem="jUc-tP-CC5" secondAttribute="centerY" id="Rre-EY-kVY"/>
<constraint firstItem="KDu-ea-kF8" firstAttribute="leading" secondItem="kh9-bI-dsS" secondAttribute="leading" constant="78" id="SRU-sX-z5b"/>
@@ -139,6 +141,7 @@
<constraint firstItem="KDu-ea-kF8" firstAttribute="top" secondItem="joK-xi-MCo" secondAttribute="bottom" constant="32" id="dLw-rF-Pfb"/>
<constraint firstItem="w2a-RA-zmI" firstAttribute="leading" secondItem="lTK-Hd-Tl2" secondAttribute="leading" id="daz-b0-eCC"/>
<constraint firstItem="jUc-tP-CC5" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="tFH-sY-Xu9" secondAttribute="trailing" constant="8" symbolic="YES" id="fS9-Ce-4ph"/>
<constraint firstItem="Urj-Dv-41y" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="j3w-gr-HzF" secondAttribute="trailing" constant="8" symbolic="YES" id="fu0-ZZ-rj9"/>
<constraint firstAttribute="trailing" secondItem="lTK-Hd-Tl2" secondAttribute="trailing" constant="16" id="gdg-7Y-7la"/>
<constraint firstAttribute="trailing" secondItem="1IX-z5-wWx" secondAttribute="trailing" constant="16" id="hHM-jO-RZd"/>
<constraint firstItem="6d9-Bc-hIz" firstAttribute="top" secondItem="joK-xi-MCo" secondAttribute="bottom" constant="32" id="m9s-An-IWV"/>
+6
View File
@@ -22,6 +22,12 @@
<string>1</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UIBackgroundModes</key>
<array>
<string>audio</string>
<string>fetch</string>
<string>processing</string>
</array>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
+19 -3
View File
@@ -79,6 +79,7 @@ class ViewController: UIViewController {
var isDownloading: Bool = false
var isStreaming: Bool = false
var beingSeeked: Bool = false
var duration: Double = 0.0
@@ -113,6 +114,7 @@ class ViewController: UIViewController {
_ = SAPlayer.Updates.ElapsedTime.subscribe { [weak self] (url, position) in
guard let self = self else { return }
guard self.beingSeeked == false else { return }
guard url == self.selectedAudio.url || url == self.savedUrls[self.selectedAudio] else { return }
self.currentTimestampLabel.text = SAPlayer.prettifyTimestamp(position)
@@ -156,10 +158,19 @@ class ViewController: UIViewController {
guard let self = self else { return }
guard url == self.selectedAudio.url || url == self.savedUrls[self.selectedAudio] else { return }
if playing {
switch playing {
case .playing:
self.isPlayable = true
self.playPauseButton.setTitle("Pause", for: .normal)
} else {
return
case .paused:
self.isPlayable = true
self.playPauseButton.setTitle("Play", for: .normal)
return
case .buffering:
self.isPlayable = false
self.playPauseButton.setTitle("Loading", for: .normal)
return
}
}
}
@@ -178,9 +189,14 @@ class ViewController: UIViewController {
// if let savedUrl = savedUrls[selectedAudio] {}
}
@IBAction func scrubberStartedSeeking(_ sender: UISlider) {
beingSeeked = true
}
@IBAction func scrubberSeeked(_ sender: Any) {
SAPlayer.shared.seekTo(seconds: Double(scrubberSlider.value))
let value = Double(scrubberSlider.value) * duration
SAPlayer.shared.seekTo(seconds: value)
beingSeeked = false
}
+2 -2
View File
@@ -178,9 +178,9 @@ Payload = `Double`
Changes in the duration of the current initialized audio. Especially helpful for audio that is being streamed and can change with more data.
### PlayingStatus
Payload = `Bool`
Payload = `SAPlayingStatus`
Changes in the playing/paused status of the player.
Changes in the playing status of the player. Can be one of the following 3: `playing`, `paused`, `buffering`.
### StreamingBuffer
Payload = `SAAudioAvailabilityRange`
+4 -8
View File
@@ -31,7 +31,7 @@ class AudioClockDirector {
private var needleClosures: DirectorThreadSafeClosures<Needle> = DirectorThreadSafeClosures()
private var durationClosures: DirectorThreadSafeClosures<Duration> = DirectorThreadSafeClosures()
private var playingStatusClosures: DirectorThreadSafeClosures<IsPlaying> = DirectorThreadSafeClosures()
private var playingStatusClosures: DirectorThreadSafeClosures<SAPlayingStatus> = DirectorThreadSafeClosures()
private var bufferClosures: DirectorThreadSafeClosures<SAAudioAvailabilityRange> = DirectorThreadSafeClosures()
private init() {}
@@ -60,7 +60,7 @@ class AudioClockDirector {
// Playing status
func attachToChangesInPlayingStatus(closure: @escaping (Key, IsPlaying) throws -> Void) -> UInt{
func attachToChangesInPlayingStatus(closure: @escaping (Key, SAPlayingStatus) throws -> Void) -> UInt{
return playingStatusClosures.attach(closure: closure)
}
@@ -103,12 +103,8 @@ extension AudioClockDirector {
}
extension AudioClockDirector {
func audioPaused(_ key: Key) {
playingStatusClosures.broadcast(key: key, payload: false)
}
func audioPlaying(_ key: Key) {
playingStatusClosures.broadcast(key: key, payload: true)
func audioPlayingStatusWasChanged(_ key: Key, status: SAPlayingStatus) {
playingStatusClosures.broadcast(key: key, payload: status)
}
}
+10 -8
View File
@@ -79,17 +79,13 @@ class AudioEngine: AudioEngineProtocol {
}
}
var isPlaying = false {
var playingStatus: SAPlayingStatus? = nil {
didSet {
guard isPlaying != oldValue else {
guard playingStatus != oldValue, let status = playingStatus else {
return
}
if isPlaying {
AudioClockDirector.shared.audioPlaying(key)
} else {
AudioClockDirector.shared.audioPaused(key)
}
AudioClockDirector.shared.audioPlayingStatusWasChanged(key, status: status)
}
}
@@ -149,7 +145,13 @@ class AudioEngine: AudioEngineProtocol {
}
func updateIsPlaying() {
isPlaying = engine.isRunning && playerNode.isPlaying
if !bufferedSeconds.isPlayable {
playingStatus = .buffering
return
}
let isPlaying = engine.isRunning && playerNode.isPlaying
playingStatus = isPlaying ? .playing : .paused
}
func play() {
+7 -3
View File
@@ -58,6 +58,7 @@ import AVFoundation
class AudioStreamEngine: AudioEngine {
//Constants
private let MAX_POLL_BUFFER_COUNT = 300 //Having one buffer in engine at a time is choppy.
private let MIN_BUFFERS_TO_BE_PLAYABLE = 1
private let PCM_BUFFER_SIZE: AVAudioFrameCount = 8192
private let queue = DispatchQueue(label: "SwiftAudioPlayer.engine", qos: .userInitiated)
@@ -86,7 +87,9 @@ class AudioStreamEngine: AudioEngine {
didSet {
if numberOfBuffersScheduledFromPoll > MAX_POLL_BUFFER_COUNT {
shouldPollForNextBuffer = false
}
if numberOfBuffersScheduledFromPoll > MIN_BUFFERS_TO_BE_PLAYABLE {
if wasPlaying {
play()
wasPlaying = false
@@ -213,7 +216,7 @@ class AudioStreamEngine: AudioEngine {
private func updateNetworkBufferRange() { //for ui
let range = converter.pollNetworkAudioAvailabilityRange()
isPlayable = (numberOfBuffersScheduledInTotal > 0 && range.1 > 0) && predictedStreamDuration > 0
isPlayable = (numberOfBuffersScheduledInTotal >= MIN_BUFFERS_TO_BE_PLAYABLE && range.1 > 0) && predictedStreamDuration > 0
Log.debug("loaded \(range), numberOfBuffersScheduledInTotal: \(numberOfBuffersScheduledInTotal), isPlayable: \(isPlayable)")
bufferedSeconds = SAAudioAvailabilityRange(startingNeedle: range.0, durationLoadedByNetwork: range.1, isPlayable: isPlayable)
}
@@ -262,7 +265,6 @@ class AudioStreamEngine: AudioEngine {
self.needle = needle //to tick while paused
queue.sync { [weak self] in
self?.seekHelperDispatchQueue(needle: needle)
}
@@ -290,6 +292,8 @@ class AudioStreamEngine: AudioEngine {
playerNode.stop()
shouldPollForNextBuffer = true
updateNetworkBufferRange()
}
override func invalidate() {
+8 -10
View File
@@ -36,6 +36,7 @@ protocol AudioThrottleable {
func tellAudioFormatFound()
func tellByteOffset(offset: UInt64)
func tellSeek(offset: UInt64)
func tellBytesPerAudioPacket(count: UInt64)
func pollRangeOfBytesAvailable() -> (UInt64, UInt64)
func invalidate()
}
@@ -98,15 +99,7 @@ class AudioThrottler: AudioThrottleable {
var byteOffsetBecauseOfSeek: UInt = 0
var totalBytesExpected: Int64? //this got sent up twice. Once at beginning of stream and second from network seek. We honor the first send
var largestPollingOffsetDifference: UInt64 = 0
var lastOffsetPolled: UInt64 = 0 {
didSet {
let diff = lastOffsetPolled - oldValue
if diff > largestPollingOffsetDifference {
largestPollingOffsetDifference = diff
}
}
}
var largestPollingOffsetDifference: UInt64 = 1
required init(withRemoteUrl url: AudioURL, withDelegate delegate: AudioThrottleDelegate) {
self.url = url
@@ -145,9 +138,14 @@ class AudioThrottler: AudioThrottleable {
shouldThrottle = true //the above layer has enough info that we can throttle
}
func tellBytesPerAudioPacket(count: UInt64) {
if count > largestPollingOffsetDifference {
largestPollingOffsetDifference = count
}
}
func tellByteOffset(offset: UInt64) {
Log.debug("offset \(offset)")
lastOffsetPolled = offset
for wrappedNetworkData in networkData {
if wrappedNetworkData.containsOffset(UInt(offset)) {
@@ -174,6 +174,10 @@ class AudioConverter: AudioConvertable {
}
private func getPacketIndex(forNeedle needle: Needle) -> AVAudioPacketCount? {
guard needle >= 0 else {
Log.error("needle should never be a negative number! needle received: \(needle)")
return nil
}
guard let frame = frameOffset(forTime: TimeInterval(needle)) else { return nil }
guard let framesPerPacket = parser.fileAudioFormat?.streamDescription.pointee.mFramesPerPacket else { return nil }
return AVAudioPacketCount(frame) / AVAudioPacketCount(framesPerPacket)
+8 -1
View File
@@ -98,7 +98,14 @@ class AudioParser: AudioParsable {
return predictedCount
}
var sumOfParsedAudioBytes:UInt32 = 0
var sumOfParsedAudioBytes:UInt32 = 0 {
didSet {
if let byteCount = averageBytesPerPacket {
throttler.tellBytesPerAudioPacket(count: UInt64(byteCount))
}
}
}
var numberOfPacketsParsed:UInt32 = 0
var audioPackets: [(AudioStreamPacketDescription?,Data)] = [] {
didSet {
+33
View File
@@ -0,0 +1,33 @@
//
// SAPlayingStatus.swift
// SwiftAudioPlayer
//
// Created by Tanha Kabir on 2019-11-24.
// Copyright © 2019 Tanha Kabir, Jon Mercer
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
import Foundation
public enum SAPlayingStatus {
case playing
case paused
case buffering
}
+15
View File
@@ -147,4 +147,19 @@ extension LockScreenViewProtocol {
func updateLockscreenPlaybackDuration(duration: Duration) {
MPNowPlayingInfoCenter.default().nowPlayingInfo?[MPMediaItemPropertyPlaybackDuration] = NSNumber(value: duration)
}
func updateLockscreenPaused(){
MPNowPlayingInfoCenter.default().nowPlayingInfo?[MPNowPlayingInfoPropertyPlaybackRate] = 0.0
}
func updateLockscreenPlaying(){
MPNowPlayingInfoCenter.default().nowPlayingInfo?[MPNowPlayingInfoPropertyPlaybackRate] = 1.0
}
func updateLockscreenChangePlaybackRate(speed: Double){
if speed > 0.0{
MPNowPlayingInfoCenter.default().nowPlayingInfo?[MPNowPlayingInfoPropertyPlaybackRate] = speed
}
}
}
+11 -2
View File
@@ -159,7 +159,9 @@ extension SAPlayer: SAPlayerDelegate {
} else {
// Fallback on earlier versions
}
try AVAudioSession.sharedInstance().setActive(true, with: .notifyOthersOnDeactivation)
try AVAudioSession.sharedInstance().setCategory(AVAudioSession.Category.playback, mode: AVAudioSession.Mode(rawValue: convertFromAVAudioSessionMode(AVAudioSession.Mode.default)), options: .allowAirPlay)
try AVAudioSession.sharedInstance().setActive(true, options: .notifyOthersOnDeactivation)
} catch {
Log.monitor("Problem setting up AVAudioSession to play in:: \(error.localizedDescription)")
}
@@ -170,7 +172,9 @@ extension SAPlayer: SAPlayerDelegate {
}
func seekEngine(toNeedle needle: Needle) {
player?.seek(toNeedle: needle)
var seekToNeedle = needle < 0 ? 0 : needle
seekToNeedle = needle > Needle(duration) ? Needle(duration) : needle
player?.seek(toNeedle: seekToNeedle)
}
func setSpeedEngine(withMultiple multiple: Double) {
@@ -178,3 +182,8 @@ extension SAPlayer: SAPlayerDelegate {
}
}
// Helper function inserted by Swift 4.2 migrator.
fileprivate func convertFromAVAudioSessionMode(_ input: AVAudioSession.Mode) -> String {
return input.rawValue
}
+8 -4
View File
@@ -35,7 +35,7 @@ class SAPlayerPresenter {
var duration: Duration?
private var key: String?
private var isPlaying = false
private var isPlaying: SAPlayingStatus = .buffering
private var mediaInfo: SALockScreenInfo?
private var urlKeyMap: [Key: URL] = [:]
@@ -126,16 +126,18 @@ class SAPlayerPresenter {
extension SAPlayerPresenter {
func handlePause() {
delegate?.pauseEngine()
self.delegate?.updateLockscreenPaused()
}
func handlePlay() {
delegate?.playEngine()
self.delegate?.updateLockscreenPlaying()
}
func handleTogglePlayingAndPausing() {
if isPlaying {
if isPlaying == .playing {
handlePause()
} else {
} else if isPlaying == .paused {
handlePlay()
}
}
@@ -156,13 +158,15 @@ extension SAPlayerPresenter {
func handleSetSpeed(withMultiple: Double) {
delegate?.setSpeedEngine(withMultiple: withMultiple)
self.delegate?.updateLockscreenChangePlaybackRate(speed: withMultiple)
}
}
//MARK:- For lock screen
extension SAPlayerPresenter {
func getIsPlaying() -> Bool {
return isPlaying
return isPlaying == .playing
}
}
+1 -1
View File
@@ -111,7 +111,7 @@ extension SAPlayer {
- Parameter playingStatus: Whether the player is playing audio or paused.
- Returns: the id for the subscription in the case you would like to unsubscribe to updates for the closure.
*/
public static func subscribe(_ closure: @escaping (_ url: URL, _ playingStatus: Bool) -> ()) -> UInt {
public static func subscribe(_ closure: @escaping (_ url: URL, _ playingStatus: SAPlayingStatus) -> ()) -> UInt {
return AudioClockDirector.shared.attachToChangesInPlayingStatus(closure: { (key, isPlaying) in
guard let url = SAPlayer.shared.getUrl(forKey: key) else { return }
closure(url, isPlaying)
+2 -2
View File
@@ -8,7 +8,7 @@
Pod::Spec.new do |s|
s.name = 'SwiftAudioPlayer'
s.version = '1.1.0'
s.version = '1.3.0'
s.summary = 'SwiftAudioPlayer is a Swift based audio player that can handle streaming from a remote location and audio manipulation.'
# This description is used to generate tags and improve search results.
@@ -31,7 +31,7 @@ SwiftAudioPlayer is a Swift based audio player that can handle streaming from a
s.ios.deployment_target = '10.0'
s.source_files = 'Source/**/*'
s.swift_version = '4.0'
s.swift_version = '4.2'
# s.resource_bundles = {
# 'SwiftAudioPlayer' => ['SwiftAudioPlayer/Assets/*.png']