如何使用 cocoalibspotify 进行音频交叉淡入淡出?
Posted
技术标签:
【中文标题】如何使用 cocoalibspotify 进行音频交叉淡入淡出?【英文标题】:How do I audio crossfade using cocoalibspotify? 【发布时间】:2013-03-29 05:49:34 【问题描述】:我想在支持 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) 用户信息:无 重复:是];
为了在第一首曲目的数据加载到 SPCoreAudioController.m 后继续播放,我更改了目标缓冲区长度:
static NSTimeInterval const kTargetBufferLength = 20;
在 SPSession.m 中:end_of_track(sp_session *session)
我删除了
// sess.playing = NO;
我调用 preloadTrackForPlayback: 大约在曲目结束前 15 秒,然后在 10 秒前调用 crossfadeTrack:。
然后设置crossfadeCountdownTime = [你想要crossfade多少秒]*2;
我在 crosssfade 上淡入音量:
- (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 的及时帮助!
【问题讨论】:
我正在尝试在 CocoaLibSpotify 中实现交叉淡入淡出功能,目前卡在最后一步,淡化两个音频控制器的音量。您能否详细说明您是如何做到这一点的? 【参考方案1】:据我所知,没有使用 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
以拥有两个 Core Audio 控制器并交替使用哪一个作为音频交付委托,那么您应该是黄金。
【讨论】:
谢谢,iKendac。我在原始帖子中发布了您帮助我完成的工作。在淡入淡出之后暂停音频只是一个小问题。有什么想法吗? 在SPPlaybackManager
中,KVO 观察器中有一行代码在播放暂停时停止核心音频控制器的音频输出,否则它将继续耗尽其缓冲区。确保您也将该呼叫重定向到您的第二个核心音频控制器。寻找self.audioController.audioOutputEnabled = self.playbackSession.isPlaying;
你对我太好了!就是这样。太感谢了。我会尽快更新原始帖子中的代码以表明您的手艺。
这个答案在 2016 年仍然有用吗?以上是关于如何使用 cocoalibspotify 进行音频交叉淡入淡出?的主要内容,如果未能解决你的问题,请参考以下文章
如何在 CocoaLibSpotify 注销时清除用户名和密码?