{"id":13465354,"url":"https://github.com/tanhakabir/SwiftAudioPlayer","last_synced_at":"2025-03-25T16:31:38.510Z","repository":{"id":43580343,"uuid":"168096792","full_name":"tanhakabir/SwiftAudioPlayer","owner":"tanhakabir","description":"Streaming and realtime audio manipulation with AVAudioEngine","archived":false,"fork":false,"pushed_at":"2022-12-10T04:41:36.000Z","size":1054,"stargazers_count":588,"open_issues_count":48,"forks_count":108,"subscribers_count":16,"default_branch":"master","last_synced_at":"2025-03-19T21:59:32.345Z","etag":null,"topics":["audiotoolbox","avaudioengine","ios","streaming","streaming-audio","swift"],"latest_commit_sha":null,"homepage":"https://medium.com/chameleon-podcast/creating-an-advanced-streaming-audio-engine-for-ios-9fbc7aef4115","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tanhakabir.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-01-29T05:44:47.000Z","updated_at":"2025-03-18T16:05:49.000Z","dependencies_parsed_at":"2022-07-13T02:50:28.511Z","dependency_job_id":null,"html_url":"https://github.com/tanhakabir/SwiftAudioPlayer","commit_stats":null,"previous_names":[],"tags_count":55,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tanhakabir%2FSwiftAudioPlayer","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tanhakabir%2FSwiftAudioPlayer/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tanhakabir%2FSwiftAudioPlayer/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tanhakabir%2FSwiftAudioPlayer/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tanhakabir","download_url":"https://codeload.github.com/tanhakabir/SwiftAudioPlayer/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245500231,"owners_count":20625529,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["audiotoolbox","avaudioengine","ios","streaming","streaming-audio","swift"],"created_at":"2024-07-31T15:00:28.160Z","updated_at":"2025-03-25T16:31:38.160Z","avatar_url":"https://github.com/tanhakabir.png","language":"Swift","funding_links":[],"categories":["Libs","Audio [🔝](#readme)","HarmonyOS","Swift","Media and Graphics","OOM-Leaks-Crash"],"sub_categories":["Audio","Windows Manager","Player"],"readme":"# SwiftAudioPlayer\n\n[![Version](https://img.shields.io/cocoapods/v/SwiftAudioPlayer.svg?style=flat)](https://cocoapods.org/pods/SwiftAudioPlayer)\n[![License](https://img.shields.io/cocoapods/l/SwiftAudioPlayer.svg?style=flat)](https://cocoapods.org/pods/SwiftAudioPlayer)\n[![Platform](https://img.shields.io/cocoapods/p/SwiftAudioPlayer.svg?style=flat)](https://cocoapods.org/pods/SwiftAudioPlayer)\n[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage)\n\nSwift-based audio player with AVAudioEngine as its base. Allows for: streaming online audio, playing local file, changing audio speed (3.5X, 4X, 32X), pitch, and real-time audio manipulation using custom [audio enhancements](https://developer.apple.com/documentation/avfoundation/audio_track_engineering/audio_engine_building_blocks/audio_enhancements).\n\nThis player was built for [podcasting](https://chameleonpodcast.com/). We originally used AVPlayer for playing audio but we wanted to manipulate audio that was being streamed. We set up AVAudioEngine at first just to play a file saved on the phone and it worked great, but AVAudioEngine on its own doesn't support streaming audio as easily as AVPlayer.\n\nThus, using [AudioToolbox](https://developer.apple.com/documentation/audiotoolbox), we are able to stream audio and convert the downloaded data into usable data for the AVAudioEngine to play. For an overview of our solution check out our [blog post](https://medium.com/chameleon-podcast/creating-an-advanced-streaming-audio-engine-for-ios-9fbc7aef4115).\n\n### Basic Features\n\n1. Realtime audio manipulation that includes going up to 10x speed, using [equalizers and other manipulations](https://developer.apple.com/documentation/avfaudio/avaudiouniteq)\n1. Stream online audio using AVAudioEngine\n1. Stream radio\n1. Play locally saved audio with the same API\n1. Download audio\n1. Queue up downloaded and streamed audio for autoplay\n1. Uses only 1-2% CPU for optimal performance for the rest of your app\n1. You're able to install taps and any other AVAudioEngine features to do cool things like skipping silences\n\n### Special Features\nThese are community supported audio manipulation features using this audio engine. You can implement your own version of these features and you can look at [SAPlayerFeatures](https://github.com/tanhakabir/SwiftAudioPlayer/blob/master/Source/SAPlayerFeatures.swift) to learn how they were implemented using the library.\n1. Skip silences in audio\n1. Sleep timer to stop playing audio after a delay\n1. Loop audio playback for both streamed and saved audio\n\n### Requirements\n\niOS 10.0 and higher.\n\n## Getting Started\n\n### Running the Example Project\n\n1. Clone repo\n2. CD to the `Example` folder where the Example app lives\n3. Run `pod install` in terminal\n4. Build and run\n\n### Installation\n\nSwiftAudioPlayer is available through [CocoaPods](https://cocoapods.org). To install\nit, simply add the following line to your Podfile:\n\n```ruby\npod 'SwiftAudioPlayer'\n```\n\n### Usage\n\nImport the player at the top:\n```swift\nimport SwiftAudioPlayer\n```\n\n**Important:** For app in background downloading please refer to [note](#important-step-for-background-downloads).\n\nTo play remote audio:\n```swift\nlet url = URL(string: \"https://randomwebsite.com/audio.mp3\")!\nSAPlayer.shared.startRemoteAudio(withRemoteUrl: url)\nSAPlayer.shared.play()\n```\n\nTo set the display information for the lockscreen:\n```swift\nlet info = SALockScreenInfo(title: \"Random audio\", artist: \"Foo\", artwork: UIImage(), releaseDate: 123456789)\nSAPlayer.shared.mediaInfo = info\n```\n\nTo receive streaming progress (for buffer progress %):\n```swift\n@IBOutlet weak var bufferProgress: UIProgressView!\n\noverride func viewDidLoad() {\n    super.viewDidLoad()\n\n    _ = SAPlayer.Updates.StreamingBuffer.subscribe{ [weak self] buffer in\n        guard let self = self else { return }\n\n        self.bufferProgress.progress = Float(buffer.bufferingProgress)\n\n        self.isPlayable = buffer.isReadyForPlaying\n    }\n}\n```\nLook at the [Updates](#saplayerupdates) section to see usage details and other updates to follow.\n\n\nFor realtime audio manipulations, [AVAudioUnit](https://developer.apple.com/documentation/avfoundation/avaudiounit) nodes are used. For example to adjust the reverb through a slider in the UI:\n```swift\n@IBOutlet weak var reverbSlider: UISlider!\n\noverride func viewDidLoad() {\n    super.viewDidLoad()\n\n    let node = AVAudioUnitReverb()\n    SAPlayer.shared.audioModifiers.append(node)\n    node.wetDryMix = 300\n}\n\n@IBAction func reverbSliderChanged(_ sender: Any) {\n    if let node = SAPlayer.shared.audioModifiers[1] as? AVAudioUnitReverb {\n            node.wetDryMix = reverbSlider.value\n        }\n}\n```\nFor a more detailed explanation on usage, look at the [Realtime Audio Manipulations](#realtime-audio-manipulation) section.\n\nFor more details and specifics look at the [API documentation](#api-in-detail) below.\n\n\n## Contact\n\n### Issues or questions\n\nSubmit any issues, requests, and questions [on the Github repo](https://github.com/tanhakabir/SwiftAudioPlayer/issues).\n\n### License\n\nSwiftAudioPlayer is available under the MIT license. See the LICENSE file for more info.\n\n---\n\n# API in detail\n\n## SAPlayer\n\nAccess the player and all of its fields and functions through `SAPlayer.shared`.\n\n### Supported file types\n\nKnown supported file types are `.mp3` and `.wav`.\n\n### Playing Audio (Basic Commands)\n\nTo set up player with audio to play, use either:\n* `startSavedAudio(withSavedUrl url: URL, mediaInfo: SALockScreenInfo?)` to play audio that is saved on the device.\n* `startRemoteAudio(withRemoteUrl url: URL, bitrate: SAPlayerBitrate, mediaInfo: SALockScreenInfo?)` to play audio streamed from a remote location.\n\nBoth of these expect a URL of the location of the audio and an optional media information to display on the lockscreen.  For streamed audio you can optionally set the bitrate to be `.high` or `.low`. High is more performant but won't work well for radio streams; for radio streams you should use low. The default bitrate if you don't set it is `.high`.\n\nFor streaming remote audio, subscribe to `SAPlayer.Updates.StreamingBuffer` for updates on streaming progress.\n\nBasic controls available:\n```swift\nplay()\npause()\ntogglePlayAndPause()\nseekTo(seconds: Double)\nskipForward()\nskipBackwards()\n```\n\n### Queuing Audio for Autoplay\n\nYou can queue either remote or locally saved audio to be played automatically next.\n\nTo queue:\n```swift\nSAPlayer.shared.queueSavedAudio(withSavedUrl: C://random_folder/audio.mp3) // or\nSAPlayer.shared.queueRemoteAudio(withRemoteUrl: https://randomwebsite.com/audio.mp3)\n```\n\nYou can also directly access and modify the queue from `SAPlayer.shared.audioQueued`.\n\n#### Important\n\nThe engine can handle audio manipulations like speed, pitch, effects, etc. To do this, nodes for effects must be finalized before initialize is called. Look at [audio manipulation documentation](#realtime-audio-manipulation) for more information.\n\n### LockScreen Media Player\n\nUpdate and set what displays on the lockscreen's media player when the player is active.\n\n`skipForwardSeconds` and `skipBackwardSeconds` for the intervals to skip forward and back with.\n\n`mediaInfo` for the audio's information to display on the lockscreen. Is of type `SALockScreenInfo` which contains:\n```swift\ntitle: String\nartist: String\nartwork: UIImage?\nreleaseDate: UTC // Int\n```\n\n`playbackRateOfAudioChanged(rate: Float)` is used to update the lockscreen media player that the playback rate has changed.\n\n## SAPlayer.Downloader\n\nUse functionaity from Downloader to save audio files from remote locations for future offline playback.\n\nAudio files are saved under custom naming scheme on device and are recoverable with original remote URL for file.\n\n#### Important step for background downloads\n\nTo ensure that your app will keep downloading audio in the background be sure to add the following to `AppDelegate.swift`:\n\n```swift\nfunc application(_ application: UIApplication, handleEventsForBackgroundURLSession identifier: String, completionHandler: @escaping () -\u003e Void) {\n    SAPlayer.Downloader.setBackgroundCompletionHandler(completionHandler)\n}\n```\n\n### Downloading\n\nAll downloads will be paused when audio is streamed from a URL. They will automatically resume when streaming is done.\n\nUse the following to start downloading audio in the background:\n\n```swift\nfunc downloadAudio(withRemoteUrl url: URL, completion: @escaping (_ savedUrl: URL) -\u003e ())\n```\n\nIt will call the completion handler you pass after successful download with the location of the downloaded file on the device.\n\nSubscribe to `SAPlayer.Updates.AudioDownloading` for downloading progress updates.\n\nAnd use the following to stop any active or prevent future downloads of the corresponding remote URL:\n\n```swift\nfunc cancelDownload(withRemoteUrl url: URL)\n```\n\nBy default downloading will be allowed on cellular data. If you would like to turn this off set:\n```swift\nSAPlayer.Downloader.allowUsingCellularData = false\n```\nYou can also retrieve what preference you have set for cellular downloads through `allowUsingCellularData`.\n\n### Manage Downloaded\n\nUse the following to manage downloaded audio files.\n\nChecks if downloaded already:\n```swift\nfunc isDownloaded(withRemoteUrl url: URL) -\u003e Bool\n```\n\nGet URL of audio file saved on device corresponding to remote location:\n```swift\nfunc getSavedUrl(forRemoteUrl url: URL) -\u003e URL?\n```\n\nDelete downloaded audio if it exists:\n```swift\nfunc deleteDownloaded(withSavedUrl url: URL)\n```\n\n**NOTE:** You're in charge or clearing downloads when your don't need them anymore\n\n## SAPlayer.Updates\n\nReceive updates for changing values from the player, such as the duration, elapsed time of playing audio, download progress, and etc.\n\nAll subscription functions for updates take the form of:\n```swift\nfunc subscribe(_ closure: @escaping (_ payload:  \u003cPayload\u003e) -\u003e ()) -\u003e UInt\n```\n\n- `closure`: The closure that will receive the updates. It's recommended to have a weak reference to a class that uses these functions.\n- `payload`: The updated value.\n- Returns: the id for the subscription in the case you would like to unsubscribe to updates for the closure.\n\nSometimes there is:\n- `url`: The corresponding remote URL for the update. In the case there might be multiple files observed, such as downloading many files at once.\n\nSimilarily unsubscribe takes the form of:\n```swift\nfunc unsubscribe(_ id: UInt)\n```\n\n- `id`: The closure with this id will stop receiving updates.\n\n\n### ElapsedTime\nPayload = `Double`\n\nChanges in the timestamp/elapsed time of the current initialized audio. Aka, where the scrubber's pointer of the audio should be at.\n\nSubscribe to this to update views on changes in position of which part of audio is being played.\n\n### Duration\nPayload = `Double`\n\nChanges in the duration of the current initialized audio. Especially helpful for audio that is being streamed and can change with more data. The engine makes a best effort guess as to the duration of the audio. The guess gets better with more bytes streamed from the web.\n\n### PlayingStatus\nPayload = `SAPlayingStatus`\n\nChanges in the playing status of the player. Can be one of the following 4: `playing`, `paused`, `buffering`, `ended` (audio ended).\n\n### StreamingBuffer\nPayload = `SAAudioAvailabilityRange`\n\nChanges in the progress of downloading audio for streaming. Information about range of audio available and if the audio is playable. Look at SAAudioAvailabilityRange for more information.\n\nFor progress of downloading audio that saves to the phone for playback later, look at AudioDownloading instead.\n\n### AudioDownloading\nPayload = `Double`\n\nChanges in the progress of downloading audio in the background. This does not correspond to progress in streaming downloads, look at StreamingBuffer for streaming progress.\n\n### AudioQueue\nPayload = `URL`\n\nNotification of the URL of the upcoming audio to be played. This URL may be remote or locally saved.\n\n## Audio Effects\n\n### Realtime Audio Manipulation\n\nAll audio effects on the player is done through [AVAudioUnit](https://developer.apple.com/documentation/avfoundation/avaudiounit) nodes. These include adding reverb, changing pitch and playback rate, and adding distortion. Full list of effects available [here](https://developer.apple.com/documentation/avfoundation/audio_track_engineering/audio_engine_building_blocks/audio_enhancements).\n\nThe effects intended to use are stored in `audioModifiers` as a list of nodes. These nodes are in the order that the engine will attach them to one another.\n\n**Note:** By default `SAPlayer` starts off with one node, an [AVAudioUnitTimePitch](https://developer.apple.com/documentation/avfoundation/avaudiounittimepitch) node, that is set to change the rate of audio without changing the pitch of the audio (intended for changing the rate of spoken word).\n\n#### Important\nAll the nodes intended to be used on the playing audio must be finalized before calling `initializeSavedAudio(...)` or `initializeRemoteAudio(...)`. Any changes to list of nodes after initialize is called for a given audio file will not be reflected in playback.\n\nOnce all nodes are added to `audioModifiers` and the player has been initialized, any manipulations done with the nodes are performed in realtime. The example app shows manipulating the playback rate in realtime:\n\n```swift\nlet speed = rateSlider.value\nif let node = SAPlayer.shared.audioModifiers[0] as? AVAudioUnitTimePitch {\n    node.rate = speed\n    SAPlayer.shared.playbackRateOfAudioChanged(rate: speed)\n}\n```\n\n**Note:** if the rate of the audio is changed, `playbackRateOfAudioChanged` should also be called to update the lockscreen's media player.\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftanhakabir%2FSwiftAudioPlayer","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftanhakabir%2FSwiftAudioPlayer","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftanhakabir%2FSwiftAudioPlayer/lists"}