Giter Site home page Giter Site logo

tanhakabir / swiftaudioplayer Goto Github PK

View Code? Open in Web Editor NEW
536.0 18.0 107.0 1.03 MB

Streaming and realtime audio manipulation with AVAudioEngine

Home Page: https://medium.com/chameleon-podcast/creating-an-advanced-streaming-audio-engine-for-ios-9fbc7aef4115

License: MIT License

Ruby 0.79% Swift 99.21%
avaudioengine swift streaming-audio streaming ios audiotoolbox

swiftaudioplayer's Introduction

SwiftAudioPlayer

Version License Platform Carthage compatible

Swift-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.

This player was built for podcasting. 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.

Thus, using 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.

Basic Features

  1. Realtime audio manipulation that includes going up to 10x speed, using equalizers and other manipulations
  2. Stream online audio using AVAudioEngine
  3. Stream radio
  4. Play locally saved audio with the same API
  5. Download audio
  6. Queue up downloaded and streamed audio for autoplay
  7. Uses only 1-2% CPU for optimal performance for the rest of your app
  8. You're able to install taps and any other AVAudioEngine features to do cool things like skipping silences

Special Features

These 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 to learn how they were implemented using the library.

  1. Skip silences in audio
  2. Sleep timer to stop playing audio after a delay
  3. Loop audio playback for both streamed and saved audio

Requirements

iOS 10.0 and higher.

Getting Started

Running the Example Project

  1. Clone repo
  2. CD to the Example folder where the Example app lives
  3. Run pod install in terminal
  4. Build and run

Installation

SwiftAudioPlayer is available through CocoaPods. To install it, simply add the following line to your Podfile:

pod 'SwiftAudioPlayer'

Usage

Import the player at the top:

import SwiftAudioPlayer

Important: For app in background downloading please refer to note.

To play remote audio:

let url = URL(string: "https://randomwebsite.com/audio.mp3")!
SAPlayer.shared.startRemoteAudio(withRemoteUrl: url)
SAPlayer.shared.play()

To set the display information for the lockscreen:

let info = SALockScreenInfo(title: "Random audio", artist: "Foo", artwork: UIImage(), releaseDate: 123456789)
SAPlayer.shared.mediaInfo = info

To receive streaming progress (for buffer progress %):

@IBOutlet weak var bufferProgress: UIProgressView!

override func viewDidLoad() {
    super.viewDidLoad()

    _ = SAPlayer.Updates.StreamingBuffer.subscribe{ [weak self] buffer in
        guard let self = self else { return }

        self.bufferProgress.progress = Float(buffer.bufferingProgress)

        self.isPlayable = buffer.isReadyForPlaying
    }
}

Look at the Updates section to see usage details and other updates to follow.

For realtime audio manipulations, AVAudioUnit nodes are used. For example to adjust the reverb through a slider in the UI:

@IBOutlet weak var reverbSlider: UISlider!

override func viewDidLoad() {
    super.viewDidLoad()

    let node = AVAudioUnitReverb()
    SAPlayer.shared.audioModifiers.append(node)
    node.wetDryMix = 300
}

@IBAction func reverbSliderChanged(_ sender: Any) {
    if let node = SAPlayer.shared.audioModifiers[1] as? AVAudioUnitReverb {
            node.wetDryMix = reverbSlider.value
        }
}

For a more detailed explanation on usage, look at the Realtime Audio Manipulations section.

For more details and specifics look at the API documentation below.

Contact

Issues or questions

Submit any issues, requests, and questions on the Github repo.

License

SwiftAudioPlayer is available under the MIT license. See the LICENSE file for more info.


API in detail

SAPlayer

Access the player and all of its fields and functions through SAPlayer.shared.

Supported file types

Known supported file types are .mp3 and .wav.

Playing Audio (Basic Commands)

To set up player with audio to play, use either:

  • startSavedAudio(withSavedUrl url: URL, mediaInfo: SALockScreenInfo?) to play audio that is saved on the device.
  • startRemoteAudio(withRemoteUrl url: URL, bitrate: SAPlayerBitrate, mediaInfo: SALockScreenInfo?) to play audio streamed from a remote location.

Both 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.

For streaming remote audio, subscribe to SAPlayer.Updates.StreamingBuffer for updates on streaming progress.

Basic controls available:

play()
pause()
togglePlayAndPause()
seekTo(seconds: Double)
skipForward()
skipBackwards()

Queuing Audio for Autoplay

You can queue either remote or locally saved audio to be played automatically next.

