12

我制作了一个 iPhone 应用程序,可以在锻炼时使用。它会发出铃声,表示您(用户)应该从锻炼程序的一个步骤切换到下一步。我设计了该应用程序,以便您在使用该应用程序时可以在 iPod 上听音乐,并且我希望在音乐上播放的音调足够清晰。我已经得到了这个工作......有点......

当音乐响亮时,很难听到音调。我的理想解决方案类似于 iPod 应用程序处理传入的文本消息或电子邮件的方式。音乐音量降低,声音播放,然后音乐音量逐渐减弱。

以下是我迄今为止尝试过的方法:

  1. 我以前AudioServicesPlaySystemSound玩的声音。

    我像这样初始化声音:

    CFBundleRef mainBundle = CFBundleGetMainBundle();
    soundFileURLRef = CFBundleCopyResourceURL(mainBundle, CFSTR ("bell"), CFSTR ("wav"), NULL);
    AudioServicesCreateSystemSoundID (soundFileURLRef, &soundFileID);
    

    我在适当的时间使用以下方法播放声音:

    AudioServicesPlaySystemSound (self.soundFileID);
    

    这可以很好地播放声音,但是在嘈杂的音乐中很难听到。继续尝试 2...

  2. 我尝试降低 iPod 的音量,播放声音,然后将音量恢复到之前的水平。

    如果当前步骤还剩 1 秒,请开始降低音量:

    if ([self.intervalSet currentStepTimeRemainingInSeconds] == 1) {
        self.volumeIncrement = originalVolume/5.0f;
        [NSTimer scheduledTimerWithTimeInterval:0.5f 
                                         target:self 
                                       selector:@selector(fadeVolumeOut:) 
                                       userInfo:[NSNumber numberWithInt:1]
                                        repeats:NO];
    }
    

    这是fadeVolumeOut:方法:

    - (void) fadeVolumeOut:(NSTimer *)timer {
        if (!TARGET_IPHONE_SIMULATOR) {
            NSNumber *volumeStep = (NSNumber *)[timer userInfo];
            int vStep = [(NSNumber *)volumeStep intValue];
            float volume = [[MPMusicPlayerController iPodMusicPlayer] volume];
            volume = volume - self.volumeIncrement;
            if (volume < 0.0f) {
                volume = 0.0f;
            }
            [[MPMusicPlayerController iPodMusicPlayer] setVolume:volume];
            if (vStep < 5) {
                vStep = vStep + 1;
                [NSTimer scheduledTimerWithTimeInterval:0.1f 
                                                 target:self 
                                               selector:@selector(fadeVolumeOut:) 
                                               userInfo:[NSNumber numberWithInt:vStep]
                                                repeats:NO];
            }
        }
    }
    

    然后,当该步骤结束时,播放警报声并将音量淡入:

     [NSTimer scheduledTimerWithTimeInterval:0.1f 
                                      target:self 
                                    selector:@selector(alertAndFadeVolumeIn) 
                                    userInfo:nil
                                     repeats:NO];
    

    这是alertAndFadeVolumeIn方法:

     - (void) alertAndFadeVolumeIn {
         [self alert];
         [NSTimer scheduledTimerWithTimeInterval:0.25f 
                                          target:self 
                                        selector:@selector(fadeVolumeIn:) 
                                        userInfo:[NSNumber numberWithInt:1]
                                         repeats:NO];
     }
    

    并且fadeVolumeIn:基本上与fadeVolumeOut:上述相反。

    这有效,音量淡出,声音播放,然后音量淡入。问题是音量降低了与 iPod 相同的量,所以它不会让听音乐变得更容易.

  3. 我切换到AVAudioSession播放声音,并设置会话,以便在使用应用程序时继续播放 iPod 音乐。这是我初始化会话的方式:

     AVAudioSession *session = [AVAudioSession sharedInstance];
     [session setCategory:AVAudioSessionCategoryPlayback error:nil];
    
     OSStatus propertySetError = 0;
     UInt32 allowMixing = true;
     propertySetError = AudioSessionSetProperty (
         kAudioSessionProperty_OverrideCategoryMixWithOthers,
         sizeof (allowMixing),
         &allowMixing
     );
    
     NSError *activationError = nil;
     [session setActive:YES error:&activationError];
    
     NSString *audioFile = [[NSBundle mainBundle] pathForResource:@"bell"
                                                           ofType:@"wav"];
     player = [[AVAudioPlayer alloc] initWithContentsOfURL:
         [NSURL fileURLWithPath:audioFile] error:NULL];
    

    为了播放声音,我[self.player play]会在适当的时候打电话。再次,音量随着 iPod 音量的降低而降低,并且音调不再容易听到。

  4. 我试着[[MPMusicPlayerController applicationMusicPlayer] setVolume:1.0f];在警报声响起之前把它放好。这有好坏参半的结果。第一次声音像我希望的那样以最大音量播放,但随后的音量要低得多。此外,音乐不会顺利淡出。似乎iPodMusicPlayer并且applicationMusicPlayer正在共享相同的卷。这是使用的结果[AVAudioSession sharedInstance];吗?有没有另一种方法来初始化一个AVAudioSession

  5. 接下来,我尝试使用AVAudioSession“闪避”:

     OSStatus propertySetError = 0;
     UInt32 allowDucking = true;
     propertySetError = AudioSessionSetProperty (
         kAudioSessionProperty_OtherMixableAudioShouldDuck,
         sizeof (allowDucking),
         &allowDucking
     );
    

    不幸的是,当音频会话被激活时,iPod 音乐会“躲避”,这是在 viewController 以我的方式加载后立即出现的。

  6. 最后,我更改了我的代码,以便在步骤结束前一秒激活音频会话,在步骤结束时播放声音,一秒后停用会话。此时我已经删除了所有的淡入淡出代码。以下是相关的代码片段:

    if ([self.intervalSet currentStepTimeRemainingInSeconds] == 1) {
        AVAudioSession *session = [AVAudioSession sharedInstance];
        [session setActive:YES error:nil];
    }
    

    ...

    if (self.shouldRing) {
        [self.player play]; 
        [NSTimer scheduledTimerWithTimeInterval:1.0f 
                                         target:self 
                                       selector:@selector(stopSound)
                                       userInfo:nil
                                        repeats:NO];
    }
    

    ...

    - (void) stopSound {
        [self.player stop];
        AVAudioSession *session = [AVAudioSession sharedInstance];
        [session setActive:NO error:nil];
    }
    

    这将我带到了我真正陷入困境的地步。这在第一步结束后完美运行。iPod 音量降低,声音响亮,然后 iPod 音量在一秒钟后逐渐减弱。然而,第二次踏步结束时声音会稍微小一些,第三次几乎听不见,第四次无声。然后,第五次,它再次大声播放,循环重复。

    最重要的是,激活和停用似乎是相当繁重的操作,它们会导致计时器略微卡顿。

