合唱
本文介绍如何实现合唱。在此之前,请确保你已参考实现文档集成 KTV API。
方案介绍
合唱场景下存在三种角色:
- 主唱:在没有伴唱加入、伴唱中途退出后,演唱的人的用户角色为独唱者。独唱者开始唱歌后,如有人加入合唱,独唱则成为合唱中的主唱。主唱者需要加入主频道,加载并播放歌曲,发布麦克风采集的音频流。此外,主唱还需要加入合唱子频道将人声同步给伴唱。KTV API 模块内部控制音乐播放器播放音乐,同步主唱的音乐播放进度,让歌词组件进入歌词滚动状态等逻辑。
- 伴唱:独唱者开始唱歌后,如有人加入合唱,则其成为合唱中的伴唱。伴唱需要加入合唱子频道,加载并播放歌曲,发布麦克风采集的音频流。KTV API 模块内部控制音乐播放器播放音乐,同步主唱的音乐播放进度,让歌词组件进入歌词滚动状态等逻辑。
- 听众:加入主频道,加载歌词。KTV API 模块内部控制听众订阅主唱的人声和音乐的音频合流,订阅伴唱的人声,同步主唱的音乐播放进度,让歌词组件进入歌词滚动状态等逻辑。如果普通听众需要上麦聊天,可以更新媒体选项。
下图为合唱方案的技术原理图:

