AVAudioPlayer淡出音量

问题描述 投票:47回答:13

我有一个AVAudioPlayer播放一些音频(呃!)

当用户按下按钮时启动音频。当他们发布它时,我希望音频淡出。

我正在使用界面构建器...所以我试图在“内部触摸”上连接一个功能,使音频在1秒内消失,然后停止。

有任何想法吗?

谢谢

iphone avaudioplayer volume
13个回答
120
投票

我是这样做的:

-(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;
    }
}

1
投票

怎么样:(如果时间过去是负面然后淡出声音,否则淡入)

- (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)];
    }
}

1
投票

Swift解决方案:

这里评价最高的答案很棒,但是由于0.1的体积步长太大,它会产生口吃效果。使用0.01可以获得更平滑的淡入淡出效果。

使用此代码,您可以指定希望淡入淡出过渡的持续时间。

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)
}

你可以使用fadeTime常数来调整时间。


1
投票

在Objective-C中试试这个:

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];

0
投票

A very good solution可以从objc.io的Nick Lockwood获得这个问题。它利用Core Animation来定时更改音量,如果需要将UI动画与音量变化(包括交互式动画)同步,则可以派上用场。


23
投票

Swift有一个AVAudioPlayer方法,你可以用它来淡出,包括iOS 10.0

var audioPlayer = AVAudioPlayer()
...
audioPlayer.setVolume(0, fadeDuration: 3)

15
投票

我使用NSOperation子类解决了这个问题,因此淡化卷不会阻塞主线程。它还允许淡入淡出排队和忘记。这对于播放具有淡入和淡出效果的一次性声音特别有用,因为它们在最后一次淡入淡出完成后被释放。

// 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];

对于MXAudioPlayerFadeOperation类代码,请参阅this post


10
投票

我最终将一些答案组合在一起并将其转换为Swift,最终以此方法结束:

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

8
投票

这些都是很好的答案,但是它们不涉及指定衰落速率(或者将对数曲线应用于衰落,这有时是期望的),或者指定从衰落的单位减少dB的数量。

这是一个除了我的一个应用程序,删除了一些“花里胡哨”,这与这个问题无关。

请享用!

#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];
}

4
投票

我在Swift写了一个帮助类,用于进出AvAudioPlayer。您可以使用对数体积函数来实现更渐进的淡入淡出效果。

let player = AVAudioPlayer(contentsOfURL: soundURL, error: nil)

let fader = iiFaderForAvAudioPlayer(player: player)
fader.fadeIn()
fader.fadeOut()

这是一个演示应用程序:https://github.com/evgenyneu/sound-fader-ios


4
投票

swift 3的扩展灵感来自最投票的答案。对于那些喜欢复制粘贴的人:)

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

4
投票

斯威夫特3

我喜欢Ambroise Collon的答案,所以我投票了,但是Swift是静态类型的,所以performSelector:方法要落在路边,也许另外一种可能是调度异步(在这个版本中我还添加了目标卷作为参数)

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

2
投票

在我看来,这是对NSOperationQueue的下降使用。

因此,这是我的解决方案:

-(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];
        }];
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.