To queue:

SAPlayer.shared.queueSavedAudio(withSavedUrl: C://random_folder/audio.mp3) // or
SAPlayer.shared.queueRemoteAudio(withRemoteUrl: https://randomwebsite.com/audio.mp3)

You can also directly access and modify the queue from SAPlayer.shared.audioQueued.

Important

The 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 for more information.

LockScreen Media Player

Update and set what displays on the lockscreen's media player when the player is active.

skipForwardSeconds and skipBackwardSeconds for the intervals to skip forward and back with.

mediaInfo for the audio's information to display on the lockscreen. Is of type SALockScreenInfo which contains:

title: String
artist: String
artwork: UIImage?
releaseDate: UTC // Int

playbackRateOfAudioChanged(rate: Float) is used to update the lockscreen media player that the playback rate has changed.

SAPlayer.Downloader

Use functionaity from Downloader to save audio files from remote locations for future offline playback.

Audio files are saved under custom naming scheme on device and are recoverable with original remote URL for file.

Important step for background downloads

To ensure that your app will keep downloading audio in the background be sure to add the following to AppDelegate.swift:

func application(_ application: UIApplication, handleEventsForBackgroundURLSession identifier: String, completionHandler: @escaping () -> Void) {
    SAPlayer.Downloader.setBackgroundCompletionHandler(completionHandler)
}

Downloading

All downloads will be paused when audio is streamed from a URL. They will automatically resume when streaming is done.

Use the following to start downloading audio in the background:

func downloadAudio(withRemoteUrl url: URL, completion: @escaping (_ savedUrl: URL) -> ())

It will call the completion handler you pass after successful download with the location of the downloaded file on the device.

Subscribe to SAPlayer.Updates.AudioDownloading for downloading progress updates.

And use the following to stop any active or prevent future downloads of the corresponding remote URL:

func cancelDownload(withRemoteUrl url: URL)

By default downloading will be allowed on cellular data. If you would like to turn this off set:

SAPlayer.Downloader.allowUsingCellularData = false

You can also retrieve what preference you have set for cellular downloads through allowUsingCellularData.

Manage Downloaded

Use the following to manage downloaded audio files.

Checks if downloaded already:

func isDownloaded(withRemoteUrl url: URL) -> Bool

Get URL of audio file saved on device corresponding to remote location:

func getSavedUrl(forRemoteUrl url: URL) -> URL?

Delete downloaded audio if it exists:

func deleteDownloaded(withSavedUrl url: URL)

NOTE: You're in charge or clearing downloads when your don't need them anymore

SAPlayer.Updates

Receive updates for changing values from the player, such as the duration, elapsed time of playing audio, download progress, and etc.

All subscription functions for updates take the form of:

func subscribe(_ closure: @escaping (_ payload:  <Payload>) -> ()) -> UInt
  • closure: The closure that will receive the updates. It's recommended to have a weak reference to a class that uses these functions.
  • payload: The updated value.
  • Returns: the id for the subscription in the case you would like to unsubscribe to updates for the closure.

Sometimes there is:

  • url: The corresponding remote URL for the update. In the case there might be multiple files observed, such as downloading many files at once.

Similarily unsubscribe takes the form of:

func unsubscribe(_ id: UInt)
  • id: The closure with this id will stop receiving updates.

ElapsedTime

Payload = Double

Changes in the timestamp/elapsed time of the current initialized audio. Aka, where the scrubber's pointer of the audio should be at.

Subscribe to this to update views on changes in position of which part of audio is being played.

Duration

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. 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.

PlayingStatus

Payload = SAPlayingStatus

Changes in the playing status of the player. Can be one of the following 4: playing, paused, buffering, ended (audio ended).

StreamingBuffer

Payload = SAAudioAvailabilityRange

Changes 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.

For progress of downloading audio that saves to the phone for playback later, look at AudioDownloading instead.

AudioDownloading

Payload = Double

Changes in the progress of downloading audio in the background. This does not correspond to progress in streaming downloads, look at StreamingBuffer for streaming progress.

AudioQueue

Payload = URL

Notification of the URL of the upcoming audio to be played. This URL may be remote or locally saved.

Audio Effects

Realtime Audio Manipulation

All audio effects on the player is done through AVAudioUnit nodes. These include adding reverb, changing pitch and playback rate, and adding distortion. Full list of effects available here.

The 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.

Note: By default SAPlayer starts off with one node, an 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).

Important

All 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.

Once 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:

let speed = rateSlider.value
if let node = SAPlayer.shared.audioModifiers[0] as? AVAudioUnitTimePitch {
    node.rate = speed
    SAPlayer.shared.playbackRateOfAudioChanged(rate: speed)
}

Note: if the rate of the audio is changed, playbackRateOfAudioChanged should also be called to update the lockscreen's media player.

swiftaudioplayer's People

Contributors

cendolinside123 avatar cntrump avatar dylancom avatar husseinhj avatar jiangdi0924 avatar jonmercer avatar moyinzunza avatar niczyja avatar tanhakabir avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

swiftaudioplayer's Issues

are save file after streaming not implement or have some bugs?

I see comment on code:
`
let shouldSave = doneCallback(id, nil)
if shouldSave && !corruptedBecauseOfSeek {
// TODO want to save file after streaming so we do not have to download again
// guard (task.response?.suggestedFilename?.pathExtension) != nil else {
// Log.monitor("Could not determine file type for file from id: (task.taskDescription ?? "nil") and url: (task.currentRequest?.url?.absoluteString ?? "nil")")
// return
// }

        // TODO no longer saving streamed files
        //            FileStorage.Audio.write(id, fileExtension: fileType, data: data)
    }

`

required condition is false: nil == owningEngine || GetEngine() == owningEngine

Hello. I use MPMediaPickerController to play files. It works fine with first file, but if I add the second file I get crash with error:

required condition is false: nil == owningEngine || GetEngine() == owningEngine

This is my code for MPMediaPickerController:

func mediaPicker(_ mediaPicker: MPMediaPickerController, didPickMediaItems mediaItemCollection: MPMediaItemCollection) {

        dismiss(animated: true, completion: nil)
        let item: MPMediaItem = mediaItemCollection.items[0]
        let pathURL = item.value(forProperty: MPMediaItemPropertyAssetURL) as? URL
        SAPlayer.shared.startSavedAudio(withSavedUrl: pathURL!)
        SAPlayer.shared.play()
    }

Pitch doesn't work because of kAudioUnitSubType_AUiPodTimeOther algorithm of the default AVAudioUnitTimePitch

You should document this and allow to change this, because now I have to apply two modifiers insted of one, or rather to fork your repo and change this in my fork

See https://developer.apple.com/forums/thread/6050

var componentDescription: AudioComponentDescription {
get {
var ret = AudioComponentDescription()
ret.componentType = kAudioUnitType_FormatConverter
ret.componentSubType = kAudioUnitSubType_AUiPodTimeOther
return ret
}
}
audioModifiers.append(AVAudioUnitTimePitch(audioComponentDescription: componentDescription))

Figure out appropriate rate and possible rate limitations for skip silences feature

Copying over discussion:

@tanhakabir:
I don't want to use hardcoded 1.5 for speeding up to skip silences as they might have the audio rate set to 4 or something by default.

There's 2 options here:

  1. We change this to add to the current rate by some appropriate factor. I'm think this rate increase for skipping silences should be less at higher rates so we'd need to determine some sort of ratio.
  2. We assume that at rates above some number, like 3, the skip silences feature wouldn't be noticeable/helpful and then we could just restrict the rate change to a lower range of original rates. This makes it easy to just add some constant to the current rate.

@jw1540:
That's a really good point that I hadn't considered. I like both suggestions and think a mix of the two would be best? Applying some appropriate factor definitely seems the right way to go and if the rate increases beyond to a point where the skip would be negligible it just switches the tap off?

Issue downloading audio

It seems to work on simulator but not on device. Callbacks with progress and completion handler never reach.

song finish at incorect time/duration on very bad connection

so it's happend when I try playing streaming from this url "https://cdn.fastlearner.media/bensound-rumble.mp3" and on debuger it's say " SwiftAudioPlayer_Example[446:56285] [DEBUG] AudioConverterErrors.swift:errorDescription:78:: Reached the end of the file
" but the song not finish at the same duration with the song duration, because of that I should drag the progress bar/seekbar forward or backward a little bit to make it play again and finish the song at correct duration
I test it on 2.8.2 but still happened on 2.9.0

how to reproduce it:
set network link conditioner to very bad connection
IMG20200310135558

http://www.mediafire.com/file/c8v9ymnxbcdm56k/VID20200218161413.mp4/file

this tested on
ios: 13.1.2
iphone: iphone 6s

Icy metadata and custom headers

Hi,
I just discovered your library and I was wondering if it implements icy-metadata (timedMetadata in AVPlayer) and also custom headers (like using a custom user-agent).
Thanks!

Library Stopped working after upgrading to Xcode 12

Please can you advise on this issue after upgrading to xcode 12 IOS 14
here is the error log & the code that has the problem in AudioParser.swift

 init(withRemoteUrl url: AudioURL, parsedFileAudioFormatCallback: @escaping(AVAudioFormat) -> ()) throws {
        self.url = url
        self.parsedFileAudioFormatCallback = parsedFileAudioFormatCallback
        self.throttler = AudioThrottler(withRemoteUrl: url, withDelegate: self)
        
        let context = unsafeBitCast(self, to: UnsafeMutableRawPointer.self)
        //Open the stream and when we call parse data is fed into this stream
        guard AudioFileStreamOpen(context, ParserPropertyListener, **ParserPacketListener**, kAudioFileMP3Type, &streamID) == noErr else {
            throw ParserError.couldNotOpenStream
        }
    }

Cannot convert value of type '(UnsafeMutableRawPointer, UInt32, UInt32, UnsafeRawPointer, UnsafeMutablePointer) -> ()' to expected argument type 'AudioFileStream_PacketsProc' (aka '@convention(c) (UnsafeMutableRawPointer, UInt32, UInt32, UnsafeRawPointer, Optional<UnsafeMutablePointer>) -> ()')

Application freezes under undetermined conditions

Actually, it doesnt freeze, becuase I use Flutter, which has it's own rendering threads, but it stops receiving any touch events.
Seems to be like the seek function causes that.
This is what I see in the debugger when I click the debugger pause button at any time when application is frozen.

UPD: it almost 100% happens when I seek to the very end of file, and also sometimes if I try to seek fast.

image

Add documentation for skip silences feature

Added Skip Silences feature but we have no documentation for the metering logic that it uses.

@jw1540 would you be able to link to me the resources you used or document the scaling function and the tap callback?

unexpected player stops automatically

I m streaming song and i found below issues
i keep log and it like

====== Player Status ====== ended
====== Player Status ====== playing

====== Player Status ====== buffering
====== Player Status ====== paused

it automatically paused , also I noticed , song total duration is incorrect first time and it increase till get actual time

Please help me , asap

Radio stream support

When I play a radio stream with startRemoteAudio, it's really buggy.
A lot of times it will pause shortly after, or increase the playback rate automatically, or not even play at all.
Does the player expect a stream with a known length or something?

Look into PCM buffer leak

We need to hold onto the pointer of the buffer we create and deallocate that once we're about to create a new buffer, or even reuse the same buffer instead of creating a new one.

Should help with:
Screen Shot 2021-03-18 at 9 51 18 AM

Using SwiftAudioPlayer with pre-existing AVAudioEngine graphs

Hi,

Thanks for sharing this impressive project!

In many cases, you can expect clients to have a pre-existing AVAudioEngine already set up to play and process sounds from different sources (e.g. from an AVAudioSourceNode wrapping a synth, or from AVPlayerNode playing back local files).

So what approach would you recommend if we want to integrate this into a pre-existing audio engine? Maybe wrapping the SwiftAudioPlayer in an AUAudioUnit subclass?

Using Swift Audio Player in TableView

I have audio in each cell of table view and I want to to get the duration of each audio in cell as the screen load.Can you please explain how can I do it.
And second is if and audio stream is completed and played once then I want to play it again will it be stream again?

Bug: play / pause work incorrect by seeking streamed audio, resulting in blank playback

When the audio seeking on the lock screen is used and the track is changed to different positions, than blank empty audio is played and the play/pause button cannot be triggered to pause and is always playing

moreover when enabling debug, i always get a bunch of

[DEBUG] AudioConverterErrors.swift:errorDescription:72:: Not enough data for read-conversion operation

no matter the source or the stream

Streaming/Subsonic server issue

Trying to get this library to work with a subsonic server, I see the server transcoding and serving the audio, but the player only ever gets into the buffering state, no playback actuall occurs.

Any ideas?

Can this property be opened?

I need to use this property to analyze the audio volume in real time.

public var playerNode: AVAudioPlayerNode? {
get {
return player?.playerNode
}
}

fixing mini audio player on lockscreen

Sory for bithering you, so after see LockScreenViewProtocol.swift is look like you have a problem on it, so I hope i can help it.
on
SAPlayer.swift

if #available(iOS 10.0, *) {
     try? AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback, mode: AVAudioSessionModeDefault, options: .allowAirPlay)
 }