主唱实现
1. 加入频道
调用 joinChannelByToken
加入频道。为保证高音质,声网推荐你在加入频道前调用 setAudioProfile
将音频编码属性设为 AgoraAudioProfileMusicHighQuality
,调用 setAudioScenario
将音频场景设为 AgoraAudioScenarioGameStreaming
。
// 加入频道
[self.rtcKit joinChannelByToken:<YOUR TOKEN>
channelId:<YOUR CHANNEL NAME>
info:nil
uid:<YOUR UID>
// 媒体选项,详见第 4 步操作
mediaOptions:mediaOption
joinSuccess:nil];
为同步频道内的歌词进度等状态信息,你需要在成功加入频道后调用 renewInnerDataStreamId
来创建数据流通道。如果退出频道后再次加入频道,你需要在加入频道成功后重新调用该方法来创建数据流通道。一个频道中最多只能创建 5 个数据流通道。
2. 加载及播放歌曲
调用 loadMusic
加载歌曲。在调用该方法后需调用 switchSingerRole
将用户角色设为独唱(SoloSinger
),然后调用 startSing
才可以播放歌曲。
如需加载声网提供的版权音乐,请参考获取版权音乐。
KTVSongConfiguration *songConfig = [[KTVSongConfiguration alloc] init];
// 同时加载歌曲和歌词
songCode.songIdentifier = songIdentifier;
songConfig.mode = KTVLoadMusicModeLoadMusicAndLrc;
songConfig.mainSingerUid = mainSingerUid;
// 歌曲加载失败的原因
- (void)onMusicLoadFailWithSongCode:(NSInteger)songCode reason:(enum KTVLoadSongFailReason)reason {
}
// 歌曲加载成功的回调,在此回调内调用 switchSingerRole 切换角色,调用 startSing 开始播放歌曲
- (void)onMusicLoadSuccessWithSongCode:(NSInteger)songCode lyricUrl:(NSString *_Nonnull)lyricUrl {
// 切换角色
[weakSelf.ktvApi
switchSingerRoleWithNewRole:KTVSingRoleSoloSinger
onSwitchRoleState:^(KTVSwitchRoleState state, KTVSwitchRoleFailReason reason){
// 切换角色状态回调
}];
// 开始播放歌曲
[weakSelf.ktvApi startSingWithSongCode:songCode startPos:0];
}
[self.ktvApi loadMusicWithSongCode:songCode config:songConfig onMusicLoadStateListener:self];
// 歌曲加载进度回调
- (void)onMusicLoadProgressWithSongCode:(NSInteger)songCode
percent:(NSInteger)percent
status:(AgoraMusicContentCenterPreloadStatus)status
msg:(NSString *)msg
lyricUrl:(NSString *)lyricUrl {
}
3. 切换角色
有人加入合唱成为伴唱时,独唱者需将其用户角色切换为主唱;最后一个伴唱者退出合唱时,主唱需将其用户角色切回独唱者;歌曲结束后,需将用户角色切为听众。
// 听众加入合唱成为伴唱,独唱者切换角色为主唱
[self.ktvApi switchSingerRoleWithNewRole:KTVSingRoleLeadSinger
onSwitchRoleState:^(KTVSwitchRoleState state, KTVSwitchRoleFailReason reason) {
}];
// 最后一个伴唱退出合唱,主唱切换角色为独唱
[self.ktvApi switchSingerRoleWithNewRole:KTVSingRoleSoloSinger
onSwitchRoleState:^(KTVSwitchRoleState state, KTVSwitchRoleFailReason reason) {
}];
// 歌曲结束后,将角色切换为听众
[self.ktvApi switchSingerRoleWithNewRole:KTVSingRoleAudience
onSwitchRoleState:^(KTVSwitchRoleState state, KTVSwitchRoleFailReason reason) {
}];
4. 根据角色更新媒体选项
通过 updateChannelWithMediaOptions
方法在主播加入频道后更新频道媒体选项,例如是否开启本地音频采集,是否发布本地音频流等。
AgoraRtcChannelMediaOptions* options = [AgoraRtcChannelMediaOptions new];
// 发布本地麦克风流
options.publishMicrophoneTrack = YES;
// 启用音频采集和播放
options.enableAudioRecordingOrPlayout = YES;
// 设置角色为主播
options.clientRoleType = AgoraClientRoleBroadcaster;
// 更新媒体选项
[self.RTCKit updateChannelWithMediaOptions:options];
伴唱实现
1. 加入频道
调用 joinChannelByToken
让伴唱加入频道。
// 加入频道
[self.rtcKit joinChannelByToken:<YOUR TOKEN>
channelId:<YOUR CHANNEL NAME>
info:nil
uid:<YOUR UID>
// 媒体选项,详见第 4 步操作
mediaOptions:mediaOption
joinSuccess:nil];
2. 加载歌曲
听众加入合唱成为伴唱前,需调用 loadMusic
加载歌曲。歌曲加载完成后,调用 switchSingerRole
将用户角色设为伴唱(CoSinger
)。
KTVSongConfiguration* songConfig = [[KTVSongConfiguration alloc] init];
// 仅加载歌曲
songCode.songIdentifier = songIdentifier;
songConfig.mode = KTVLoadMusicModeLoadMusicOnly;
songConfig.mainSingerUid = mainSingerUid;
[self.ktvApi loadMusicWithSongCode:songCode config:songConfig onMusicLoadStateListener:self];
// 歌曲加载失败
- (void)onMusicLoadFailWithSongCode:(NSInteger)songCode reason:(enum KTVLoadSongFailReason)reason {
}
// 歌曲加载成功
- (void)onMusicLoadSuccessWithSongCode:(NSInteger)songCode lyricUrl:(NSString* _Nonnull)lyricUrl {
// 切换角色为伴唱
[weakSelf.ktvApi
switchSingerRoleWithNewRole:KTVSingRoleCoSinger
onSwitchRoleState:^(KTVSwitchRoleState state, KTVSwitchRoleFailReason reason){
}];
}
3. 切换角色
当歌曲结束或伴唱想要中途退出合唱时,调用 switchSingerRole
将用户角色切换为听众。
[self.ktvApi switchSingerRoleWithNewRole:KTVSingRoleAudience
onSwitchRoleState:^(KTVSwitchRoleState state, KTVSwitchRoleFailReason reason) {
}];
4. 根据角色更新媒体选项
通过 updateChannelWithMediaOptions
方法在伴唱加入频道后更新频道媒体选项,例如是否开启本地音频采集,是否发布本地音频流等。
AgoraRtcChannelMediaOptions* options = [AgoraRtcChannelMediaOptions new];
// 发布本地麦克风流
options.publishMicrophoneTrack = YES;
// 启用音频采集和播放
options.enableAudioRecordingOrPlayout = YES;
// 设置角色为主播
options.clientRoleType = AgoraClientRoleBroadcaster;
// 更新媒体选项
[self.RTCKit updateChannelWithMediaOptions:options];
听众实现
1. 加入频道
调用 joinChannelByToken
让听众加入频道。
// 加入频道
[self.rtcKit joinChannelByToken:<YOUR TOKEN>
channelId:<YOUR CHANNEL NAME>
info:nil
uid:<YOUR UID>
// 媒体选项,详见步骤 3
mediaOptions:mediaOption
joinSuccess:nil];
2. 加载歌词
调用 loadMusic
加载歌词。听众加入频道后,默认订阅主唱人声和音乐混合的音频流,默认订阅伴唱人声,因此只需要加载歌词。
KTVSongConfiguration* songConfig = [[KTVSongConfiguration alloc] init];
// 仅加载歌词
songCode.songIdentifier = songIdentifier;
songConfig.mode = KTVLoadMusicModeLoadLrcOnly;
songConfig.mainSingerUid = mainSingerUid;
[self.ktvApi loadMusicWithSongCode:songCode config:songConfig onMusicLoadStateListener:self];
// 加载失败
- (void)onMusicLoadFailWithSongCode:(NSInteger)songCode reason:(enum KTVLoadSongFailReason)reason {
}
// 加载成功
- (void)onMusicLoadSuccessWithSongCode:(NSInteger)songCode lyricUrl:(NSString * _Nonnull)lyricUrl {
}
// 报告加载进度
- (void)onMusicLoadProgressWithSongCode:(NSInteger)songCode
percent:(NSInteger)percent
status:(AgoraMusicContentCenterPreloadStatus)status
msg:(NSString *)msg
lyricUrl:(NSString *)lyricUrl {
}
3. 根据角色更新媒体选项
通过 updateChannelWithMediaOptions
方法在听众加入频道后更新频道媒体选项,例如是否开启本地音频采集,是否发布本地音频流等。
听众的用户角色为 AgoraClientRoleAudience
,因此无法在频道内发布音频流。如果听众想上麦与主唱/伴唱语聊,需要将用户角色修改为 AgoraClientRoleBroadcaster
。修改角色后,SDK 默认发布该连麦听众的音频流,主唱、伴唱、其他听众都能听到连麦听众的声音。
// 对需要上麦聊天的听众更新媒体选项
AgoraRtcChannelMediaOptions* options = [AgoraRtcChannelMediaOptions new];
// 发布本地麦克风流
options.publishMicrophoneTrack = YES;
// 启用音频采集和播放
options.enableAudioRecordingOrPlayout = YES;
// 设置角色为主播
options.clientRoleType = AgoraClientRoleBroadcaster;
// 更新媒体选项
[self.RTCKit updateChannelWithMediaOptions:options];
// 对未上麦的听众更新媒体选项
AgoraRtcChannelMediaOptions* options = [AgoraRtcChannelMediaOptions new];
// 不发布本地麦克风流
options.publishMicrophoneTrack = NO;
// 启用音频采集和播放
options.enableAudioRecordingOrPlayout = YES;
// 设置角色为观众
options.clientRoleType = AgoraClientRoleAudience;
// 更新媒体选项
[self.RTCKit updateChannelWithMediaOptions:options];
下图展示合唱的 API 调用时序图:
API 参考
本文集成步骤中使用如下 API: