7

我想在支持 Spotify 的应用程序中从一首曲目交叉淡入到下一首曲目。两个曲目都是 Spotify 曲目,由于一次只能来自 Spotify 的一个数据流,我怀疑我需要缓冲(我想我可以提前 1.5 倍播放速度)第一首曲目的最后几秒钟,开始流对于轨道二,使用 AudioUnit 淡出一并淡入二。

我已经查看了示例应用程序:Viva - https://github.com/iKenndac/Viva SimplePlayer with EQ - https://github.com/iKenndac/SimplePlayer-with-EQ并试图让我的想法围绕 SPCircularBuffer,但是我仍然需要帮助。有人可以给我指出另一个例子或帮助指出一个轨道交叉淡入淡出的游戏计划吗?

更新:感谢 iKenndac,我大约 95% 在那里。我将发布到目前为止的内容:

在 SPPlaybackManager.m 中:initWithPlaybackSession:(SPSession *)aSession {

添加:

self.audioController2 = [[SPCoreAudioController alloc] init];
self.audioController2.delegate = self;

并且在

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {

...


self.audioController.audioOutputEnabled = self.playbackSession.isPlaying;

// for crossfade, add

self.audioController2.audioOutputEnabled = self.playbackSession.isPlaying;

并添加了基于 playTrack 的新方法

-(void)crossfadeTrack:(SPTrack *)aTrack callback:(SPErrorableOperationCallback)block {
// switch audiocontroller from current to other
if (self.playbackSession.audioDeliveryDelegate == self.audioController)
{
   self.playbackSession.audioDeliveryDelegate = self.audioController2;
   self.audioController2.delegate = self;
   self.audioController.delegate = nil;
}
else
{

   self.playbackSession.audioDeliveryDelegate = self.audioController;
   self.audioController.delegate = self;
   self.audioController2.delegate = nil;
}

if (aTrack.availability != SP_TRACK_AVAILABILITY_AVAILABLE) {
    if (block) block([NSError spotifyErrorWithCode:SP_ERROR_TRACK_NOT_PLAYABLE]);
    self.currentTrack = nil;
}

self.currentTrack = aTrack;
self.trackPosition = 0.0;

[self.playbackSession playTrack:self.currentTrack callback:^(NSError *error) {

    if (!error)
        self.playbackSession.playing = YES;
    else
        self.currentTrack = nil;

    if (block) {
        block(error);
    }
}];
}

这将启动交叉淡入淡出的计时器

crossfadeTimer = [NSTimer scheduledTimerWithTimeInterval: 0.5 目标:自我选择器:@selector (crossfadeCountdown) userInfo: nil repeats: YES];

为了在数据加载到 SPCoreAudioController.m 后继续播放第一首曲目,我更改了目标缓冲区长度:

static NSTimeInterval const kTargetBufferLength = 20;

在 SPSession.m 中: end_of_track(sp_session *session) {

我删除了

// sess.playing = NO;

我在曲目结束前约 15 秒调用 preloadTrackForPlayback:,然后在 10 秒前调用 crossfadeTrack:。

然后设置 crossfadeCountdownTime = [你想要交叉淡入淡出的秒数]*2;

我在交叉淡入淡出上淡化音量:

  - (void) crossfadeCountdown
  {

      [UIAppDelegate.playbackSPManager setVolume:(1- (((float)crossfadeCountdownTime/     (thisCrossfadeSeconds*2.0)) *0.2) )];
    crossfadeCountdownTime -= 0.5;


   if (crossfadeCountdownTime == 1.0)
   {
    NSLog(@"Crossfade countdown done");
    crossfadeCountdownTime = 0;
    [crossfadeTimer invalidate];
    crossfadeTimer = nil;
    [UIAppDelegate.playbackSPManager setVolume:1.0];

   }
 }

我会继续努力,如果我能做得更好,我会更新。再次感谢 iKendac 的及时帮助!

4

1 回答 1

4

我知道没有使用 CocoaLibSpotify 的预先编写的淡入淡出示例。然而,一个(也许不是理想的)游戏计划是:

  • 制作两个单独的音频队列。SPCoreAudioController是音频队列的封装,因此您应该能够实例化其中的两个。

  • 正常播放音乐到一个队列。当你接近轨道的尽头时,调用SPSession'spreloadTrackForPlayback:callback:方法和下一个轨道让它准备好。

  • 当播放曲目的所有音频数据都已交付时,SPSession将触发音频委托方法sessionDidEndPlayback:。这意味着所有音频数据都已交付。但是,由于 CocoaLibSpotify 会缓冲来自 libspotify 的音频,因此在音频停止之前还有一段时间。

  • 此时,开始播放新曲目,但将音频数据转移到第二个音频队列。开始降低第一个队列的音量,同时提高下一个队列的音量。这应该会产生令人愉悦的淡入淡出。

几点建议:

  • SPCoreAudioController.m中,您将找到以下行,它定义了 CocoaLibSpotify 缓冲的音频量(以秒为单位)。如果您想要更大的淡入淡出,则需要增加它。

    static NSTimeInterval const kTargetBufferLength = 0.5;
    
  • 由于您以最大 1.5 倍实际播放速度获得音频数据,因此请注意不要在用户刚刚跳过接近曲目末尾时执行 5 秒交叉淡入淡出。您可能没有足够的音频数据来完成它。

  • 好好看看SPPlaybackManager.m。这个类是 CocoaLibSpotify 和 Core Audio 之间的接口。它并不太复杂,理解它会让你走得很远。SPCoreAudioController并且SPCircularBuffer几乎是将音频导入Core Audio的实现细节,您无需了解它们的实现即可实现您想要的。

  • 另外,请确保您了解各种代表SPSession的情况。音频传递委托只有一项工作——接收音频数据。播放委托获取所有其他播放事件 - 当音频完成传递给音频传递委托等时。没有什么可以阻止一个类同时是两者,但在当前实现中,SPPlaybackManager是播放委托,它创建一个实例SPCoreAudioController作为音频交付代表。如果您修改SPPlaybackManager为拥有两个 Core Audio 控制器并交替使用哪一个作为音频交付委托,那么您应该是黄金。

于 2013-04-07T12:10:54.523 回答