else{
    try? AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback, with: .allowAirPlay)
}

before
try AVAudioSession.sharedInstance().setActive(true, with: .notifyOthersOnDeactivation) to keep AVAudioEngine keep runing on background

so it would be look like this

private func becomeDeviceAudioPlayer() {
        do {
            if #available(iOS 11.0, *) {
//                try AVAudioSession.sharedInstance().setCategory(.playback, mode: .spokenAudio, policy: .longForm, options: [])
            } else {
                // Fallback on earlier versions
            }
            if #available(iOS 10.0, *) {
                try? AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback, mode: AVAudioSessionModeDefault, options: .allowAirPlay)
            }
            else{
                try? AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback, with: .allowAirPlay)
            }
            try AVAudioSession.sharedInstance().setActive(true, with: .notifyOthersOnDeactivation)
        } catch {
            Log.monitor("Problem setting up AVAudioSession to play in:: \(error.localizedDescription)")
        }
    }

and on SAPlayerPresenter.swift

add
self.delegate?.setLockScreenControls(presenter: self)
after
delegate?.playEngine() , handlePause() , delegate?.pauseEngine() ,or
handlePlay()

so, it'll look like this

func handlePause() {
        delegate?.pauseEngine()
self.delegate?.setLockScreenControls(presenter: self)
    }
    
    func handlePlay() {
        delegate?.playEngine()
self.delegate?.setLockScreenControls(presenter: self)
    }

