I want to use the AVAudioPlayer to play a previously recorded file. That recording works fine, and I saved the file to the temp directory.
The problem is, as soon as I setup the AVAudioPlayer, it starts buffering and will not start playing before fully buffering that recording. Now it may work fine with recordings of about 30 seconds, but I also want it to work with like 1 hour long recordings. I am talking about > 1 minute of waiting.
How do I force AVAudioPlayer to just begin playing with the current buffer?
Like I can tell AVPlayer with player.automaticallyWaitsToMinimizeStalling = false
because I don't expect there to be any stalling issue.
Or how do I get something like averagePower()->Float from AVPlayer?
I could not use AVPlayer because I need something like AVAudioPlayer's func averagePower(forChannel channelNumber: Int) -> Float to show a visualized presentation of the recording. If you know how to get to those averagePower values from AVPlayer, -Item, -Asset or -Track, that would be fine too, I could not find anything fitting.
I found that, if you use AVAudioPlayer but call player.play() on another thread, it is not waiting for the buffer and starts playing right away.
DispatchQueue.global(qos: .background).async {
self.player.play()
}
But this comes with some restrictions, because now I can't use player.play(at: TimeInverval) because that seems to interrupt building the buffer and then the file will not be played to its full duration. I worked around that by setting player.currentTime before player.play(), which does not to bother the buffer.
Related
I have large looping background music files of up to 10 minutes length. The sounds are looped perfectly and if the player introduces no delay you would not notice where the loop point is.
Can AVAudioPlayer play them without that the user will hear a gap caused by latency or other problems with looping?
You might be hearing a delay because your music is compressed (mp3 for example). AVAudioPlayer has to decode the mp3 as it streams. If you had the same music in an uncompressed format, it would take significantly more disk space, but would loop seamlessly.
As long as you don't stop/start it at the end and you set numberOfLoops to a negative integer, it should work.
I had problems trying to create seamless loops. I tried using the numberOfLoops property, with no success. I tried switching to uncompressed audio files, with no success. I even tried switching to an AVAudioQueuePlayer, queued with multiple instances of the same asset, with no success. In the end I solved the problem by creating two instances of AVAudioPlayer (player1 & player2) and switching between them. I began playback of player1 and used player2's prepareToPlay method, and the play(atTime time:) method setting the TimeInterval to player1.deviceCurrentTime - player1.currentTime + player1.duration.
I then used the delegate method audioPlayerDidFinishPlaying to prepare player1 to restart playback after player2 was done. To be honest, I don't know why this approach worked and simply setting numberOfLoops to -1 did not. But it may work for you also
What is the correct way to begin playback of a video from a specific time?
Currently, the approach we use is to check at an interval whether it's possible to seek via currentTime and then seek. The problem with this is, when the video fullscreen view pops up, it begins playback from the beginning for up to a second before seeking.
I've tried events such as onloadmetadata and canplay, but those seem to happen too early.
Added information:
It seems the very best I can do is to set a timer that tries to set currentTime repeatedly as soon as play() is called, however, this is not immediate enough. The video loads from the beginning, and after about a second, depending on the device, jumps. This is a problem for me as it provides an unsatisfactory experience to the user.
It seems like there can be no solution which does better, but I'm trying to see if there is either:
a) something clever/undocumented which I have missed which allows you to either seek before loading or otherwise indicate that the video needs to start not from 00:00 but from an arbitrary point
b) something clever which allows you to hide the video while it's playing and not display it until it has seeked (So you would see a longer delay on the phone before the fullscreen video window pops up, but it would start immediately where I need it to instead of seeking)
do something like this;
var video = document.getElementsById("video");
video.currentTime = starttimeoffset;
more Information can be found on this page dedicated to video time offset howtos
For desktop browser Chrome/Safari, you can append #t=starttimeoffsetinseconds to your video src url to make it start from certain position.
For iOS device, the best we can do is to listen for the timeupdated event, and do the seek in there. I guess this is the same as your original approach of using a timer.
-R
I was reading the AVPlayer class documentation and I couldn't find the answer for my question.
I'm playing a streamed audio from the Internet on my iPhone app and I'd like to know if after a [myAVPlayer pause]; invocation myAVPlayer will keep downloading the audio file on the background for a long time.
If the user pushes the "Pause" button, invoking [myAVPlayer pause]; and then leaves the app, will myAVPlayer keep downloading a large amount of data?
I'm concerned about this when the user is on 3G Network.
I am faced with the same question and have done some experimentation. My observations are only valid for video, but if they are also valid for audio, then AVPlayer will try to buffer around 30s of content when you press pause. If you have access to the webserver, you could run tcpdump/wireshark and see how long after you press pause that the server continues to send data.
You can manage how long AVPlayer continues to buffer.
You need to manage preferredForwardBufferDuration of avplayer currentItem. If you want to stop buffering set value to 1 because if you set it to 0 it will be set up automatically
self.avPlayer.currentItem.preferredForwardBufferDuration = 1;
From Apple documentation: This property defines the preferred forward buffer duration in seconds. If set to 0, the player will choose an appropriate level of buffering for most use cases. Setting this property to a low value will increase the chance that playback will stall and re-buffer, while setting it to a high value will increase demand on system resources.
I am making a music game and when the user presses a note it will produce a sound. The sound naturally needs to play immediately when the user presses, so they can tell whether they are in time with the music. However, it feels as if the sound is lagging, especially when note presses become quicker.
My background .m4a music file is played with AVAudioPlayer. I chose to use this over Cocos Denshion as I have access to the currentTime property. I may be wrong, but I dont think I can access this with CocosDenshion.
I made a .wav file which is extremely short (less than a second). I preload my sound effect on init:
[[SimpleAudioEngine sharedEngine] preloadEffect:#"Assist.wav"];
Then to play the sound effect, in CCTouchesBegan I call:
[[SimpleAudioEngine sharedEngine] playEffect:#"Assist.wav"];
After that it calls my code to determine the users timing and awards points. Any idea why it might be lagging, or a better way to play sound effects in time with music?
EDIT: Ive tried a few things recently with no results. First I tried playing the sounds automatically as they came up to the appropriate time in the song. Still had the lag, so I dont think it is touch events being slow. I also tried 3 different sound libraries.
However, when I ran in the simulator, it seemed to not be laggy. Does anyone have an idea? Im clueless and its a major feature I cant really take out...
you should avoid this code:- [[SimpleAudioEngine sharedEngine] preloadEffect:#"Assist.wav"];
with the start of app you should load your framework SimpleAudioEngine by writing this code :-
//SimpleAudioEngine *palySound; made object in .h file.
palySound=[SimpleAudioEngine sharedEngine];
and whenever you want to play sound you can write: [palySound playEffect:#"Assist.wav"];
I am not sure what you're doing in your SoundEngine, but in my own experience, the best way to not get lag to play a sound is to assign an AVAudioPlayer for each sound file (unless you want to start messing around with AudioQueues).
Here it is an example:
Let's assume that you have an AVAudioPlayer *assistPlayer; in your current view controller.
In your viewDidLoad initialize it with your sound:
NSURL *wavURL = [[NSBundle mainBundle] URLForResource:#"Assist" withExtension:#"wav"];
assistPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:wavURL error:nil];
Then, in your IBAction where you want to play the file, just do:
[assistPlayer play];
You shouldn't get any lag.
Did you try Finch? It claims to play sounds with low latency, and it is also just a wrapper around OpenAL.
Other than that, I'm really not experienced with OpenAL, but can think of two possible reasons for your lag:
The main thread is too busy - Try to offload work from it to other
threads.
Perhaps OpenAL is defined with too large of a buffer, so the pipeline loads the entire sound into the buffer (or a big chunk of it), and only afterwards the playback starts.
I have set up an AVAudioPlayer object in viewDidLoad, as per the Apple guidelines, calling prepareToPlay as the last line in viewDidLoad (i have tried in awakeFromNib also).
When i press my play button, there is a pause, as it would appear to load the file, then it plays.
In audioPlayerDidFinishPlaying, i reload the player with a different sound, and when clicking play for a second time, the file plays instantly.
What would cause the player to lag on the first play?
Thanks.
The delay is due to AVAudioPlayer being initialised. Please see this answer.
The audio system runs on several asynchronous software processes (audio units, OS drivers, etc.) and hardware systems (DMA, DACs, audio amp power supplies, etc.) that never really all completely finish initialization until some sound is actually played all the way out the speakers or earphones.
Here's one method to do that: Create a sound file containing a half second of silence. On app start up, while your app and view controller are still loading, use AVAudioPlayer to play this file of silence. Now when your view finishes loading, AVAudioPlayer should be ready to play subsequent non-silent sounds much faster, since some audio (silence) has already already gone all the way out to the speakers.
What kind of sound are you playing? Alerts, something longer? If alerts, I did go this way and it's much better with lags ...
create system sound with AudioServicesCreateSystemSoundID
play system sound with AudioServicesPlaySystemSound
dispose system sound with AudioServicesDisposeSystemSoundID
... you only need to store SystemSoundID for each sound you would like to play.