有没有人试图做类似的事情?有没有首选的方法来通过 iPod 音乐播放声音?

4

4 回答 4

11

AVAudioSession是处理“应用程序、应用程序间和设备级别的音频行为”的正确方法。我在 iOS 4 上使用了稍微修改的 #6,没有任何问题。背景音频淡出,我的声音播放,背景音频淡出。

  1. 初始化音频会话(删除错误处理代码):

    AVAudioSession* audioSession = [AVAudioSession sharedInstance];
    [audioSession setCategory:AVAudioSessionCategoryPlayback error:nil]
    [audioSession setActive:NO error:nil];
    
  2. 播放声音(AVAudioPlayer的 play/prepareToPlay 将为您激活音频会话):

    AVAudioPlayer* audioPlayer = [[[AVAudioPlayer alloc] initWithContentsOfURL:audioURL error:nil];
    [audioPlayer play];
    
  3. 停止声音:

    [audioPlayer stop];
    [[AVAudioSession sharedInstance] setActive:NO withFlags:AVAudioSessionSetActiveFlags_NotifyOthersOnDeactivation error:nil];
    

    标志kAudioSessionSetActiveFlag_NotifyOthersOnDeactivation(iOS 4 新增)告诉系统通知背景音频恢复播放。

于 2010-06-24T15:41:44.390 回答
11

做你最初说你想做的事情的正确方法是你的 (5) - 使用闪避。这是交易。使用允许其他应用程序播放声音的音频会话类型(播放)。在您要播放声音之前不要打开闪避。当您要播放声音时,请打开闪避!背景音乐会立即闪避,因此可以听到您的声音。然后,当您的声音播放完毕后,关闭闪避,现在(这是诀窍)停用您的音频会话并再次激活它,以使背景音乐立即恢复其以前的响度:

UInt32 duck = 0;
AudioSessionSetProperty(kAudioSessionProperty_OtherMixableAudioShouldDuck, sizeof(duck), &duck);
AudioSessionSetActive(false);
AudioSessionSetActive(true);

编辑:在 iOS 6 中,您可以(并且应该)仅使用 Objective-C API 来完成所有这些工作。因此,要开始回避其他音频:

[[AVAudioSession sharedInstance] 
    setCategory: AVAudioSessionCategoryAmbient
    withOptions: AVAudioSessionCategoryOptionDuckOthers
          error: nil];

播放完声音后,关闭闪避:

[[AVAudioSession sharedInstance] setActive:NO withOptions:0 error:nil];
[[AVAudioSession sharedInstance] setCategory: AVAudioSessionCategoryAmbient
                                 withOptions: 0
                                       error: nil];
[[AVAudioSession sharedInstance] setActive:YES withOptions: 0 error:nil];
于 2011-01-09T19:04:19.560 回答
4

为什么不回到#2?代替:

  1. 消退
  2. 播放提醒
  3. 淡入

采用:

  1. 消退
  2. 暂停音乐
  3. 调高音量
  4. 播放提醒
  5. 调低音量
  6. 播放音乐
  7. 淡入
于 2010-01-17T08:27:27.240 回答
0

适用于 Xcode 8、iOS 10

客观的:

像在导航应用程序(Waze/谷歌地图/RideAustin)中一样工作播放声音,降低背景音乐音量。播放声音后,恢复背景音乐音量

执行:

初始化:

{
    NSError *error = nil;
    [[AVAudioSession sharedInstance] 
        setCategory: AVAudioSessionCategoryPlayback
        withOptions: AVAudioSessionCategoryOptionDuckOthers | AVAudioSessionCategoryOptionMixWithOthers
              error: nil];
    [[AVAudioSession sharedInstance] setPreferredIOBufferDuration:0.005 error:&error];
    ...
}

播放声音时

{
    ...
    NSError *error = nil;
    AVAudioPlayer *player = [[AVAudioPlayer alloc] initWithContentsOfURL:fileURL error:&error];

    //set delegate
    player.delegate = self
    ...
    [player play]
}

确保将恢复其他音乐播放器应用程序的音量

#pragma mark - AVAudioPlayerDelegate
-(void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag {
    ...
    [[AVAudioSession sharedInstance] setActive:NO withOptions:AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation error:&error];
}
于 2017-04-13T08:43:32.693 回答