already tested on iphone 6 ios 13 and iphone 5 ios 10.3
*when test it on example, make sure to enable background capability on audio and fetch

Can't play audio from the firebase storage

I'm trying to play audio from the firebase storage, but getting errors

UPD: I guess that's probably related to the .m4a format, because I tried playing .mp3 now and it's working.
What are the supported formats in whole, I'm struggling to find anything about this in your readme/comments?

Temp link, it will expire in some time:
https://firebasestorage.googleapis.com/v0/b/calmstory-dev.appspot.com/o/audio%2Fyxngxr1%2FzoxjteZdtDFgZjucyizK.m4a?alt=media&token=d0c38847-5fae-4da6-b5d7-3012705213e4

2020-10-14 15:30:06.956029+0300 Runner[13667:95986] [WARNING] AudioParser.swift:getOffset(fromPacketIndex:):207:: should not get here
2020-10-14 15:30:06.956273+0300 Runner[13667:95986] [DEBUG] AudioConverterErrors.swift:errorDescription:72:: Not enough data for read-conversion operation

how to use

please mention which module to import to use this library in swift ios project

Playback stops (playing status == .buffering) after 55 seconds when streaming

Hi @tanhakabir ๐Ÿ‘‹ Fisrt of all let me say that I'm impressed by the amount of work you've put into creating this player ๐Ÿ™‡โ€โ™‚๏ธ

