AVPlayer Video SeekToTime - iphone

I'm using AVPlayer for play my video using slider and Some Buttons.
Here is my methods for moving forward and backward using buttons.
-(IBAction)MoveForward
{
//int value = timeSlider.value*36000 + 10;
//CMTime newTime = CMTimeMakeWithSeconds(value, playspeed);
//CMTime newTime = CMTimeMake(value,(playspeed*timeSlider.maximumValue));
CMTime newTime = CMTimeMakeWithSeconds(timeSlider.value, playspeed);
newTime.value += 60;
[player seekToTime: newTime];
}
-(IBAction)MoveBackward
{
CMTime newTime = CMTimeMakeWithSeconds(timeSlider.value-1, playspeed);
[player seekToTime: newTime];
}
My Problem on this is the time to Seek is not working properly. That navigate to next frame based on seconds. I need to move next frame minutely. Help me...

I don't really understand your code, you don't really need separate methods to move forwards and backwards, you can use the same one for both. I've got a working AVPlayer Movie Player, I'll show you how I did the slider part.
-(IBAction)sliding:(id)sender {
CMTime newTime = CMTimeMakeWithSeconds(seeker.value, 1);
[self.player seekToTime:newTime];
}
-(void)setSlider {
sliderTimer = [[NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(updateSlider) userInfo:nil repeats:YES] retain];
self.seeker.maximumValue = [self durationInSeconds];
[seeker addTarget:self action:#selector(sliding:) forControlEvents:UIControlEventValueChanged];
seeker.minimumValue = 0.0;
seeker.continuous = YES;
}
- (void)updateSlider {
self.seeker.maximumValue = [self durationInSeconds];
self.seeker.value = [self currentTimeInSeconds];
}
- (Float64)durationInSeconds {
Float64 dur = CMTimeGetSeconds(duration);
return dur;
}
- (Float64)currentTimeInSeconds {
Float64 dur = CMTimeGetSeconds([self.player currentTime]);
return dur;
}
And that's it, there are two gotchas in this code, first, the duration property returns a CMTime variable, you have to convert it to a float, also, this returns the raw number of seconds, you have to convert it to h:mm:ss if you want to display time labels. Second, the updateSlider method is triggered by a timer every second.
Good Luck.

The following snippet of code worked for me:
CMTime videoLength = self.mPlayer.currentItem.asset.duration; // Gets the video duration
float videoLengthInSeconds = videoLength.value/videoLength.timescale; // Transfers the CMTime duration into seconds
[self.mPlayer seekToTime:CMTimeMakeWithSeconds(videoLengthInSeconds * [slider value], 1)
completionHandler:^(BOOL finished)
{
dispatch_async(dispatch_get_main_queue(), ^{
isSeeking = NO;
// Do some stuff
});
}];

Related

Reset NSTimer after navigating to another view

Currently I am developing a game which needs a stop watch say 1 minute,when user taps the play button it enters the game screen,I want the to run the stop watch count down of 1 minute 01:00,00:59,00:58 etc. For that reason I searched and found NSTimer would be the ideal choice to implement a stop watch.Hence I took a label,created an instance of NSTimer,assigned time interval and started decrementing the value in timer label,i.e.:
-(void)viewDidAppear:(BOOL)animated
{
self.stopWatchTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(viewWillAppear:) userInfo:nil repeats:YES];
[super viewDidAppear:YES];
}
-(void)viewWillAppear:(BOOL)animated
{
static int currentTime = 60;
int newTime = currentTime--;
int minutesRemaining = newTime / 60; // integer division, truncates fractional part
int secondsRemaining = newTime % 60; // modulo division
self.timerLabel.text = [NSString stringWithFormat:#"%02d:%02d", minutesRemaining, secondsRemaining];
if ([self.timerLabel.text isEqualToString:#"00:00"])
{
[stopWatchTimer invalidate];
}
[super viewWillAppear:YES];
}
The problem here is the timer starts running 01:00,00:59,00:58 and goes on,say at 53rd second,I navigated to another view and came back,it is running from 00:53,00:52 and so on,but I want it to run from 01:00 and for that I implemented invalidating NSTimer in viewDidDisappear i.e.
-(void)viewDidDisappear:(BOOL)animated
{
if ([stopWatchTimer isValid])
{
[stopWatchTimer invalidate];
self.stopWatchTimer = nil;
}
[super viewDidDisappear:YES];
}
Still the same issue exists!
Done lots of Research on the issue and found no answer useful and working.
Can some one please guide me,any help is appreciated.
Thanks every one in advance :)
You are use the selector viewWillAppear for the timer. viewWillAppear is a method on a viewController that gets called when the view appears on screen, you should not call it yourself. Instead, create your own method that decrements the timer:
-(void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
self.stopWatchTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(updateTime:) userInfo:nil repeats:YES];
currentTime = 60; // This could be an instance variable
self.timerLabel.text = #"01:00";
}
-(void)updateTime {
int newTime = currentTime--;
int minutesRemaining = newTime / 60; // integer division, truncates fractional part
int secondsRemaining = newTime % 60; // modulo division
self.timerLabel.text = [NSString stringWithFormat:#"%02d:%02d", minutesRemaining, secondsRemaining];
if ([self.timerLabel.text isEqualToString:#"00:00"])
{
[self.stopWatchTimer invalidate];
}
}
-(void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
if ([self.stopWatchTimer isValid])
{
[self.stopWatchTimer invalidate];
self.stopWatchTimer = nil;
}
}
With this technique the timer is responsible for calling every second, but you will probably have timing issues after some seconds. To have a more accurate timing, you should store the time that you started the countdown and then on every updateTime call, compare the current time with the stored started time.
Add to your interface:
#property (nonatomic) NSTimeInterval startTime;
then in the implementation:
-(void)viewDidAppear:(BOOL)animated
{
self.startTime = [NSDate timeIntervalSinceReferenceDate];
self.duration = 60; // How long the countdown should be
self.stopWatchTimer = [NSTimer scheduledTimerWithTimeInterval:0.1
target:self
selector:#selector(updateTime)
userInfo:nil
repeats:YES];
self.timerLabel.text = #"01:00"; // Make sure this represents the countdown time
}
-(void)updateTime {
int newTime = self.duration - (round([NSDate timeIntervalSinceReferenceDate] - self.startTime));
int minutesRemaining = newTime / 60; // integer division, truncates fractional part
int secondsRemaining = newTime % 60; // modulo division
self.timerLabel.text = [NSString stringWithFormat:#"%02d:%02d", minutesRemaining, secondsRemaining];
if (newTime < 1)
{
[self.stopWatchTimer invalidate];
/* Do more stuff here */
}
}
-(void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
if ([self.stopWatchTimer isValid])
{
[self.stopWatchTimer invalidate];
self.stopWatchTimer = nil;
}
}
Some extra (unrelated) comments on the code:
when implementing viewDidDisappear: pass the animated parameter to the call to super:
[super viewDidDisappear:animated];
when implementing viewDidAppear: pass the animated parameter to the call to super, also, make sure you call super first this in the method, before doing anything else

Cocos2d schedule interval decreaser

I have made a simple timer but in trying to increase the timers speed over time, so I basically want the timer to have an interval of 1.0 and every 10 seconds I want that timer's interval to decrease by 0.1
How can I go about doing that?
[self schedule:#selector(tick:)interval:1.0];
That's in the init section
-(void)tick:(ccTime)dt{
myTime = myTime+1;
totalTime = myTime;
[timeLabel setString:[NSString stringWithFormat:#"%i", totalTime]];
}
That's the timer.
It's a basic timer which has an interval of 1.0 second, however I would like the interval to decrease by 10% every 10 seconds.
in .h file
#property(nonatomic,readWrite)CGFloat lastUpdatedInterval;
in.m file, initialize self.lastUpdatedInterval = 1.0;
then call
[self schedule:#selector(tick:)interval:self.lastUpdatedInterval];
in update loop,
-(void)tick:(ccTime)dt
{
if(myTime>9 && (myTime%10 == 0))
{
[self unschedule:#selector(tick:)];
self.lastUpdatedInterval = self.lastUpdatedInterval - (self.lastUpdatedInterval*0.1);
[self schedule:#selector(tick:)interval:self.lastUpdatedInterval];
}
myTime = myTime+1;
totalTime = myTime;
[timeLabel setString:[NSString stringWithFormat:#"%i", totalTime]];
}

Slider stop at the end of playing audio

In my project I am using an AVAudioPlayer to play my saved audio.I am showing the slider for playing audio. The value of the slider is set using the current time property of the player.
playSlider.value = avAudioPlayer.currentTime;
This is the timer code
updateTimer = [NSTimer scheduledTimerWithTimeInterval:.01 target:self selector:#selector(updateCurrentTime) userInfo:p repeats:YES];
//Calls the update current time function
- (void)updateCurrentTime
{
[self updateCurrentTimeForPlayer:self.player];
}
//Updates the current time while the audio is playing
-(void)updateCurrentTimeForPlayer:(AVAudioPlayer *)avAudioPlayer
{
currentTimeLabel.text = [NSString stringWithFormat:#"%d:%02d", (int)avAudioPlayer.currentTime / 60, (int)avAudioPlayer.currentTime % 60, nil];
playSlider.value = avAudioPlayer.currentTime;
if (playSlider.value==playSlider.minimumValue)
{
moveToBeginningButton.enabled = NO;
rewindButton.enabled = NO;
NSLog(#"PlaySliderMaxValue1 === %f",playSlider.maximumValue);
NSLog(#"PlaySliderValue1 === %f",playSlider.value);
}
else
{
moveToBeginningButton.enabled = YES;
rewindButton.enabled = YES;
NSLog(#"PlaySliderMaxValue2 === %f",playSlider.maximumValue);
NSLog(#"PlaySliderValue2 === %f",playSlider.value);
}
}
The maximum value of slider is set as follows
playSlider.maximumValue = audioPlayer.duration;
The problem that I am experiencing is that the slider point will always return to the beginning of the audio after playing.Is this a bug? How can I change this? Please provide your suggestion.
I cannot say whether this is a bug or not, But try using delegate methods of AVAudioPlayer. In didFinishPlaying method set Slider value to maximumValue. This could be one of the solution.
Print the value of avAudioPlayer.currentTime before assigning it to playslider.value
I think at the end it will become zero. and it will assign zero to playslider.value

UILabel animating number change

I've got an UILabel that displays users score. And score changes from time to time, is there a way to animate this change, to slowly increment this number from its current value to its result value?
Something like http://josheinstein.com/blog/index.php/2010/02/silverlight-animated-turbotax-number-display/ but for objective-c.
Use a CADisplayLink to change the text property of custom subclass of UILabel over some period of time. You'll probably want to use a NSNumberFormatter for prettier output.
// Create instance variables/properties for: `from`, `to`, and `startTime` (also include the QuartzCore framework in your project)
- (void)animateFrom:(NSNumber *)aFrom toNumber:(NSNumber *)aTo {
self.from = aFrom; // or from = [aFrom retain] if your not using #properties
self.to = aTo; // ditto
self.text = [from stringValue];
CADisplayLink *link = [CADisplayLink displayLinkWithTarget:self selector:#selector(animateNumber:)];
startTime = CACurrentMediaTime();
[link addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
}
- (void)animateNumber:(CADisplayLink *)link {
static float DURATION = 1.0;
float dt = ([link timestamp] - startTime) / DURATION;
if (dt >= 1.0) {
self.text = [to stringValue];
[link removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
return;
}
float current = ([to floatValue] - [from floatValue]) * dt + [from floatValue];
self.text = [NSString stringWithFormat:#"%i", (long)current];
}
AUIAnimatedText has all you was asking for. That is replacement for UILabel with reach text animating possibilities ,

AVAudioPlayer fade volume out

I have an AVAudioPlayer playing some audio (duh!)
The audio is initiated when the user presses a button.
When they release it I want the audio to fade out.
I am using Interface builder...so I am trying to hook up a function on "touch up inside" that fades the audio out over 1 sec then stops.
Any ideas?
Thanks
Here's how I'm doing it:
-(void)doVolumeFade
{
if (self.player.volume > 0.1) {
self.player.volume = self.player.volume - 0.1;
[self performSelector:#selector(doVolumeFade) withObject:nil afterDelay:0.1];
} else {
// Stop and get the sound ready for playing again
[self.player stop];
self.player.currentTime = 0;
[self.player prepareToPlay];
self.player.volume = 1.0;
}
}
.
11 years later: do note that setVolume#fadeDuration now exists!
Swift has an AVAudioPlayer method you can use for fading out which was included as of iOS 10.0:
var audioPlayer = AVAudioPlayer()
...
audioPlayer.setVolume(0, fadeDuration: 3)
I tackled this problem using an NSOperation subclass so fading the volume doesn't block the main thread. It also allows fades to be queued and and forgotten about. This is especially useful for playing one shot sounds with fade-in and fade-out effects as they are dealloced after the last fade is completed.
// Example of MXAudioPlayerFadeOperation in NSOperationQueue
NSOperationQueue *audioFaderQueue = [[NSOperationQueue alloc] init];
[audioFaderQueue setMaxConcurrentOperationCount:1]; // Execute fades serially.
NSString *filePath = [[NSBundle mainBundle] pathForResource:#"bg" ofType:#"mp3"]; // path to bg.mp3
AVAudioPlayer *player = [[[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:filePath] error:NULL] autorelease];
[player setNumberOfLoops:-1];
[player setVolume:0.0];
// Note that delay is delay after last fade due to the Operation Queue working serially.
MXAudioPlayerFadeOperation *fadeIn = [[MXAudioPlayerFadeOperation alloc] initFadeWithAudioPlayer:player toVolume:1.0 overDuration:3.0];
[fadeIn setDelay:2.0];
MXAudioPlayerFadeOperation *fadeDown = [[MXAudioPlayerFadeOperation alloc] initFadeWithAudioPlayer:player toVolume:0.1 overDuration:3.0];
[fadeDown setDelay:0.0];
MXAudioPlayerFadeOperation *fadeUp = [[MXAudioPlayerFadeOperation alloc] initFadeWithAudioPlayer:player toVolume:1.0 overDuration:4.0];
[fadeUp setDelay:0.0];
MXAudioPlayerFadeOperation *fadeOut = [[MXAudioPlayerFadeOperation alloc] initFadeWithAudioPlayer:player toVolume:0.0 overDuration:3.0];
[fadeOut setDelay:2.0];
[audioFaderQueue addOperation:fadeIn]; // 2.0s - 5.0s
[audioFaderQueue addOperation:fadeDown]; // 5.0s - 8.0s
[audioFaderQueue addOperation:fadeUp]; // 8.0s - 12.0s
[audioFaderQueue addOperation:fadeOut]; // 14.0s - 17.0s
[fadeIn release];
[fadeDown release];
[fadeUp release];
[fadeOut release];
For MXAudioPlayerFadeOperation class code see this post.
I ended up combining some of the answer together and converted it to Swift ending up in this method:
func fadeVolumeAndPause(){
if self.player?.volume > 0.1 {
self.player?.volume = self.player!.volume - 0.1
var dispatchTime: dispatch_time_t = dispatch_time(DISPATCH_TIME_NOW, Int64(0.1 * Double(NSEC_PER_SEC)))
dispatch_after(dispatchTime, dispatch_get_main_queue(), {
self.fadeVolumeAndPause()
})
} else {
self.player?.pause()
self.player?.volume = 1.0
}
}
These are all good answers, however they don't deal with specifying the rate of fade (or applying a logarithmic curve to the fade, which is sometimes desirable), or specifying the number of dB's reduction from unity that you are fading to.
this is an except from one of my apps, with a few "bells and whistles" removed, that are not relevant to this question.
enjoy!
#define linearToDecibels(linear) (MIN(10,MAX(-100,20.0 * log10(linear))))
#define decibelsToLinear(decibels) (pow (10, (0.05 * decibels)))
#define fadeInfoId(n) [fadeInfo objectForKey:##n]
#define fadeInfoObject(NSObject,n) ((NSObject*) fadeInfoId(n))
#define fadeInfoFloat(n) [fadeInfoId(n) floatValue]
#define useFadeInfoObject(n) * n = fadeInfoId(n)
#define useFadeInfoFloat(n) n = fadeInfoFloat(n)
#define setFadeInfoId(n,x) [fadeInfo setObject:x forKey:##n]
#define setFadeInfoFloat(n,x) setFadeInfoId(n,[NSNumber numberWithFloat:x])
#define setFadeInfoFlag(n) setFadeInfoId(n,[NSNumber numberWithBool:YES])
#define saveFadeInfoId(n) setFadeInfoId(n,n)
#define saveFadeInfoFloat(n) setFadeInfoFloat(n,n)
#define fadeAVAudioPlayer_default nil
#define fadeAVAudioPlayer_linearFade #"linearFade"
#define fadeAVAudioPlayer_fadeToStop #"fadeToStop"
#define fadeAVAudioPlayer_linearFadeToStop #"linearFadeToStop"
-(void) fadeAVAudioPlayerTimerEvent:(NSTimer *) timer {
NSMutableDictionary *fadeInfo = timer.userInfo;
NSTimeInterval elapsed = 0 - [fadeInfoObject(NSDate,startTime) timeIntervalSinceNow];
NSTimeInterval useFadeInfoFloat(fadeTime);
float useFadeInfoFloat(fadeToLevel);
AVAudioPlayer useFadeInfoObject(player);
double linear;
if (elapsed>fadeTime) {
if (fadeInfoId(stopPlaybackAtFadeTime)) {
[player stop];
linear = fadeInfoFloat(fadeFromLevel);
} else {
linear = fadeToLevel;
}
[timer invalidate];
[fadeInfo release];
} else {
if (fadeInfoId(linearCurve)) {
float useFadeInfoFloat(fadeFromLevel);
float fadeDelta = fadeToLevel-fadeFromLevel;
linear = fadeFromLevel + (fadeDelta * (elapsed/fadeTime));
} else {
float useFadeInfoFloat(fadeToDB);
float useFadeInfoFloat(fadeFromDB);
float fadeDelta = fadeToDB-fadeFromDB;
float decibels = fadeFromDB + (fadeDelta * (elapsed/fadeTime));
linear = decibelsToLinear(decibels);
}
}
[player setVolume: linear];
//[self displayFaderLevelForMedia:player];
//[self updateMediaVolumeLabel:player];
}
-(void) fadeAVAudioPlayerLinear:(AVAudioPlayer *)player over:(NSTimeInterval) fadeTime fadeToLevel:(float) fadeToLevel fadeMode:(NSString*)fadeMode {
NSMutableDictionary *fadeInfo = [[NSMutableDictionary alloc ]init];
saveFadeInfoId(player);
float fadeFromLevel = player.volume;// to optimize macros put value in var, so we don't call method 3 times.
float fadeFromDB = linearToDecibels(fadeFromLevel);
float fadeToDB = linearToDecibels(fadeToLevel);
saveFadeInfoFloat(fadeFromLevel);
saveFadeInfoFloat(fadeToLevel);
saveFadeInfoFloat(fadeToDB);
saveFadeInfoFloat(fadeFromDB);
saveFadeInfoFloat(fadeTime);
setFadeInfoId(startTime,[NSDate date]);
if([fadeMode isEqualToString:fadeAVAudioPlayer_fadeToStop]||[fadeMode isEqualToString:fadeAVAudioPlayer_linearFadeToStop]){
setFadeInfoFlag(stopPlaybackAtFadeTime);
}
if([fadeMode isEqualToString:fadeAVAudioPlayer_linearFade]||[fadeMode isEqualToString:fadeAVAudioPlayer_linearFadeToStop]){
setFadeInfoFlag(linearCurve);
}
[NSTimer scheduledTimerWithTimeInterval:0.05 target:self selector:#selector(fadeAVAudioPlayerTimerEvent:) userInfo:fadeInfo repeats:YES];
}
-(void) fadeAVAudioPlayer:(AVAudioPlayer *)player over:(NSTimeInterval) fadeTime fadeToDB:(float) fadeToDB fadeMode:(NSString*)fadeMode {
[self fadeAVAudioPlayerLinear:player over:fadeTime fadeToLevel:decibelsToLinear(fadeToDB) fadeMode:fadeMode ];
}
-(void) fadeoutAVAudioPlayer:(AVAudioPlayer *)player {
[self fadeAVAudioPlayerLinear:player over:5.0 fadeToLevel:0 fadeMode:fadeAVAudioPlayer_default];
}
-(void) fadeinAVAudioPlayer:(AVAudioPlayer *)player {
[self fadeAVAudioPlayerLinear:player over:5.0 fadeToLevel:0 fadeMode:fadeAVAudioPlayer_default];
}
An extension for swift 3 inspired from the most voted answer. For those of you who like copy-pasting :)
extension AVAudioPlayer {
func fadeOut() {
if volume > 0.1 {
// Fade
volume -= 0.1
perform(#selector(fadeOut), with: nil, afterDelay: 0.1)
} else {
// Stop and get the sound ready for playing again
stop()
prepareToPlay()
volume = 1
}
}
}
I wrote a helper class in Swift for fading AvAudioPlayer in and out. You can use logarithmic volume function for more gradual fading effect.
let player = AVAudioPlayer(contentsOfURL: soundURL, error: nil)
let fader = iiFaderForAvAudioPlayer(player: player)
fader.fadeIn()
fader.fadeOut()
Here a demo app: https://github.com/evgenyneu/sound-fader-ios
Swift 3
I like Ambroise Collon answer's , so i voted up but Swift is statically typed so the performSelector: methods are to fall by the wayside, maybe an alternative could be dispatch async (in this version I've added also the destination volume as parameter)
func dispatchDelay(delay:Double, closure:#escaping ()->()) {
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + delay, execute: closure)
}
extension AVAudioPlayer {
func fadeOut(vol:Float) {
if volume > vol {
//print("vol is : \(vol) and volume is: \(volume)")
dispatchDelay(delay: 0.1, closure: {
[weak self] in
guard let strongSelf = self else { return }
strongSelf.volume -= 0.01
strongSelf.fadeOut(vol: vol)
})
} else {
volume = vol
}
}
func fadeIn(vol:Float) {
if volume < vol {
dispatchDelay(delay: 0.1, closure: {
[weak self] in
guard let strongSelf = self else { return }
strongSelf.volume += 0.01
strongSelf.fadeIn(vol: vol)
})
} else {
volume = vol
}
}
}
This seems to me to be a descent use of an NSOperationQueue.
Hence here is my solution:
-(void) fadeIn
{
if (self.currentPlayer.volume >= 1.0f) return;
else {
self.currentPlayer.volume+=0.10;
__weak typeof (self) weakSelf = self;
[NSThread sleepForTimeInterval:0.2f];
[self.fadingQueue addOperationWithBlock:^{
NSLog(#"fading in %.2f", self.currentPlayer.volume);
[weakSelf fadeIn];
}];
}
}
-(void) fadeOut
{
if (self.currentPlayer.volume <= 0.0f) return;
else {
self.currentPlayer.volume -=0.1;
__weak typeof (self) weakSelf = self;
[NSThread sleepForTimeInterval:0.2f];
[self.fadingQueue addOperationWithBlock:^{
NSLog(#"fading out %.2f", self.currentPlayer.volume);
[weakSelf fadeOut];
}];
}
}
How about this: (if time passed in is negative then fade out the sound, otherwise fade in)
- (void) fadeInOutVolumeOverTime: (NSNumber *)time
{
#define fade_out_steps 0.1
float theVolume = player.volume;
NSTimeInterval theTime = [time doubleValue];
int sign = (theTime >= 0) ? 1 : -1;
// before we call this, if we are fading out, we save the volume
// so that we can restore back to that level in the fade in
if ((sign == 1) &&
((theVolume >= savedVolume) ||
(theTime == 0))) {
player.volume = savedVolume;
}
else if ((sign == -1) && (theVolume <= 0)) {
NSLog(#"fading");
[player pause];
[self performSelector:#selector(fadeInOutVolumeOverTime:) withObject:[NSNumber numberWithDouble:0] afterDelay:1.0];
}
else {
theTime *= fade_out_steps;
player.volume = theVolume + fade_out_steps * sign;
[self performSelector:#selector(fadeInOutVolumeOverTime:) withObject:time afterDelay:fabs(theTime)];
}
}
In Objective-C try this:
NSURL *trackURL = [[NSURL alloc]initFileURLWithPath:#"path to audio track"];
// fade duration in seconds
NSTimeInterval fadeDuration = 0.3;
// duration of the audio track in seconds
NSTimeInterval audioTrackDuration = 5.0;
AVAudioPlayer *audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:trackURL error:nil];
[audioPlayer play];
// first we set the volume to 1 - highest
[audioPlayer setVolume:1.0];
// then to 0 - lowest
[musicAudioPlayer setVolume:0 fadeDuration:audioTrackDuration - fadeDuration];
Swift solution:
The top rated answer here is great but it gives a stuttering effect as the volume step of 0.1 is too much. Using 0.01 gives a smoother fade effect to hear.
Using this code you can specify how long you want the fade transition to last.
let fadeVolumeStep: Float = 0.01
let fadeTime = 0.5 // Fade time in seconds
var fadeVolumeStepTime: Double {
return fadeTime / Double(1.0 / fadeVolumeStep)
}
func fadeOut() {
guard let player = self.player else {
return
}
if !player.playing { return }
func fadeOutPlayer() {
if player.volume > fadeVolumeStep {
player.volume -= fadeVolumeStep
delay(time: fadeVolumeStepTime, closure: {
fadeOutPlayer()
})
} else {
player.stop()
player.currentTime = 0
player.prepareToPlay()
}
}
fadeOutPlayer()
}
func fadeIn() {
guard let player = self.player else {
return
}
if player.playing { return }
player.volume = 0
player.play()
func fadeInPlayer() {
if player.volume <= 1 - fadeVolumeStep {
player.volume += fadeVolumeStep
delay(time: fadeVolumeStepTime, closure: {
fadeInPlayer()
})
} else {
player.volume = 1
}
}
fadeInPlayer()
}
func delay(time delay:Double, closure:()->()) {
dispatch_after(
dispatch_time(
DISPATCH_TIME_NOW,
Int64(delay * Double(NSEC_PER_SEC))
),
dispatch_get_main_queue(), closure)
}
you can adjust the time using fadeTime constant.