我想在Spotify启用的应用程序中从一个轨道交叉淡出到下一个轨道。两个轨道都是Spotify轨道,并且由于一次只有一个数据流可以来自Spotify,我怀疑我需要缓冲(我认为我可以提前读取1.5倍播放速度)第一个轨道的最后几秒钟,开始流为轨道二,淡出一个和淡出两个使用AudioUnit。
我已经审查了示例应用程序:万岁- https://github.com/iKenndac/Viva SimplePlayer与EQ - https://github.com/iKenndac/SimplePlayer-with-EQ并试图让我的头脑围绕SPCircularBuffer,但我仍然需要帮助。谁能给我举个例子,或者帮我说明一下轨迹交叉游戏计划?
更新:感谢iKenndac,我大约有95%的人在那里。我将把我到目前为止的内容贴出来:
在SPPlaybackManager。m: initWithPlaybackSession:(SPSession *) session {
补充道:
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);
}
}];
}
为crossfade启动一个计时器
crossfadeTimer = [NSTimer scheduledTimerWithTimeInterval: 0.5 .目标:自我选择器:@selector (crossfadecoundown)用户信息:零重复:是的,
为了在SPCoreAudioController中加载数据后保持第一轨播放。我更改了目标缓冲区长度:
static NSTimeInterval const kTargetBufferLength = 20;
和在SPSession。M: end_of_track(sp_session *session) {
我删除
// sess.playing = NO;
我在轨道结束前大约15秒调用preloadTrackForPlayback,然后在轨道结束前10秒调用crossfadeTrack。
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
我使用:
- (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];
}
}
我会继续努力,如果我能做得更好,我会更新。再次感谢iKenndac的准确帮助!
据我所知,没有一个预先编写的crossfade示例使用了CocoaLibSpotify。然而,一个(也许不是理想的)游戏计划是:
-
创建两个独立的音频队列。
SPCoreAudioController
是一个音频队列的封装,所以你应该只能够实例化其中的两个。 -
对一个队列正常播放音乐。当你接近曲目的结尾时,调用
SPSession
的preloadTrackForPlayback: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
有两个核心音频控制器,并选择其中一个是音频传递委托,你应该是黄金。