本文介绍如何通过声网音频 SDK v4.x 在你的 Android 项目里实现在线 K 歌房的主要功能。
在线 K 歌房内用户角色说明如下:
角色 | 描述 |
---|---|
房主 | 歌房创建者。可以收、发音频流。 |
连麦主播 | 进入歌房后,通过上麦成为连麦主播,可以收、发音频流。 |
主唱 | 连麦主播点歌后进行排麦演唱,正在演唱者成为主唱,可以收、发音频流。 |
合唱者 | 主唱发起合唱后,连麦主播接受邀请,成为合唱者,可以收、发音频流。 |
听众 | 进入歌房的倾听者,只能接收音频流。 |
实现在线 K 歌房场景中不同角色流程如下:
joinChannel
方法加入歌房。create [2/2]
方法创建并初始化 RtcEngine
实例,并将实例的音频场景设置为合唱(CHORUS
)。setClientRole
方法将用户角色设置为主播( BROADCASTER
)。参考下图实现在线 K 歌房场景的主唱端流程:
joinChannel
方法加入歌房。create [2/2]
方法创建并初始化 RtcEngine
实例,并将实例的音频场景设置为合唱(CHORUS
)。setClientRole
方法将用户角色设置为主播( BROADCASTER
)。上麦后成为连麦主播,此时可以与房间内其他连麦主播进行语音互动。同时房间内所有用户收到主播端的上麦信息并更新房间内麦位信息。IMediaPlayer
类的方法播放歌曲,并伴随歌曲唱歌。如需切换原唱或伴奏模式,可以调用 setAudioDualMonoMode
方法。同时,主唱调用 joinChannelEx
方法,实现合唱。sendStreamMessage
方法获将当前歌曲播放进度同步到房间内所有用户。合唱者和听众可以结合歌词组件实现歌词同步显示,并自定义歌词界面,详见歌词组件教程。leaveChannel
方法离开歌房。joinChannel
方法加入歌房。create [2/2]
方法创建并初始化 RtcEngine
实例,并将实例的音频场景设置为合唱(CHORUS
)。setClientRole
方法将用户角色设置为主播( BROADCASTER
)。上麦后成为连麦主播,此时可以与房间内其他连麦主播进行语音互动。同时房间内所有用户收到主播端的上麦信息并更新房间内麦位信息。IMediaPlayer
类的方法播放本地歌曲,并伴随歌曲与主唱一起唱歌,并调用 muteRemoteAudioStream
方法静音主唱的歌曲。onStreamMessage
回调接收主唱发送的数据流,实现歌曲同步。同时结合歌词组件实现歌词同步显示,并自定义歌词界面,详见歌词组件教程。leaveChannel
方法离开歌房。用户选择歌房,调用 joinChannel
方法加入歌房。
create [2/2]
方法创建并初始化 RtcEngine
实例,并将实例的音频场景设置为合唱(CHORUS
)。setClientRole
方法将用户角色设置为听众( AUDIENCE
)。主播上麦成为主唱后,听众通过第三方云服务同步房间内麦位信息。
主唱发起指令设置当前歌曲 ID,听众接收主唱指令并更新当前歌曲 ID。主唱唱歌时,听众接收主唱的音频流。
听众如需上麦,需要再次调用 setClientRole
方法将用户角色设为 BROADCASTER
,才可以在房间内发布音频流。
听众通过第三方云服务发起获取当前歌词指令,并下载歌词。同时结合 onStreamMessage
回调和歌词组件,实时同步歌词进度。
听众调用 leaveChannel
方法离开歌房。
开始前,请确保你的开发环境满足以下条件:
根据下表提供的链接,下载对应平台的 SDK 并集成到你的项目中,并开通内容中心服务。
产品 | SDK 下载 | 集成文档 |
---|---|---|
声网音频 SDK v4.x | 声网音频 SDK v4.x | 实现音频通话 |
第三方云服务 | 第三方云服务 SDK | SDK 安装指南 你可以联系声网技术支持获取第三方云服务测试环境,或自行集成第三方云服务。 |
声网歌词组件 | 源码 | 歌词组件教程 |
参考如下 API 时序图实现在线 K 歌房场景,同时你可以提交工单获取示例项目。
创建和加入歌房
实时独唱
实时合唱
加入歌房前,调用以下方法进行初始设置:
API | 实现功能 |
---|---|
create [2/2] |
调用其他 API 之前,需要调用该方法创建并初始化 RtcEngine ,并将 RtcEngineConfig 中的 mAudioScenario 设置为 AUDIO_SCENARIO_CHORUS (7) 。 |
示例代码如下:
RtcEngineConfig config = new RtcEngineConfig();
config.mContext = mContext;
config.mAppId = appid;
config.mEventHandler = mIRtcEngineEventHandler;
config.mChannelProfile = Constants.CHANNEL_PROFILE_LIVE_BROADCASTING;
config.mAudioScenario = Constants.AUDIO_SCENARIO_CHORUS;
mRtcEngine = RtcEngine.create(config);
调用以下方法加入或离开歌房:
API | 实现功能 |
---|---|
joinChannel |
加入歌房。用户加入歌房后才能接收或发布音频流。 |
updateChannelMediaOptions |
加入歌房后更新媒体选项。 |
leaveChannel |
离开歌房。房主离开歌房后,房间对象自动销毁,其他成员会自动离开歌房。 |
joinChannelEx |
合唱场景下,主唱需要调用 joinChannel 发布麦克风采集音频流,调用 joinChannelEx 发布播放器音频流。两次调用必须设置不同的 uid 。 |
updateChannelMediaOptionsEx |
合唱场景下,主唱加入另一个频道后更新媒体选项。 |
leaveChannelEx |
合唱场景下,主唱停止合唱后离开频道。 |
独唱场景下,主唱发布播放器音频流和麦克风采集音频流,示例代码如下:
mediaPlayer = engine.createMediaPlayer();
mediaPlayer.registerPlayerObserver(this);
mediaPlayer.open(url, 0);
ChannelMediaOptions options = new ChannelMediaOptions();
options.autoSubscribeVideo = true;
options.autoSubscribeAudio = true;
options.publishScreenCaptureVideo = false;
options.publishCameraTrack = false;
// 发布麦克风采集音频流
options.publishMicrophoneTrack = true;
options.enableAudioRecordingOrPlayout = true;
options.publishMediaPlayerId = mediaPlayer.getMediaPlayerId();
// 发布播放器音频流
options.publishMediaPlayerAudioTrack = true;
engine.updateChannelMediaOptions(options);
合唱场景下,主唱调用 joinChannelEx
发布另一路音频流,示例代码如下:
mediaPlayer = engine.createMediaPlayer();
mediaPlayer.registerPlayerObserver(this);
mediaPlayer.open(url, 0);
ChannelMediaOptions mediaOptions = new ChannelMediaOptions();
mediaOptions.autoSubscribeAudio = false;
mediaOptions.autoSubscribeVideo = false;
mediaOptions.publishMicrophoneTrack = false;
mediaOptions.publishCameraTrack = false;
mediaOptions.publishMediaPlayerId = mediaPlayer.getMediaPlayerId();
// 发布播放器音频流
mediaOptions.publishMediaPlayerAudioTrack = true;
mediaOptions.enableAudioRecordingOrPlayout = false;
mediaOptions.clientRoleType = Constants.CLIENT_ROLE_BROADCASTER;
rtcConnection.channelId = channel2;
rtcConnection.localUid = channel2Uid;
int ret = engine.joinChannelEx(null, rtcConnection, mediaOptions, iRtcEngineEventHandler2);
调用以下方法进行上麦或下麦:
API | 实现功能 |
---|---|
setClientRole |
设置用户角色。 加入歌房时,需要将主唱的用户角色设为 BROADCASTER 、听众的用户角色设为 AUDIENCE 。听众成功上麦后,需要先调用该方法将用户角色切换为 BROADCASTER ,才能在房间里发布音频流。 |
主唱调用以下方法设置音频流:
API | 实现功能 |
---|---|
setAudioProfile [2/2] |
设置音频编码属性。 |
muteLocalAudioStream |
主唱可以关闭或开启本地麦克风。 |
adjustRecordingSignalVolume |
调节人声音量。 |
参考以下示例代码使用音乐播放器播放音乐资源。
注册曲库并获取 IAgoraMusicContentCenter
。
mcc = IAgoraMusicContentCenter.create(rtcEngine());
初始化 IAgoraMusicContentCenter
。
AgoraMusicContentCenterConfiguration contentCenterConfiguration = new AgoraMusicContentCenterConfiguration();
// 已开启音乐内容中心项目的 App ID。
contentCenterConfiguration.appId = "填入你的 App ID";
// 使用音乐内容中心的用户 ID。该 ID 可以和你加入 RTC 频道时使用的 uid 一致,但不能为 0。
contentCenterConfiguration.mccUid = 填入当前使用音乐内容中心用户的 uid;
// 填入用于鉴权的 Token。
contentCenterConfiguration.token = "xxxxxxxxxxxx";
// 创建 IAgoraMusicContentCenterEventHandler,用于 SDK 向客户端发送音乐内容中心事件通知。
contentCenterConfiguration.eventHandler = new IAgoraMusicContentCenterEventHandler();
// 初始化 IAgoraMusicContentCenter。
mcc.initialize(contentCenterConfiguration);
创建音乐播放器。如果你需要播放音乐内容中心的音乐资源,需要使用此播放器进行播放。
musicPlayer = mcc.createMusicPlayer();
获取音乐榜单列表或搜索音乐资源。
你可以通过以下 API 来获取音乐资源:
getMusicCharts
:获取曲库中全部的音乐榜单。getMusicCollectionByMusicChartId
:通过音乐榜单的 ID 获取指定榜单的音乐资源列表。searchMusic
:通过关键词搜索并获取音乐资源。mcc.getMusicCharts();
mcc.getMusicCollectionByMusicChartId(0,0,10);
mcc.searchMusic("hello",0,10);
你还可以通过 searchMusic[2/2] 和 getMusicCollectionByMusicChartId[2/2] 中的 jsonOption
字段来筛选有副歌片段、支持打分的音乐资源。jsonOption
字段的具体说明见对应方法的 API 文档。
筛选支持打分的音乐资源:
mMcc.searchMusic("千里之外", 1, 50, "{\"pitchType\":1}");
mMcc.getMusicCollectionByMusicChartId(3, 1, 10, "{\"pitchType\":1}");
筛选有副歌片段的音乐资源:
mMcc.searchMusic("千里之外", 1, 50, "{\"needHighPart\":true}");
mMcc.getMusicCollectionByMusicChartId(3, 1, 10, "{\"needHighPart\":true}");
预加载音乐资源。
调用 preload
方法来预加载需要播放的音乐资源。加载音乐资源之后,你可以调用 isPreload
方法来检测音乐资源是否已被预加载,该方法可同步调用且不包含耗时操作。
onPreLoadEvent
回调报告音乐资源加载完成。mcc.preload(songCode, null);
mcc.isPreload(songCode);
如果你需要预加载的是某一音乐资源独立的副歌片段,在预加载之前你还需要调用 getInternalSongCode 来获取该片段的资源编号(internalSongCode
)。
// 传入音乐资源的编号和 jsonOption 的值来获取副歌片段的资源编号,详细参数解释请参考该方法的 API 文档。
mMcc.getInternalSongCode(songCode, "{\"format\":{\"highpart\":0}}");
// 将获取到的副歌片段资源编号传入 songCode 参数来预加载该片段。
int ret = mMcc.preload(songCode);
songCode
参数。打开音乐资源。
调用 open
方法打开音乐资源。
musicPlayer.open(songCode, 0);
open
后,请确保收到 onPlayerStateChanged
回调报告状态为 PLAYER_STATE_OPEN_COMPLETED(2)
再调用 play
来进行播放。销毁播放器及引擎。
当你无需再使用音乐播放器时,你需要:
a. 调用 IAgoraMusicPlayer
类下的 destroy
销毁音乐播放器;
b. 调用 IAgoraMusicContentCenter
类下的 destroy
销毁音乐内容中心实例;
c. 调用 RtcEngine
类下的 destroy
销毁引擎。
void destroy() {
musicPlayer.unRegisterPlayerObserver(mediaPlayerObserver);
musicPlayer.destroy();
musicPlayer = null;
mcc.unregisterEventHandler();
IAgoraMusicContentCenter.destroy();
contentCenterConfiguration.eventHandler = null;
contentCenterConfiguration = null;
mcc = null;
rtcEngine.destroy();
rtcEngine = null;
}
你可以参考版权音乐 API 文档了解更多详细信息,音乐播放器的参数调节、推流设置、事件回调等请参考 媒体播放器 API 文档。
API | 实现功能 |
---|---|
createDataStream [2/2] |
主唱创建数据流。用于同步歌曲播放进度。 |
sendStreamMessage |
主唱发送数据流到歌房内所有用户。 |
onStreamMessage |
歌房内所有用户接收主唱发送的数据流,结合歌词控件实现歌词进度的同步更新。 |
示例代码如下:
// 以下代码发送接收的 json 格式仅为示例格式,具体协议格式根据你的具体项目而定。
DataStreamConfig cfg = new DataStreamConfig();
cfg.syncWithAudio = true;
cfg.ordered = true;
mStreamId = getRtcEngine().createDataStream(cfg);
private void sendSyncLrc(String lrcId, long duration, long time) {
Map<String, Object> msg = new HashMap<>();
msg.put("cmd", "setLrcTime");
msg.put("lrcId", lrcId);
msg.put("duration", duration);
msg.put("time", time);//ms
JSONObject jsonMsg = new JSONObject(msg);
int streamId = RoomManager.Instance(mContext).getStreamId();
int ret = RoomManager.Instance(mContext).getRtcEngine().sendStreamMessage(streamId, jsonMsg.toString().getBytes());
if (ret < 0) {
mLogger.e("sendSyncLrc() sendStreamMessage called returned: ret = [%s]", ret);
}
}
@Override
public void onStreamMessage(int uid, int streamId, byte[] data) {
JSONObject jsonMsg;
String strMsg = new String(data);
jsonMsg = new JSONObject(strMsg);
if (jsonMsg.getString("cmd").equals("setLrcTime")) {
long position = jsonMsg.getLong("time");
if (position == 0) {
mHandler.obtainMessage(ACTION_ON_RECEIVED_PLAY, uid).sendToTarget();
} else if (position == -1) {
mHandler.obtainMessage(ACTION_ON_RECEIVED_PAUSE, uid).sendToTarget();
} else {
Bundle bundle = new Bundle();
bundle.putInt("uid", uid);
bundle.putLong("time", position);
Message message = Message.obtain(mHandler, ACTION_ON_RECEIVED_SYNC_TIME);
message.setData(bundle);
message.sendToTarget();
}
}
}
音乐资源类型 | 切换原唱\伴奏方式 |
---|---|
左声道伴奏、右声道原唱的单音轨歌曲 | 切换至原唱:调用 setAudioDualMonoMode ,将 mode 设为 AUDIO_DUAL_MONO_R (2)。切换至伴奏:调用 setAudioDualMonoMode ,将 mode 设为 AUDIO_DUAL_MONO_L (1)。必须设置只播放左声道或右声道,否则会因为左、右声道音源不同导致音质变差。 |
|多音轨的歌曲 |
切换至原唱:调用 selectAudioTrack
将 index
设为 0
。
切换至伴奏:调用 selectAudioTrack
将 index
设为 1
。
调用 setAudioEffectPreset
方法,在不改变原声的性别特征的前提下,设置人声音效。设置音效后,频道内所有用户都能听到该效果。详见设置人声效果。
调用 enableInEarMonitoring
[2/2] 方法开启主唱的耳返功能。
调用 enableAudioVolumeIndication
方法开启歌唱评分功能,并通过 onAudioVolumeIndication
回调,结合声网歌词组件实现歌唱评分效果,详见歌词打分组件。