I'm experiencing an issue when streaming an audio file. Player is playing up until 55th second and then it stops. It happens in the example app and in my app consistently.

When I turned debug logging I can see that at this point AudioStreamEngine runs out of buffers and status is changed to buffering. When I skip over this point during playback (before it comes to this time) everything works fine, so the data is there is't just not passed over to the engine. Here's the log from this moment:

2021-04-15 11:13:49.219419+0200 Euphoric[4607:1766321] [DEBUG ๐Ÿ๐Ÿ๐Ÿ๐Ÿ] AudioStreamEngine.swift:updateNetworkBufferRange():210:: [Optional("loaded (0.0, 4002.089795918367), numberOfBuffersScheduledInTotal: 1, isPlayable: true")]
2021-04-15 11:13:49.316270+0200 Euphoric[4607:1766321] [DEBUG ๐Ÿ๐Ÿ๐Ÿ๐Ÿ] AudioStreamEngine.swift:numberOfBuffersScheduledInTotal:74:: [Optional("number of buffers scheduled in total: 0")]
2021-04-15 11:13:49.407243+0200 Euphoric[4607:1766321] [INFO ๐Ÿ–ค๐Ÿ–ค๐Ÿ–ค๐Ÿ–ค] AudioStreamEngine.swift:isPlayable:126:: isPlayable status changed: false
2021-04-15 11:13:49.407730+0200 Euphoric[4607:1766321] [DEBUG ๐Ÿ๐Ÿ๐Ÿ๐Ÿ] AudioStreamEngine.swift:updateNetworkBufferRange():210:: [Optional("loaded (0.0, 4002.089795918367), numberOfBuffersScheduledInTotal: 0, isPlayable: false")]

I'd like to fix the issue myself, but I don't have good understanding of the code yet, so I was hoping that maybe you as an author have rough idea where this issue may come from and where should I start looking in the code.

Best,
Maciej.

stream large files (170mb, 3 hours) never get working

i get a lot of

[DEBUG] AudioConverterErrors.swift:errorDescription:86:: Could not create a PCM Buffer because reader does not have a converter yet
buffer progress: nan

but it will never buffer.
any suggestions?

1 'AVAudioPlayerNode' instance per engine?

The engine currently has a single playerNode = AVAudioPlayerNode(). Are there plans to allow streaming of multiple sounds at the same time?

I see that SAPlayer uses player = AudioStreamEngine(withRemoteUrl: url, delegate: presenter) to initiate streaming data into the engine itself. But the engine only has a single AVAudioPlayerNode pointer (playerNode). How difficult would it be to create an array of AVAudioPlayerNode ?


Next day follow up:

I created a StreamingAudioPlayerNode object & an accompanying AudioURLStream object & carried logic out of the AudioStreamEngine (& some logic out of the AudioEngine) & placed it inside the AudioURLStream object.

Now AudioEngine has an array of StreamingAudioPlayerNode objects. I pass a URL into the engine & it creates an StreamingAudioPlayerNode instance. Calling play makes the streamed audio work on a device (I even used URL's from your example project for testing).
However, streaming of multiple audio sources still does NOT work.

I'm guessing the issue lies deeper within the library (haven't completely studied it) & how buffers are created, or something like that.
Any ideas what I can try doing?

[Feature Request] Add in ability to pause after a delay, "skip" silences.

Hey,

Recently forked the project because I needed a couple of custom methods on the player but wanted to offer them as suggestions:

  1. Ability to pause after a delay (useful for sleep timers).
  2. Skipping silences.

For 1 I have an implementation on a fork and would be happy to raise a PR. For the second feature request I have a very rough implementation which I can share, though I'm not sure it's the best possible solution. Let me know if this is something you'd think would be valuable and I'll raise a PR!

Cheers,
Joe

crash when press cancel streaming

device: iphone 6s
os: ios 13.1.2
crash message:
2020-02-18 14:28:01.732206+0700 SwiftAudioPlayer_Example[421:31675] [WARNING] AudioStreamWorker.swift:stop(withId:):198:: stream_error tried to stop a task that's in state: 3
2020-02-18 14:28:01.733392+0700 SwiftAudioPlayer_Example[421:31675] [avae] AVAEInternal.h:76 required condition is false: [AVAudioEngine.mm:421:AttachNode: (nil == owningEngine || GetEngine() == owningEngine)]
2020-02-18 14:28:01.734817+0700 SwiftAudioPlayer_Example[421:31675] *** Terminating app due to uncaught exception 'com.apple.coreaudio.avfaudio', reason: 'required condition is false: nil == owningEngine || GetEngine() == owningEngine'
*** First throw call stack:
(0x188cd898c 0x188a010a4 0x188bdad1c 0x19564de24 0x19564dd94 0x1956f6324 0x102c3e104 0x102c29630 0x102c29008 0x102bfa814 0x102bfb610 0x102c1558c 0x102bf9ec0 0x102782374 0x1027826dc 0x18cce7a44 0x18c72b6d0 0x18c72ba34 0x18c72aa50 0x18cd20d68 0x18cd220a8 0x18ccfeae8 0x18cd7623c 0x18cd78798 0x18cd7160c 0x188c567e0 0x188c56738 0x188c55ed0 0x188c5101c 0x188c508bc 0x192abc328 0x18cce66d4 0x1027875e8 0x188adb460)
libc++abi.dylib: terminating with uncaught exception of type NSException

PlayerStatus callback suddenly return false value cause by the buffer is "Not enough data for read-conversion operation"

so it's happend when I try playing streaming from this url "https://cdn.fastlearner.media/bensound-rumble.mp3" using project from example folder.
The SAPlayer.Updates.PlayingStatus.subscribe sometime return false and player stopping playing the song.
after I try to debuging process inside converter.pullBuffer(withSize: PCM_BUFFER_SIZE) the error cause on the buffer is "Not enough data for read-conversion operation" with no OSStatus error ReaderNotEnoughDataError (932332582)
Almost all streaming with duration below 1 hour got this error

this happen on
ios: 10.3 , 13.1.2 (rarely happend in this os version)
iphone: iphone 5, iphone 6s (rarely happened)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.