From 37f40b116b7cb6eb7fc21cff857b5a3ffe871f95 Mon Sep 17 00:00:00 2001 From: kelzr Date: Tue, 3 Jan 2023 11:00:36 +0800 Subject: [PATCH 01/20] [KTVI] add --- .../iOS/\347\202\271\346\255\214.md" | 158 ++++++++++++++++++ 1 file changed, 158 insertions(+) create mode 100644 "markdown/online-ktv/\346\224\271\350\277\233/iOS/\347\202\271\346\255\214.md" diff --git "a/markdown/online-ktv/\346\224\271\350\277\233/iOS/\347\202\271\346\255\214.md" "b/markdown/online-ktv/\346\224\271\350\277\233/iOS/\347\202\271\346\255\214.md" new file mode 100644 index 00000000000..d1ff694b472 --- /dev/null +++ "b/markdown/online-ktv/\346\224\271\350\277\233/iOS/\347\202\271\346\255\214.md" @@ -0,0 +1,158 @@ +## 简介 + +本文介绍如何实现点歌功能。用户需要在 K 歌之前进行点歌。点歌指用户通过浏览或搜索正版音乐,选定心仪的音乐,然后下载播放。音乐开始播放后,参与 K 歌的用户可以开始唱歌。 + +## 示例项目 + +声网在 GitHub 上提供开源的 //TODO 示例项目供你参考。 + +## 准备开发环境 + +## 前提条件 + +- Xcode 12.0 或以上版本。 +- iOS 11.0 或以上版本的真机设备。 +- 有效的[声网开发者账号](/cn/Agora%20Platform/sign_in_and_sign_up?platform=iOS)。 +- 有效的声网项目,并获取项目的 App ID 和 RTM Token。详情请参考[开始使用 Agora 平台](/cn/Agora%20Platform/get_appid_token?platform=All%20Platforms)。 +- 联系 sales@agora.io 申请开通声网正版音乐内容中心服务。 + +
  • 模拟器可能无法运行在线 K 歌房的全部功能,因此声网推荐你在真机上运行 K 歌房项目。
  • 如果你的网络环境部署了防火墙,请参考应用企业防火墙限制以正常使用声网服务。
  • + +### 创建项目 + +参考以下步骤创建一个 iOS 项目。 + +
    + 创建 iOS 项目 + +1. 打开 **Xcode** 并点击 **Create a new Xcode project**。 +2. 选择项目类型为 **Single View App**,并点击 **Next**。 +3. 输入项目信息,如项目名称、开发团队信息、组织名称和语言,并点击 **Next**。 + + **Note**:如果你没有添加过开发团队信息,会看到 **Add account…** 按钮。点击该按钮并按照屏幕提示登入 Apple ID,完成后即可选择你的账户作为开发团队。 +4. 选择项目存储路径,并点击 **Create**。 +5. 将你的 iOS 设备连接至电脑。 +6. 进入 **TARGETS > Project Name > Signing & Capabilities** 菜单,选择 **Automatically manage signing**,并在弹出菜单中点击 **Enable Automatic**。 +
    + +### 集成 SDK + +在线 K 歌房需要集成声网的两个 SDK: + +- 音频 SDK:主要提供在线音频互动、音频管理(例如调节本地麦克风采集的歌声音量、混合用户歌声和背景音乐声、获取本地歌声的音高值)、NTP 时间戳同步功能。 +- 歌词组件:主要提供歌词文本和标准音高线随音乐播放而同步展示、用户音准评分功能。 + +下节介绍如何使用 Cocoapods 集成 SDK: + +1. 开始前确保你已安装 Cocoapods。参考 [Getting Started with CocoaPods](https://guides.cocoapods.org/using/getting-started.html#getting-started) 安装说明。 + +2. 在终端里进入项目根目录,并运行 `pod init` 命令。项目文件夹下会生成一个 `Podfile` 文本文件。 + +3. 打开 `Podfile` 文件,修改文件为如下内容。注意将 `Your App` 替换为你的 Target 名称。 + + ```ruby + target 'Your App' do + pod 'AgoraAudio_iOS', '~> 4.0.1' + pod 'AgoraLyricsScore', '~> 1.0.8' + end + ``` + + 示例代码中的版本号仅供参考。音频 SDK 最新版本号详见[发版说明](/cn/voice-call-4.x/release_ios_audio_ng?platform=iOS)。歌词组件最新版本号详见 [GitHub Tags](https://github.com/AgoraIO-Community/LrcView-iOS/tags)。 + + +4. 在终端内运行 `pod install` 命令安装 SDK。成功安装后,Terminal 中会显示 `Pod installation complete!`,此时项目文件夹下会生成一个 `xcworkspace` 文件。 + +5. 打开新生成的 `xcworkspace` 文件。 + +## 实现方法 + +//TODO pic + +### 1. 初始化音乐内容中心 + +调用 sharedContentCenterWithConfig 初始化声网正版音乐内容中心。音乐内容AgoraMusicContentCenterConfiguration 中需要传入用户账号和 RTM Token xxx + +提供用户的RTM账号与RTM Token,如何生成请查看xxx + +```objective-c +AgoraMusicContentCenterConfig *contentCenterConfiguration = [[AgoraMusicContentCenterConfig alloc] init]; +contentCenterConfiguration.rtcEngine = self.RTCkit; +contentCenterConfiguration.appId = [[AppContext shared] appId]; +contentCenterConfiguration.mccUid = [VLUserCenter.user.id integerValue]; +contentCenterConfiguration.token = VLUserCenter.user.agoraRTMToken; +self.AgoraMcc = [AgoraMusicContentCenter sharedContentCenterWithConfig:contentCenterConfiguration]; +``` + +### 2. 创建音乐播放器 + +通过createMusicPlayerWithDelegate创建曲库专用的MusicPlayer,delegate传入的对象可以用来监听播放器相关的事件。 + +```objective-c +self.rtcMediaPlayer = [self.AgoraMcc createMusicPlayerWithDelegate:[AppContext shared]]; +``` + +### 3. 获取歌曲 + +使用searchMusicWithKeyWord基于关键词搜索歌曲,或使用getMusicCollectionWithMusicChartId基于榜单获取歌曲,获取的结果可以按需进行分页,内容包含歌手,专辑封面,歌词类型,歌曲长度等。 + +关键词搜索 + +``` +- (void)searchWithKeyWord:(NSString*)keyWord{ + self.requestId = [self.AgoraMcc searchMusicWithKeyWord:keyWord + page:self.page + pageSize:5 + jsonOption:nil]; +} +- (void)onMusicCollectionResult:(NSString *)requestId + status:(AgoraMusicContentCenterStatusCode)status + result:(AgoraMusicCollection *)result { + if (![self.requestId isEqualToString:requestId]) { + return; + } + //extra operations + ... +} +``` + +榜单获取 +``` +-(void)searchWithKeyWord:(NSString*)keyWord{ + self.requestId = [self.AgoraMcc getMusicCollectionWithMusicChartId:chartId + page:self.page + pageSize:20 + jsonOption:nil]; +} + +- (void)onMusicCollectionResult:(NSString *)requestId + status:(AgoraMusicContentCenterStatusCode)status + result:(AgoraMusicCollection *)result { + if (![self.requestId isEqualToString:requestId]) { + return; + } + //extra operations + ... +} +``` + +### 4. 下载播放音乐 + +使用preloadWithSongCode接口预加载歌曲资源,可以预先通过isPreloadedWithSongCode确认歌曲是否已在本地下载过,若已下载过则可以直接跳过preload过程。 + +通过openMediaWithSongCode加载下载完成的音乐,等待player的回调以确认歌曲加载完成。 + +加载完成后就可以调用player的play接口进行音乐的播放。 + +## API 参考 + +如需了解更多点歌相关的 API,请参考[音频 SDK 版权音乐 API 说明](https://docs.agora.io/cn/voice-call-4.x/API%20Reference/ios_ng/API/toc_drm.html)。 + + + +- [sharedContentCenterWithConfig](https://docs.agora.io/cn/voice-call-4.x/API%20Reference/ios_ng/API/toc_drm.html?platform=iOS#api_imusiccontentcenter_initialize) +- [createMusicPlayerWithDelegate](https://docs.agora.io/cn/voice-call-4.x/API%20Reference/ios_ng/API/toc_drm.html?platform=iOS#api_imusiccontentcenter_createmusicplayer) +- [searchMusicWithKeyWord](https://docs.agora.io/cn/voice-call-4.x/API%20Reference/ios_ng/API/toc_drm.html?platform=iOS#api_imusiccontentcenter_searchmusic) +- [getMusicCollectionWithMusicChartId](https://docs.agora.io/cn/voice-call-4.x/API%20Reference/ios_ng/API/toc_drm.html?platform=iOS#api_imusiccontentcenter_getmusiccollectionbymusicchartid) +- [isPreloadedWithSongCode](https://docs.agora.io/cn/voice-call-4.x/API%20Reference/ios_ng/API/toc_drm.html?platform=iOS#api_imusiccontentcenter_ispreloaded) +- [preloadWithSongCode](https://docs.agora.io/cn/voice-call-4.x/API%20Reference/ios_ng/API/toc_drm.html?platform=iOS#api_imusicontentcenter_preload) +- [openMediaWithSongCode](https://docs.agora.io/cn/voice-call-4.x/API%20Reference/ios_ng/API/toc_drm.html?platform=iOS#api_imusicplayer_open) \ No newline at end of file From 9091c0a02cd9ba3892d2b4b64f57f070eb72bb5b Mon Sep 17 00:00:00 2001 From: kelzr Date: Wed, 4 Jan 2023 16:18:07 +0800 Subject: [PATCH 02/20] [KTVI] temp file --- .../iOS/\345\220\210\345\224\261" | 0 .../iOS/\347\202\271\346\255\214.wsd" | 37 +++++++++++++++++++ .../iOS/\347\213\254\345\224\261" | 0 ...3\206\346\210\220\346\246\202\350\277\260" | 0 4 files changed, 37 insertions(+) create mode 100644 "markdown/online-ktv/\346\224\271\350\277\233/iOS/\345\220\210\345\224\261" create mode 100644 "markdown/online-ktv/\346\224\271\350\277\233/iOS/\347\202\271\346\255\214.wsd" create mode 100644 "markdown/online-ktv/\346\224\271\350\277\233/iOS/\347\213\254\345\224\261" create mode 100644 "markdown/online-ktv/\346\224\271\350\277\233/iOS/\351\233\206\346\210\220\346\246\202\350\277\260" diff --git "a/markdown/online-ktv/\346\224\271\350\277\233/iOS/\345\220\210\345\224\261" "b/markdown/online-ktv/\346\224\271\350\277\233/iOS/\345\220\210\345\224\261" new file mode 100644 index 00000000000..e69de29bb2d diff --git "a/markdown/online-ktv/\346\224\271\350\277\233/iOS/\347\202\271\346\255\214.wsd" "b/markdown/online-ktv/\346\224\271\350\277\233/iOS/\347\202\271\346\255\214.wsd" new file mode 100644 index 00000000000..250b4d01265 --- /dev/null +++ "b/markdown/online-ktv/\346\224\271\350\277\233/iOS/\347\202\271\346\255\214.wsd" @@ -0,0 +1,37 @@ +@startuml +autonumber +skinparam monochrome true +participant "RTC SDK" as rtc +participant "Android app" as app +participant "RTM SDK" as rtm +participant "云服务" as cloud +== 登录 RTM 系统 == +app -> rtm: createInstance +app -> rtm: login +== 创建直播间 == +app -> cloud: post create room +cloud -->> app: create room result +== 加入直播间 == +app -> cloud: post join room +cloud -->> app: join room result +note right +返回直播间信息,包括用户 +列表、用户信息、房间信息 +end note +== 加入 RTC 和 RTM 频道并开始直播 == +app -> rtc: create +app -> rtc: enableVideo +app -> rtc: setChannelProfile(BROADCASTING) +app -> rtc: setClientRole(BROADCASTER) +app -> rtc: setupLocalVideo +app -> rtc: joinChannel +rtc -->> app: onJoinChannelSuccess +app -> rtm: createChannel˚ +app -> rtm: join +rtm -->> app: onSuccess +== 停止直播并离开 RTC 和 RTM 频道 == +app -> cloud: post leave room +app -> rtm: leave +app -> rtm: logout +app -> rtc: leaveChannel +@enduml \ No newline at end of file diff --git "a/markdown/online-ktv/\346\224\271\350\277\233/iOS/\347\213\254\345\224\261" "b/markdown/online-ktv/\346\224\271\350\277\233/iOS/\347\213\254\345\224\261" new file mode 100644 index 00000000000..e69de29bb2d diff --git "a/markdown/online-ktv/\346\224\271\350\277\233/iOS/\351\233\206\346\210\220\346\246\202\350\277\260" "b/markdown/online-ktv/\346\224\271\350\277\233/iOS/\351\233\206\346\210\220\346\246\202\350\277\260" new file mode 100644 index 00000000000..e69de29bb2d From 008513e54a2d0cd95a7825bbce3b49bf654c04c5 Mon Sep 17 00:00:00 2001 From: kelzr Date: Mon, 9 Jan 2023 18:24:53 +0800 Subject: [PATCH 03/20] =?UTF-8?q?[KTVI]=20=E7=82=B9=E6=AD=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../iOS/\347\202\271\346\255\214.md" | 194 +++++++++++++----- .../iOS/\347\202\271\346\255\214.wsd" | 54 ++--- 2 files changed, 159 insertions(+), 89 deletions(-) diff --git "a/markdown/online-ktv/\346\224\271\350\277\233/iOS/\347\202\271\346\255\214.md" "b/markdown/online-ktv/\346\224\271\350\277\233/iOS/\347\202\271\346\255\214.md" index d1ff694b472..b6e7345b0af 100644 --- "a/markdown/online-ktv/\346\224\271\350\277\233/iOS/\347\202\271\346\255\214.md" +++ "b/markdown/online-ktv/\346\224\271\350\277\233/iOS/\347\202\271\346\255\214.md" @@ -8,7 +8,7 @@ ## 准备开发环境 -## 前提条件 +### 前提条件 - Xcode 12.0 或以上版本。 - iOS 11.0 或以上版本的真机设备。 @@ -39,8 +39,8 @@ 在线 K 歌房需要集成声网的两个 SDK: -- 音频 SDK:主要提供在线音频互动、音频管理(例如调节本地麦克风采集的歌声音量、混合用户歌声和背景音乐声、获取本地歌声的音高值)、NTP 时间戳同步功能。 -- 歌词组件:主要提供歌词文本和标准音高线随音乐播放而同步展示、用户音准评分功能。 +- 音频 SDK:主要提供在线音频互动、音频管理(例如,调节本地麦克风采集的歌声音量、混合用户歌声和背景音乐声、获取本地歌声的音高值)、NTP 时间戳同步功能。 +- 歌词组件:主要提供歌词文本随音乐播放而同步展示、标准音高线随音乐播放而同步展示、用户音准评分功能。 下节介绍如何使用 Cocoapods 集成 SDK: @@ -66,13 +66,17 @@ ## 实现方法 -//TODO pic +![](https://web-cdn.agora.io/docs-files/1673259817555) -### 1. 初始化音乐内容中心 +### 1. 初始化和创建 -调用 sharedContentCenterWithConfig 初始化声网正版音乐内容中心。音乐内容AgoraMusicContentCenterConfiguration 中需要传入用户账号和 RTM Token xxx +调用 `sharedContentCenterWithConfig` 初始化声网正版音乐内容中心(`AgoraMusicContentCenter`)。音乐内容中心配置(`AgoraMusicContentCenterConfiguration`)中需要传入如下值: + +- `rtcEngine`: `AgoraRtcEngineKit` 实例 +- `appId`: 项目 App ID。请确保你已经为项目开通声网正版音乐内容中心服务。详见[前提条件](#前提条件)。//TODO add link +- `token`: 音乐内容中心里用到的 Access Token。生成方式详见[使用 Access Token2 鉴权](/cn/Real-time-Messaging/token2_server_rtm?platform=All%20Platforms)。生成 Access Token 时需要传入的 `userId` 即为下面的 `mccUid`。//TODO RTM or RTC Token +- `mccUid`: 音乐内容中心里用到的用户 ID。可以与加入 RTC 频道时使用的 UID 一致。不能为 0。 -提供用户的RTM账号与RTM Token,如何生成请查看xxx ```objective-c AgoraMusicContentCenterConfig *contentCenterConfiguration = [[AgoraMusicContentCenterConfig alloc] init]; @@ -83,76 +87,154 @@ contentCenterConfiguration.token = VLUserCenter.user.agoraRTMToken; self.AgoraMcc = [AgoraMusicContentCenter sharedContentCenterWithConfig:contentCenterConfiguration]; ``` -### 2. 创建音乐播放器 - -通过createMusicPlayerWithDelegate创建曲库专用的MusicPlayer,delegate传入的对象可以用来监听播放器相关的事件。 +调用 `createMusicPlayerWithDelegate` 创建声网正版音乐内容中心专用的音乐播放器。向 `delegate` 传入的对象可以用来监听播放器相关的事件。 ```objective-c self.rtcMediaPlayer = [self.AgoraMcc createMusicPlayerWithDelegate:[AppContext shared]]; ``` -### 3. 获取歌曲 +### 2. 获取音乐资源 -使用searchMusicWithKeyWord基于关键词搜索歌曲,或使用getMusicCollectionWithMusicChartId基于榜单获取歌曲,获取的结果可以按需进行分页,内容包含歌手,专辑封面,歌词类型,歌曲长度等。 +调用 `searchMusicWithKeyWord` 或 `getMusicCollectionWithMusicChartId` 获取音乐资源。方法调用成功后,SDK 会通过 `onMusicCollectionResult` 向你报告音乐资源的详细信息。你可以基于实际业务逻辑,将获取的音乐资源结果进行分页展示,展示的内容可以为专辑封面、歌手、歌曲时长、歌词类型等,详见 [`AgoraMusic`](./API%20Reference/ios_ng/API/class_music.html)。 -关键词搜索 +- 方式一:通过 `searchMusicWithKeyWord` 方法,传入歌曲或歌手名称,搜索到音乐资源。 + + ```objective-c + - (void)searchWithKeyWord:(NSString*)keyWord{ + self.requestId = [self.AgoraMcc searchMusicWithKeyWord:keyWord + page:self.page + pageSize:5 + jsonOption:nil]; + } + + #pragma mark - AgoraMusicContentCenterEventDelegate + - (void)onMusicCollectionResult:(NSString *)requestId + status:(AgoraMusicContentCenterStatusCode)status + result:(AgoraMusicCollection *)result { + if (![self.requestId isEqualToString:requestId]) { + return; + } + + // 此处添加你的业务逻辑 + ... + } + ``` + +- 方式二:通过 `getMusicCollectionWithMusicChartId` 方法,传入音乐榜单的 ID,获取该榜单的音乐资源列表。//TODO 是否需要补充获取音乐榜单 ID 的步骤? + + ```objective-c + - (void)searchWithRankingChartId:(NSString*)chartId atPage:(NSInteger)page{ + self.requestId = [self.AgoraMcc getMusicCollectionWithMusicChartId:chartId + page:page + pageSize:20 + jsonOption:nil]; + } + + #pragma mark - AgoraMusicContentCenterEventDelegate + - (void)onMusicCollectionResult:(NSString *)requestId + status:(AgoraMusicContentCenterStatusCode)status + result:(AgoraMusicCollection *)result { + if (![self.requestId isEqualToString:requestId]) { + return; + } + + // 此处添加你的业务逻辑 + ... + } + ``` + +### 3. 预加载音乐资源 + +调用 `isPreloadedWithSongCode` 检测获取到且待播放的音乐资源是否已在本地预加载: + +- 如果尚未预加载,调用 `preloadWithSongCode` 预加载音乐资源。其中,`songCode` 取值为上节操作中 `onMusicCollectionResult` 回调在 `result` 参数中报告的 `songCode`。等 `onPreLoadEvent` 回调报告加载状态为 `AgoraMusicContentCenterPreloadStatusOK(0)` 后,再进行下一节的打开与播放音乐操作。 +- 如果已经预加载,直接跳过本节操作。 + +`isPreloadedWithSongCode` 方法可同步调用且不包含耗时操作。 + + +```objective-c +typedef void (^LoadMusicCallback)(AgoraMusicContentCenterPreloadStatus); + +- (void)preloadMusic:(NSInteger)songCode withCallback:(LoadMusicCallback)block { + NSInteger error = [self.musicCenter isPreloadedWithSongCode:songCode]; + NSString* songCodeKey = [NSString stringWithFormat: @"%ld", songCode]; + if(error == 0) { + if(block) { + [self.musicCallbacks removeObjectForKey:songCodeKey]; + block(AgoraMusicContentCenterPreloadStatusOK); + } -``` -- (void)searchWithKeyWord:(NSString*)keyWord{ - self.requestId = [self.AgoraMcc searchMusicWithKeyWord:keyWord - page:self.page - pageSize:5 - jsonOption:nil]; -} -- (void)onMusicCollectionResult:(NSString *)requestId - status:(AgoraMusicContentCenterStatusCode)status - result:(AgoraMusicCollection *)result { - if (![self.requestId isEqualToString:requestId]) { return; } - //extra operations - ... -} -``` -榜单获取 -``` --(void)searchWithKeyWord:(NSString*)keyWord{ - self.requestId = [self.AgoraMcc getMusicCollectionWithMusicChartId:chartId - page:self.page - pageSize:20 - jsonOption:nil]; + error = [self.musicCenter preloadWithSongCode:songCode jsonOption:nil]; + if (error != 0) { + if(block) { + [self.musicCallbacks removeObjectForKey:songCodeKey]; + block(AgoraMusicContentCenterPreloadStatusError); + } + return; + } + [self.musicCallbacks setObject:block forKey:songCodeKey]; } -- (void)onMusicCollectionResult:(NSString *)requestId - status:(AgoraMusicContentCenterStatusCode)status - result:(AgoraMusicCollection *)result { - if (![self.requestId isEqualToString:requestId]) { +#pragma mark - AgoraMusicContentCenterEventDelegate +- (void)onPreLoadEvent:(NSInteger)songCode + percent:(NSInteger)percent + status:(AgoraMusicContentCenterPreloadStatus)status + msg:(nonnull NSString *)msg + lyricUrl:(nonnull NSString *)lyricUrl { + if (status == AgoraMusicContentCenterPreloadStatusPreloading) { return; } - //extra operations - ... + NSString* songCodeKey = [NSString stringWithFormat: @"%ld", songCode]; + LoadMusicCallback block = [self.musicCallbacks objectForKey:songCodeKey]; + if(!block) { + return; + } + [self.musicCallbacks removeObjectForKey:songCodeKey]; + block(status); } ``` -### 4. 下载播放音乐 +### 4. 打开并播放音乐资源 -使用preloadWithSongCode接口预加载歌曲资源,可以预先通过isPreloadedWithSongCode确认歌曲是否已在本地下载过,若已下载过则可以直接跳过preload过程。 +调用 `AgoraMusicPlayerProtocol.openMediaWithSongCode` 打开已经预加载完的音乐资源。收到 `AgoraRtcMediaPlayerDelegate.didChangedToState` 回调报告的音乐播放器状态为 `AgoraMediaPlayerStateOpenCompleted(5)` 后,调用 `AgoraRtcMediaPlayerProtocol.play` 播放音乐。 -通过openMediaWithSongCode加载下载完成的音乐,等待player的回调以确认歌曲加载完成。 +```objective-c +- (void)openMusicMedia:(NSInteger)songCode{ + [self.rtcMediaPlayer openMediaWithSongCode:songCode startPos:0]; +} -加载完成后就可以调用player的play接口进行音乐的播放。 +#pragma mark - AgoraRtcMediaPlayerDelegate +- (void)AgoraRtcMediaPlayer:(id)playerKit didChangedToState:(AgoraMediaPlayerState)state error:(AgoraMediaPlayerError)error +{ + if (state == AgoraMediaPlayerStateOpenCompleted) { + [player play]; + } +} +``` ## API 参考 -如需了解更多点歌相关的 API,请参考[音频 SDK 版权音乐 API 说明](https://docs.agora.io/cn/voice-call-4.x/API%20Reference/ios_ng/API/toc_drm.html)。 - - - -- [sharedContentCenterWithConfig](https://docs.agora.io/cn/voice-call-4.x/API%20Reference/ios_ng/API/toc_drm.html?platform=iOS#api_imusiccontentcenter_initialize) -- [createMusicPlayerWithDelegate](https://docs.agora.io/cn/voice-call-4.x/API%20Reference/ios_ng/API/toc_drm.html?platform=iOS#api_imusiccontentcenter_createmusicplayer) -- [searchMusicWithKeyWord](https://docs.agora.io/cn/voice-call-4.x/API%20Reference/ios_ng/API/toc_drm.html?platform=iOS#api_imusiccontentcenter_searchmusic) -- [getMusicCollectionWithMusicChartId](https://docs.agora.io/cn/voice-call-4.x/API%20Reference/ios_ng/API/toc_drm.html?platform=iOS#api_imusiccontentcenter_getmusiccollectionbymusicchartid) -- [isPreloadedWithSongCode](https://docs.agora.io/cn/voice-call-4.x/API%20Reference/ios_ng/API/toc_drm.html?platform=iOS#api_imusiccontentcenter_ispreloaded) -- [preloadWithSongCode](https://docs.agora.io/cn/voice-call-4.x/API%20Reference/ios_ng/API/toc_drm.html?platform=iOS#api_imusicontentcenter_preload) -- [openMediaWithSongCode](https://docs.agora.io/cn/voice-call-4.x/API%20Reference/ios_ng/API/toc_drm.html?platform=iOS#api_imusicplayer_open) \ No newline at end of file +本节列出本文提到的 API: + +- AgoraMusicContentCenter: + - [sharedContentCenterWithConfig](./API%20Reference/ios_ng/API/toc_drm.html?platform=iOS#api_imusiccontentcenter_initialize) + - [createMusicPlayerWithDelegate](./API%20Reference/ios_ng/API/toc_drm.html?platform=iOS#api_imusiccontentcenter_createmusicplayer) + - [searchMusicWithKeyWord](./API%20Reference/ios_ng/API/toc_drm.html?platform=iOS#api_imusiccontentcenter_searchmusic) + - [getMusicCollectionWithMusicChartId](./API%20Reference/ios_ng/API/toc_drm.html?platform=iOS#api_imusiccontentcenter_getmusiccollectionbymusicchartid) + - [isPreloadedWithSongCode](./API%20Reference/ios_ng/API/toc_drm.html?platform=iOS#api_imusiccontentcenter_ispreloaded) + - [preloadWithSongCode](./API%20Reference/ios_ng/API/toc_drm.html?platform=iOS#api_imusicontentcenter_preload) +- AgoraMusicContentCenterEventDelegate: + - [onMusicCollectionResult](./API%20Reference/ios_ng/API/toc_drm.html#callback_imusiccontentcentereventhandler_onmusiccollectionresult) + - [onPreLoadEvent](./API%20Reference/ios_ng/API/toc_drm.html#callback_imusiccontentcentereventhandler_onpreloadevent) +- AgoraMusicPlayerProtocol: + - [openMediaWithSongCode](./API%20Reference/ios_ng/API/toc_drm.html?platform=iOS#api_imusicplayer_open) +- AgoraRtcMediaPlayerProtocol: + - [play](./API%20Reference/ios_ng/v4.1.0/API/toc_mediaplayer.html#api_imediaplayer_play) +- AgoraRtcMediaPlayerDelegate: + - [didChangedToState](./API%20Reference/ios_ng/v4.1.0/API/toc_mediaplayer.html#callback_imediaplayersourceobserver_onplayersourcestatechanged) + +如需了解更多,请参考[音频 SDK API 参考文档](./API%20Reference/ios_ng/API/rtc_api_overview_ng.html)。 \ No newline at end of file diff --git "a/markdown/online-ktv/\346\224\271\350\277\233/iOS/\347\202\271\346\255\214.wsd" "b/markdown/online-ktv/\346\224\271\350\277\233/iOS/\347\202\271\346\255\214.wsd" index 250b4d01265..415cc27b0fd 100644 --- "a/markdown/online-ktv/\346\224\271\350\277\233/iOS/\347\202\271\346\255\214.wsd" +++ "b/markdown/online-ktv/\346\224\271\350\277\233/iOS/\347\202\271\346\255\214.wsd" @@ -1,37 +1,25 @@ @startuml +title 点歌 API 时序图 autonumber skinparam monochrome true -participant "RTC SDK" as rtc -participant "Android app" as app -participant "RTM SDK" as rtm -participant "云服务" as cloud -== 登录 RTM 系统 == -app -> rtm: createInstance -app -> rtm: login -== 创建直播间 == -app -> cloud: post create room -cloud -->> app: create room result -== 加入直播间 == -app -> cloud: post join room -cloud -->> app: join room result -note right -返回直播间信息,包括用户 -列表、用户信息、房间信息 -end note -== 加入 RTC 和 RTM 频道并开始直播 == -app -> rtc: create -app -> rtc: enableVideo -app -> rtc: setChannelProfile(BROADCASTING) -app -> rtc: setClientRole(BROADCASTER) -app -> rtc: setupLocalVideo -app -> rtc: joinChannel -rtc -->> app: onJoinChannelSuccess -app -> rtm: createChannel˚ -app -> rtm: join -rtm -->> app: onSuccess -== 停止直播并离开 RTC 和 RTM 频道 == -app -> cloud: post leave room -app -> rtm: leave -app -> rtm: logout -app -> rtc: leaveChannel +participant "iOS 应用" as a +participant "声网 SDK" as b +== 初始化和创建 == +a -> b: **AgoraMusicContentCenter**.sharedContentCenterWithConfig +a -> b: AgoraMusicContentCenter.createMusicPlayerWithDelegate +== 获取音乐资源(方式一) == +a -> b: AgoraMusicContentCenter.searchMusicWithKeyWord +b -->> a: AgoraMusicContentCenterEventDelegate.onMusicCollectionResult +== 获取音乐资源(方式二) == +a -> b: AgoraMusicContentCenter.getMusicCollectionWithMusicChartId +b -->> a: AgoraMusicContentCenterEventDelegate.onMusicCollectionResult +== 预加载音乐资源 == +a -> b: AgoraMusicContentCenter.isPreloadedWithSongCode +a -> b: AgoraMusicContentCenter.preloadWithSongCode +b -->> a: AgoraMusicContentCenterEventDelegate.onPreLoadEvent (AgoraMusicContentCenterPreloadStatus = 0) +== 打开并播放音乐资源 == +a -> b: **AgoraMusicPlayerProtocol**.openMediaWithSongCode +b -->> a: AgoraRtcMediaPlayerDelegate.didChangedToState (AgoraMediaPlayerState = 5) +a ->b: **AgoraRtcMediaPlayerProtocol**.play +b -->> a: AgoraRtcMediaPlayerDelegate.didChangedToState (AgoraMediaPlayerState = 3) @enduml \ No newline at end of file From 141b5b5256edc339ae54a09b2d0cd566c5ac3c32 Mon Sep 17 00:00:00 2001 From: kelzr Date: Wed, 18 Jan 2023 10:21:34 +0800 Subject: [PATCH 04/20] [KTVI] temp file --- .../iOS/\347\202\271\346\255\214.md" | 11 +++++++++-- .../iOS/\347\213\254\345\224\261" | 0 .../iOS/\347\213\254\345\224\261.md" | 3 +++ 3 files changed, 12 insertions(+), 2 deletions(-) delete mode 100644 "markdown/online-ktv/\346\224\271\350\277\233/iOS/\347\213\254\345\224\261" create mode 100644 "markdown/online-ktv/\346\224\271\350\277\233/iOS/\347\213\254\345\224\261.md" diff --git "a/markdown/online-ktv/\346\224\271\350\277\233/iOS/\347\202\271\346\255\214.md" "b/markdown/online-ktv/\346\224\271\350\277\233/iOS/\347\202\271\346\255\214.md" index b6e7345b0af..1324af21ab4 100644 --- "a/markdown/online-ktv/\346\224\271\350\277\233/iOS/\347\202\271\346\255\214.md" +++ "b/markdown/online-ktv/\346\224\271\350\277\233/iOS/\347\202\271\346\255\214.md" @@ -1,6 +1,6 @@ ## 简介 -本文介绍如何实现点歌功能。用户需要在 K 歌之前进行点歌。点歌指用户通过浏览或搜索正版音乐,选定心仪的音乐,然后下载播放。音乐开始播放后,参与 K 歌的用户可以开始唱歌。 +本文介绍如何实现点歌功能。用户需要在线上 K 歌前进行点歌。点歌指用户通过浏览榜单或搜索关键词选定想唱的正版音乐,然后下载播放音乐。只有上麦的用户可以点歌,观众不能点歌。 ## 示例项目 @@ -237,4 +237,11 @@ typedef void (^LoadMusicCallback)(AgoraMusicContentCenterPreloadStatus); - AgoraRtcMediaPlayerDelegate: - [didChangedToState](./API%20Reference/ios_ng/v4.1.0/API/toc_mediaplayer.html#callback_imediaplayersourceobserver_onplayersourcestatechanged) -如需了解更多,请参考[音频 SDK API 参考文档](./API%20Reference/ios_ng/API/rtc_api_overview_ng.html)。 \ No newline at end of file +如需了解更多,请参考[音频 SDK API 参考文档](./API%20Reference/ios_ng/API/rtc_api_overview_ng.html)。 + +## 下一步 +//TODO +点歌后,点歌用户可以进行如下操作: +- 切换原唱和伴奏,分别调节原唱和伴奏的音量,设置混响、美声、音效,对歌曲进行升调或降调,开启或关闭耳返功能。 +- 开始独唱或和房间其他上麦用户合唱。 +- 多次点歌后,在已点歌曲列表中对歌曲进行排序,调整演唱顺序。在已点歌曲列表中删除歌曲,放弃不想演唱的歌曲。在 K 歌界面点击切歌按钮,放弃演唱当前播放的歌曲。 \ No newline at end of file diff --git "a/markdown/online-ktv/\346\224\271\350\277\233/iOS/\347\213\254\345\224\261" "b/markdown/online-ktv/\346\224\271\350\277\233/iOS/\347\213\254\345\224\261" deleted file mode 100644 index e69de29bb2d..00000000000 diff --git "a/markdown/online-ktv/\346\224\271\350\277\233/iOS/\347\213\254\345\224\261.md" "b/markdown/online-ktv/\346\224\271\350\277\233/iOS/\347\213\254\345\224\261.md" new file mode 100644 index 00000000000..fbced22ad99 --- /dev/null +++ "b/markdown/online-ktv/\346\224\271\350\277\233/iOS/\347\213\254\345\224\261.md" @@ -0,0 +1,3 @@ +## 简介 + +本文介绍如何实现独唱场景。 \ No newline at end of file From af1cdb2dbf23fe0cf35dea2cc6ae7c1de9fe9d22 Mon Sep 17 00:00:00 2001 From: kelzr Date: Fri, 17 Feb 2023 15:52:53 +0800 Subject: [PATCH 05/20] =?UTF-8?q?[KTVI]=20=E7=8B=AC=E5=94=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../img/\345\220\210\345\224\261.drawio.svg" | 148 ++++++++++++++ .../img/\347\202\271\346\255\214.drawio.svg" | 0 .../img/\347\213\254\345\224\261.drawio.svg" | 28 +++ .../iOS/\347\202\271\346\255\214.md" | 2 +- .../iOS/\347\213\254\345\224\261.md" | 183 +++++++++++++++++- 5 files changed, 359 insertions(+), 2 deletions(-) create mode 100644 "markdown/online-ktv/\346\224\271\350\277\233/iOS/img/\345\220\210\345\224\261.drawio.svg" rename "markdown/online-ktv/\346\224\271\350\277\233/iOS/\345\220\210\345\224\261" => "markdown/online-ktv/\346\224\271\350\277\233/iOS/img/\347\202\271\346\255\214.drawio.svg" (100%) create mode 100644 "markdown/online-ktv/\346\224\271\350\277\233/iOS/img/\347\213\254\345\224\261.drawio.svg" diff --git "a/markdown/online-ktv/\346\224\271\350\277\233/iOS/img/\345\220\210\345\224\261.drawio.svg" "b/markdown/online-ktv/\346\224\271\350\277\233/iOS/img/\345\220\210\345\224\261.drawio.svg" new file mode 100644 index 00000000000..8f881ad5c7a --- /dev/null +++ "b/markdown/online-ktv/\346\224\271\350\277\233/iOS/img/\345\220\210\345\224\261.drawio.svg" @@ -0,0 +1,148 @@ + + + + + + + +
    +
    +
    + + 主唱 + +
    +
    +
    +
    + + 主唱 + +
    +
    + + + + +
    +
    +
    + + 播放器 + +
    +
    +
    +
    + + 播放器 + +
    +
    + + + + +
    +
    +
    + + 声网 SD-RTN + +
    +
    +
    +
    + + 声网 SD-RTN + +
    +
    + + + + +
    +
    +
    + + 伴唱 + +
    +
    +
    +
    + + 伴唱 + +
    +
    + + + + +
    +
    +
    + + 播放器 + +
    +
    +
    +
    + + 播放器 + +
    +
    + + + + +
    +
    +
    + + 连麦听众 + +
    +
    +
    +
    + + 连麦听众 + +
    +
    + + + + +
    +
    +
    + + 听众 + +
    +
    +
    +
    + + 听众 + +
    +
    + + +
    + + + + + Text is not SVG - cannot display + + + +
    \ No newline at end of file diff --git "a/markdown/online-ktv/\346\224\271\350\277\233/iOS/\345\220\210\345\224\261" "b/markdown/online-ktv/\346\224\271\350\277\233/iOS/img/\347\202\271\346\255\214.drawio.svg" similarity index 100% rename from "markdown/online-ktv/\346\224\271\350\277\233/iOS/\345\220\210\345\224\261" rename to "markdown/online-ktv/\346\224\271\350\277\233/iOS/img/\347\202\271\346\255\214.drawio.svg" diff --git "a/markdown/online-ktv/\346\224\271\350\277\233/iOS/img/\347\213\254\345\224\261.drawio.svg" "b/markdown/online-ktv/\346\224\271\350\277\233/iOS/img/\347\213\254\345\224\261.drawio.svg" new file mode 100644 index 00000000000..852233e46e1 --- /dev/null +++ "b/markdown/online-ktv/\346\224\271\350\277\233/iOS/img/\347\213\254\345\224\261.drawio.svg" @@ -0,0 +1,28 @@ + + + + + + + +
    +
    +
    +
    +
    +
    +
    +
    + +
    +
    +
    + + + + + Text is not SVG - cannot display + + + +
    \ No newline at end of file diff --git "a/markdown/online-ktv/\346\224\271\350\277\233/iOS/\347\202\271\346\255\214.md" "b/markdown/online-ktv/\346\224\271\350\277\233/iOS/\347\202\271\346\255\214.md" index 1324af21ab4..a843aef0097 100644 --- "a/markdown/online-ktv/\346\224\271\350\277\233/iOS/\347\202\271\346\255\214.md" +++ "b/markdown/online-ktv/\346\224\271\350\277\233/iOS/\347\202\271\346\255\214.md" @@ -1,6 +1,6 @@ ## 简介 -本文介绍如何实现点歌功能。用户需要在线上 K 歌前进行点歌。点歌指用户通过浏览榜单或搜索关键词选定想唱的正版音乐,然后下载播放音乐。只有上麦的用户可以点歌,观众不能点歌。 +本文介绍如何实现点歌功能。用户需要在唱歌前进行点歌。点歌指用户通过浏览榜单或搜索关键词选定想唱的正版音乐,然后下载播放音乐。 ## 示例项目 diff --git "a/markdown/online-ktv/\346\224\271\350\277\233/iOS/\347\213\254\345\224\261.md" "b/markdown/online-ktv/\346\224\271\350\277\233/iOS/\347\213\254\345\224\261.md" index fbced22ad99..71118855fb4 100644 --- "a/markdown/online-ktv/\346\224\271\350\277\233/iOS/\347\213\254\345\224\261.md" +++ "b/markdown/online-ktv/\346\224\271\350\277\233/iOS/\347\213\254\345\224\261.md" @@ -1,3 +1,184 @@ ## 简介 -本文介绍如何实现独唱场景。 \ No newline at end of file +本文介绍如何实现独唱功能。主唱点歌后,可以开始独唱,K 歌房内的听众都可以听到主唱唱歌。房间内想与主唱连麦语聊的听众可以上麦。 + +### 技术架构 + + + +## 实现方法(主唱) + +### 1. 获取歌词 + +调用 `getLyricWithSongCode`,SDK 会触发 `onLyricResult` 回调报告歌词的下载地址(`lyricUrl`)。 + +```objective-c +- (void)loadLyric:(NSInteger)songNo withCallback:(void (^ _Nullable)(NSString* lyricUrl))block { + NSString* requestId = [self.musicCenter getLyricWithSongCode:songNo lyricType:0]; + if ([requestId length] == 0) { + if (block) { + block(nil); + } + return; + } + [self.lyricCallbacks setObject:block forKey:requestId]; +} + +#pragma mark AgoraMusicContentCenterEventDelegate +- (void)onLyricResult:(nonnull NSString *)requestId + lyricUrl:(nonnull NSString *)lyricUrl { + LyricCallback callback = [self.lyricCallbacks objectForKey:requestId]; + if(!callback) { + return; + } + [self.lyricCallbacks removeObjectForKey:requestId]; + + if ([lyricUrl length] == 0) { + callback(nil); + return; + } + + callback(lyricUrl); +} +``` + +### 2. 发布音乐和人声 + +发布音乐和人声指发布音乐播放器的音频流和麦克风采集到主唱的音频流。主唱在频道内的角色为 AgoraClientRoleBroadcaster,因此 SDK 默认发布主唱的音频流。发布音乐播放器的音频流需要你先调用 `AgoraMusicPlayerProtocol.openMediaWithSongCode` 方法打开音乐资源,再通过 AgoraRtcChannelMediaOptions 对象设置 publishMediaPlayerAudioTrack 为 true 并在 publishMediaPlayerId 里传入播放器 ID。 + +```objective-c +- (void)playSong:(NSInteger)songCode +{ + KTVSingRole role = self.config.role; + KTVSongType type = self.config.type; + if(type == KTVSongTypeSolo) { + if(role == KTVSingRoleMainSinger) { + [self.rtcMediaPlayer openMediaWithSongCode:songCode startPos:0]; + AgoraRtcChannelMediaOptions* options = [AgoraRtcChannelMediaOptions new]; + options.autoSubscribeAudio = YES; + options.autoSubscribeVideo = YES; + options.publishMediaPlayerId = [self.rtcMediaPlayer getMediaPlayerId]; + options.publishMediaPlayerAudioTrack = YES; + [self.engine updateChannelWithMediaOptions:options]; + } else { + .... + } + } else { + .... + } +} +``` + +### 3. 发布音乐播放进度 + +自行定义 syncPlayerPosition 方法,在该方法中创建一个 dict 字典对象,用于存储三个键值对: + +- cmd:消息的命令。 +- duration:音乐文件的总时长。 +- time:音乐播放进度。 + +将 dict 字典对象序列化成 JSON 数据,然后调用 sendStreamMessage 将 JSON 数据发送到数据流中。用户接收到数据流,可以解析出音乐播放进度和总时长。 + +SDK 会在音乐播放时每秒触发一次 didChangedToPosition 回调,你可以通过该回调调用 syncPlayerPosition 方法,达到每秒同步音乐播放进度的效果。 + + +```objective-c +- (void)syncPlayerPosition:(NSInteger)position duration:(NSInteger)duration { + NSDictionary *dict = @{ + @"cmd":@"playerPosition", + @"duration":@(duration), + @"time":@(position) + }; + NSData *messageData = [NSJSONSerialization dataWithJSONObject:dict options:0 error:nil]; + int code = [self.engine sendStreamMessage:self.dataStreamId + data:messageData]; +} +- (void)AgoraRtcMediaPlayer:(id)playerKit didChangedToPosition:(NSInteger)position +{ + if (self.config.role == KTVSingRoleMainSinger) { + // 如果是独唱 + [self syncPlayerPosition:position duration:[playerKit getDuration]]; + } +} +``` + +### 4.(可选)关闭麦克风 + +主唱停止唱歌或希望暂时关闭麦克风时,可以调用 adjustRecordingSignalVolume,将音频采集信号音量设置为 0。 + +```objective-c +[self.RTCkit adjustRecordingSignalVolume:isNowMicMuted ? 0 : 100]; +``` + +## 实现方法(听众) + +### 1. 订阅主唱音频流 + +听众需要将 autoSubscribeAudio 设为 YES,以订阅主唱发布的音乐和人声。为了不影响主唱的音乐播放,听众需要确保本地不播放音乐,即将 publishMediaPlayerAudioTrack 设为 NO。 + +听众的用户角色为 AgoraClientRoleAudience,因此无法在频道内发布音频流。如果听众想上麦与主唱语聊,需要将用户角色修改为 AgoraClientRoleBroadcaster。修改角色后,SDK 默认发布该连麦听众的音频流,主唱和其他听众都能听到连麦听众的声音。 + +```objective-c +- (void)playSong:(NSInteger)songCode +{ + KTVSingRole role = self.config.role; + KTVSongType type = self.config.type; + if(type == KTVSongTypeSolo) { + if(role == KTVSingRoleMainSinger) { + .... + } else { + AgoraRtcChannelMediaOptions* options = [AgoraRtcChannelMediaOptions new]; + options.autoSubscribeAudio = YES; + options.autoSubscribeVideo = YES; + options.publishMediaPlayerAudioTrack = NO; + [self.engine updateChannelWithMediaOptions:options]; + } + } else { + .... + } +} +``` + +### 2. 歌词同步 + +听众通过 receiveStreamMessageFromUid 回调接收数据流,并在回调中通过自行定义的 dictionaryForJsonData 方法将接收到的数据转换为字典对象。当字典中的 cmd 值为 setLrcTime 时,将字典中的 time 值转换成整数并赋值给 remotePlayerPosition,将字典中的 duration 值转换成整数并赋值给 remotePlayerDuration,从而解析出主唱同步的音乐播放进度和总时长。最后,歌词组件通过音乐播放进度和总时长,在歌词界面中渲染出代表歌词播放进度的视图。 + +```objective-c +- (void)setLrcView:(AgoraLrcScoreView *)lrcView +{ + _lrcView = lrcView; + lrcView.delegate = self; +} + + +#pragma mark - AgoraLrcViewDelegate +- (NSTimeInterval)getTotalTime { + if (self.config.role == KTVSingRoleMainSinger) { + NSTimeInterval time = [_rtcMediaPlayer getDuration]; + return time; + } + return self.remotePlayerDuration; +} + +- (NSTimeInterval)getPlayerCurrentTime { + if (self.config.role == KTVSingRoleMainSinger) { + NSTimeInterval time = [_rtcMediaPlayer getPosition]; + return time; + } + + return self.remotePlayerPosition; +} + +#pragma mark - AgoraRtcEngineDelegate +- (void)rtcEngine:(AgoraRtcEngineKit *)engine receiveStreamMessageFromUid:(NSUInteger)uid streamId:(NSInteger)streamId data:(NSData *)data +{ + NSDictionary *dict = [VLGlobalHelper dictionaryForJsonData:data]; + if ([dict[@"cmd"] isEqualToString:@"setLrcTime"]) { // 同步歌词 + NSInteger position = [dict[@"time"] integerValue]; + NSInteger duration = [dict[@"duration"] integerValue]; + self.remotePlayerPosition = position; + self.remotePlayerDuration = duration; + } +} +``` + From 312dd7b7b65df74882e6e2fb0a5827d922ac4026 Mon Sep 17 00:00:00 2001 From: kelzr Date: Fri, 17 Feb 2023 15:53:20 +0800 Subject: [PATCH 06/20] =?UTF-8?q?[KTVI]=20=E5=90=88=E5=94=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../iOS/\345\220\210\345\224\261.md" | 299 ++++++++++++++++++ 1 file changed, 299 insertions(+) create mode 100644 "markdown/online-ktv/\346\224\271\350\277\233/iOS/\345\220\210\345\224\261.md" diff --git "a/markdown/online-ktv/\346\224\271\350\277\233/iOS/\345\220\210\345\224\261.md" "b/markdown/online-ktv/\346\224\271\350\277\233/iOS/\345\220\210\345\224\261.md" new file mode 100644 index 00000000000..291ec4b15f4 --- /dev/null +++ "b/markdown/online-ktv/\346\224\271\350\277\233/iOS/\345\220\210\345\224\261.md" @@ -0,0 +1,299 @@ +## 简介 + +本文介绍如何实现合唱功能。主唱点歌开唱后,伴唱可以和主唱一起唱歌,K 歌房内的听众都可以听到合唱。房间内想与主唱或伴唱连麦语聊的听众可以上麦。 + +### 技术架构 + + + +相比独唱,合唱场景中增加了一位或多位伴唱。主唱在发布音乐和人声的音频合流之外,还需要再发布一路人声的音频流,以供伴唱接收,方便伴唱跟随主唱人声进行合唱。 + + + +## 实现方法(主唱) + +### 1. 设置私有参数 + + +```objective-c +-(void)initializeEngine { + .... + // 设置声网私有参数,方便主唱通过 NTP 时间与伴唱进行实时进度同步 + [engine setParameters:@"{\"rtc.ntp_delay_drop_threshold\":1000}"]; + [engine setParameters:@"{\"che.audio.agc.enable\": true}"]; + [engine setParameters:@"{\"rtc.video.enable_sync_render_ntp\": true}"]; + [engine setParameters:@"{\"rtc.net.maxS2LDelay\": 800}"]; +} +``` + +### 2. 开始合唱 + +```objective-c +- (void)playSong:(NSInteger)songCode +{ + KTVSingRole role = self.config.role; + KTVSongType type = self.config.type; + if(type == KTVSongTypeSolo) { + .... + } else { + if(role == KTVSingRoleMainSinger) { + [self.rtcMediaPlayer openMediaWithSongCode:songCode startPos:0]; + AgoraRtcChannelMediaOptions* options = [AgoraRtcChannelMediaOptions new]; + options.autoSubscribeAudio = YES; + options.autoSubscribeVideo = YES; + options.publishMediaPlayerId = [self.rtcMediaPlayer getMediaPlayerId]; + options.publishMediaPlayerAudioTrack = YES; + options.publishMicrophoneTrack = YES; + // 合唱场景下,主唱端开启本地音频录制和采集 + options.enableAudioRecordingOrPlayout = YES; + [self.engine updateChannelWithMediaOptions:options]; + [self joinChorus2ndChannel]; + + // 调整播放器音量 + [self.rtcMediaPlayer adjustPlayoutVolume:50]; + [self.rtcMediaPlayer adjustPublishSignalVolume:50]; + } else if(role == KTVSingRoleCoSinger) { + .... + } else { + .... + } + } +} + +- (void)joinChorus2ndChannel +{ + if(self.subChorusConnection) { + KTVLogWarn(@"joinChorus2ndChannel fail! rejoin!"); + return; + } + + KTVSingRole role = self.config.role; + AgoraRtcChannelMediaOptions* options = [AgoraRtcChannelMediaOptions new]; + // 主唱不订阅 2nd 频道的音频流 + // 伴唱订阅 2nd 频道的音频流 + options.autoSubscribeAudio = role == KTVSingRoleMainSinger ? NO : YES; + options.autoSubscribeVideo = NO; + options.publishMicrophoneTrack = NO; + // 主唱不开启 2nd 频道的本地录音和播放 + // 伴唱开启 2nd 频道的本地录音和播放 + options.enableAudioRecordingOrPlayout = role == KTVSingRoleMainSinger ? NO : YES; + options.clientRoleType = AgoraClientRoleBroadcaster; + // 主唱在 2nd 频道里发布自定义音频流 + // 伴唱在 2nd 频道里不发布自定义音频流 + options.publishDirectCustomAudioTrack = role == KTVSingRoleMainSinger ? YES : NO;; + + AgoraRtcConnection* connection = [AgoraRtcConnection new]; + connection.channelId = [NSString stringWithFormat:@"%@_ex", self.channelName]; + connection.localUid = [VLLoginModel mediaPlayerUidWithUid:VLUserCenter.user.id]; + self.subChorusConnection = connection; + + VL(weakSelf); + // 设置自定义音频源 + [self.engine setDirectExternalAudioSource:YES]; + [self.engine setAudioFrameDelegate:self]; + // 设置音频属性为合唱 + [self.engine setAudioScenario:AgoraAudioScenarioChorus]; + // 加入 2nd 频道 + [self.engine joinChannelExByToken:VLUserCenter.user.agoraPlayerRTCToken connection:connection delegate:self mediaOptions:options joinSuccess:^(NSString * _Nonnull channel, NSUInteger uid, NSInteger elapsed) { + // 对合唱场景下的主唱端,设置 pushDirectAudioEnable 为 YES + if(weakSelf.config.type == KTVSongTypeChorus && + weakSelf.config.role == KTVSingRoleMainSinger) { + weakSelf.pushDirectAudioEnable = YES; + } + + [weakSelf updateRemotePlayBackVolumeIfNeed]; + }]; +} + +// 根据音乐播放情况,设置主唱、伴唱、听众的本地播放音量 +- (void)updateRemotePlayBackVolumeIfNeed { + // 对合唱场景下的听众端,本地播放音乐音量设为 100 + if (self.config.type != KTVSongTypeChorus || self.config.role == KTVSingRoleAudience) { + [self.engine adjustPlaybackSignalVolume:100]; + return; + } + + // 对合唱场景下的主唱或伴唱端,如果音乐播放器状态为 AgoraMediaPlayerStatePlaying,则本地播放音乐音量设为与远端伴唱或主唱的音量一致 + int volume = self.playerState == AgoraMediaPlayerStatePlaying ? self.chorusRemoteUserVolume : 100; + if (self.config.role == KTVSingRoleMainSinger) { + [self.engine adjustPlaybackSignalVolume:volume]; + } else if (self.config.role == KTVSingRoleCoSinger) { + [self.engine adjustPlaybackSignalVolume:volume]; + } +} + +#pragma mark - AgoraAudioFrameDelegate +- (BOOL)onRecordAudioFrame:(AgoraAudioFrame *)frame channelId:(NSString *)channelId +{ + // 如果是合唱场景下的主唱端,发送自定义音频数据给 SDK + if(self.pushDirectAudioEnable) { + // 警告:你必须在成功加入 2nd 频道后调用 pushDirectAudioFrameRawData,否则 SDK 可能卡死 + [self.engine pushDirectAudioFrameRawData:frame.buffer samples:frame.channels*frame.samplesPerChannel sampleRate:frame.samplesPerSec channels:frame.channels]; + } + return true; +} + +#pragma RTC delegate for chorus channel2 +- (void)rtcEngine:(AgoraRtcEngineKit *)engine didLeaveChannelWithStats:(AgoraChannelStats *)stats +{ + [engine setAudioScenario:AgoraAudioScenarioGameStreaming]; +} +``` + +### 3. 同步播放进度 + +```objective-c +// 获取音乐总时长 +- (NSTimeInterval)getTotalTime { + if (self.config.role == KTVSingRoleMainSinger) { + NSTimeInterval time = self.playerDuration; + return time; + } + .... +} + +// 获取主唱和伴唱的音乐播放进度 +- (NSTimeInterval)getPlayerCurrentTime { + if (self.config.role == KTVSingRoleMainSinger || self.config.role == KTVSingRoleCoSinger) { + NSTimeInterval time = uptime() - self.localPlayerPosition; + return time; + } + + .... +} + +- (NSInteger)playerDuration { + if (_playerDuration == 0) { + _playerDuration = [_rtcMediaPlayer getDuration]; + } + + return _playerDuration; +} + +- (void)AgoraRtcMediaPlayer:(id)playerKit didChangedToPosition:(NSInteger)position +{ + self.localPlayerPosition = uptime() - position; + // audioPlayoutDelay 指音频播放延迟,即从音频数据发送至接收端,到数据在接收端开始播放所需的时间。 + // 如果是合唱场景下的主播,且播放进度大于音频播放延迟,则通过发送数据流来同步进度。 + if (self.config.role == KTVSingRoleMainSinger && position > self.audioPlayoutDelay) { + NSDictionary *dict = @{ + @"cmd":@"setLrcTime", + @"duration":@(self.playerDuration), + @"time":@(position - self.audioPlayoutDelay), + @"ntp":@([self getNtpTimeInMs]), + @"playerState":@(self.playerState) + }; + [self sendStreamMessageWithDict:dict success:nil]; + } + + .... +} +``` + +### 4.(可选)退出合唱 + +```objective-c +- (void)leaveChorus2ndChannel +{ + if(self.subChorusConnection == nil) { + KTVLogWarn(@"leaveChorus2ndChannel fail connection = nil"); + return; + } + + [self.engine setDirectExternalAudioSource:NO]; + [self.engine setAudioFrameDelegate:nil]; + + KTVSingRole role = self.config.role; + if(role == KTVSingRoleMainSinger) { + AgoraRtcChannelMediaOptions* options = [AgoraRtcChannelMediaOptions new]; + options.publishDirectCustomAudioTrack = NO; + [self.engine updateChannelExWithMediaOptions:options connection:self.subChorusConnection]; + [self.engine leaveChannelEx:self.subChorusConnection leaveChannelBlock:nil]; + } else if(role == KTVSingRoleCoSinger) { + .... + } +} +``` + + +## 实现方法(伴唱) + +### 1. 加入合唱 + +```objective-c +- (void)playSong:(NSInteger)songCode +{ + KTVSingRole role = self.config.role; + KTVSongType type = self.config.type; + if(type == KTVSongTypeSolo) { + .... + } else { + if(role == KTVSingRoleMainSinger) { + .... + } else if(role == KTVSingRoleCoSinger) { + [self.rtcMediaPlayer openMediaWithSongCode:songCode startPos:0]; + AgoraRtcChannelMediaOptions* options = [AgoraRtcChannelMediaOptions new]; + options.autoSubscribeAudio = YES; + options.autoSubscribeVideo = YES; + // 合唱场景下,伴唱不发布播放器音乐 + options.publishMediaPlayerAudioTrack = NO; + [self.engine updateChannelWithMediaOptions:options]; + [self joinChorus2ndChannel]; + + [self.rtcMediaPlayer adjustPlayoutVolume:50]; + [self.rtcMediaPlayer adjustPublishSignalVolume:50]; + } else { + .... + } + } +} + +- (void)joinChorus2ndChannel +{ + if(self.subChorusConnection) { + KTVLogWarn(@"joinChorus2ndChannel fail! rejoin!"); + return; + } + + KTVSingRole role = self.config.role; + AgoraRtcChannelMediaOptions* options = [AgoraRtcChannelMediaOptions new]; + // 主唱不订阅 2nd 频道的音频流 + // 伴唱订阅 2nd 频道的音频流 + options.autoSubscribeAudio = role == KTVSingRoleMainSinger ? NO : YES; + options.autoSubscribeVideo = NO; + options.publishMicrophoneTrack = NO; + // 主唱不开启 2nd 频道的本地录音和播放 + // 伴唱开启 2nd 频道的本地录音和播放 + options.enableAudioRecordingOrPlayout = role == KTVSingRoleMainSinger ? NO : YES; + options.clientRoleType = AgoraClientRoleBroadcaster; + // 主唱在 2nd 频道里发布自定义音频流 + // 伴唱在 2nd 频道里不发布自定义音频流 + options.publishDirectCustomAudioTrack = role == KTVSingRoleMainSinger ? YES : NO;; + + AgoraRtcConnection* connection = [AgoraRtcConnection new]; + connection.channelId = [NSString stringWithFormat:@"%@_ex", self.channelName]; + connection.localUid = [VLLoginModel mediaPlayerUidWithUid:VLUserCenter.user.id]; + self.subChorusConnection = connection; + + VL(weakSelf); + // 设置自定义音频源 + [self.engine setDirectExternalAudioSource:YES]; + [self.engine setAudioFrameDelegate:self]; + // 设置音频属性为合唱 + [self.engine setAudioScenario:AgoraAudioScenarioChorus]; + [self.engine joinChannelExByToken:VLUserCenter.user.agoraPlayerRTCToken connection:connection delegate:self mediaOptions:options joinSuccess:^(NSString * _Nonnull channel, NSUInteger uid, NSInteger elapsed) { + .... + }]; +} + + +#pragma RTC delegate for chorus channel2 + +- (void)rtcEngine:(AgoraRtcEngineKit *)engine didLeaveChannelWithStats:(AgoraChannelStats *)stats +{ + [self.engine setAudioScenario:AgoraAudioScenarioGameStreaming]; +} +``` + +## 实现方法(观众) + From 49d2e69f0fe0c899a226f90c4757e04d2e16c571 Mon Sep 17 00:00:00 2001 From: kelzr Date: Mon, 20 Feb 2023 10:53:05 +0800 Subject: [PATCH 07/20] =?UTF-8?q?[KTVI]=20=E5=90=88=E5=94=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../iOS/\345\220\210\345\224\261.md" | 98 ++++++++++++++++++- 1 file changed, 93 insertions(+), 5 deletions(-) diff --git "a/markdown/online-ktv/\346\224\271\350\277\233/iOS/\345\220\210\345\224\261.md" "b/markdown/online-ktv/\346\224\271\350\277\233/iOS/\345\220\210\345\224\261.md" index 291ec4b15f4..cb83ab86c6f 100644 --- "a/markdown/online-ktv/\346\224\271\350\277\233/iOS/\345\220\210\345\224\261.md" +++ "b/markdown/online-ktv/\346\224\271\350\277\233/iOS/\345\220\210\345\224\261.md" @@ -16,7 +16,7 @@ ```objective-c --(void)initializeEngine { +- (void)initializeEngine { .... // 设置声网私有参数,方便主唱通过 NTP 时间与伴唱进行实时进度同步 [engine setParameters:@"{\"rtc.ntp_delay_drop_threshold\":1000}"]; @@ -49,7 +49,7 @@ [self.engine updateChannelWithMediaOptions:options]; [self joinChorus2ndChannel]; - // 调整播放器音量 + // 调整播放器本地和远端所听见的音量 [self.rtcMediaPlayer adjustPlayoutVolume:50]; [self.rtcMediaPlayer adjustPublishSignalVolume:50]; } else if(role == KTVSingRoleCoSinger) { @@ -173,7 +173,7 @@ - (void)AgoraRtcMediaPlayer:(id)playerKit didChangedToPosition:(NSInteger)position { self.localPlayerPosition = uptime() - position; - // audioPlayoutDelay 指音频播放延迟,即从音频数据发送至接收端,到数据在接收端开始播放所需的时间。 + // audioPlayoutDelay 是音频播放延迟,即从音频数据发送至接收端,到数据在接收端开始播放所需的时间。 // 如果是合唱场景下的主播,且播放进度大于音频播放延迟,则通过发送数据流来同步进度。 if (self.config.role == KTVSingRoleMainSinger && position > self.audioPlayoutDelay) { NSDictionary *dict = @{ @@ -288,12 +288,100 @@ #pragma RTC delegate for chorus channel2 - - (void)rtcEngine:(AgoraRtcEngineKit *)engine didLeaveChannelWithStats:(AgoraChannelStats *)stats { [self.engine setAudioScenario:AgoraAudioScenarioGameStreaming]; } ``` -## 实现方法(观众) +### 2. 播放音乐并记录关键时间 + +```objective-c +- (void)AgoraRtcMediaPlayer:(id)playerKit didChangedToPosition:(NSInteger)position +{ + // localPlayerPosition 是系统时间减去播放进度,即播放器开始播放的时间 + self.localPlayerPosition = uptime() - position; + + .... +} + +// 获取伴唱的音乐播放器的音频播放延迟,即从音频数据发送至接收端,到数据在接收端开始播放所需的时间。 +- (void)rtcEngine:(AgoraRtcEngineKit *)engine localAudioStats:(AgoraRtcLocalAudioStats *)stats { + self.audioPlayoutDelay = stats.audioPlayoutDelay; +} +``` + +### 3. 校准播放进度 + +通过 receiveStreamMessageFromUid 回调让伴唱接收主唱的数据流。在该回调中,从接收到的数据解析出主唱的音乐播放进度、音乐总时长、NTP 时间、播放状态。通过 seekToPosition 方法将伴唱的播放进度朝着主唱的播放进度校准。 + +```objective-c +- (void)rtcEngine:(AgoraRtcEngineKit *)engine receiveStreamMessageFromUid:(NSUInteger)uid streamId:(NSInteger)streamId data:(NSData *)data +{ + // 将数据转换为字典 + NSDictionary *dict = [VLGlobalHelper dictionaryForJsonData:data]; + // 如果 cmd 是 playerPosition + if ([dict[@"cmd"] isEqualToString:@"playerPosition"]) { + // 从字典中获取主唱的播放进度、总时长、NTP 时间、播放状态 + NSInteger position = [dict[@"time"] integerValue]; + NSInteger duration = [dict[@"duration"] integerValue]; + NSInteger remoteNtp = [dict[@"ntp"] integerValue]; + AgoraMediaPlayerState state = [dict[@"playerState"] integerValue]; + // 如果当前播放和 cmd 中不同,更新伴唱的播放状态 + if (self.playerState != state) { + self.playerState = state; + [self updateCosingerPlayerStatusIfNeed]; + [self.delegate controller:self song:self.config.songCode didChangedToState:state local:NO]; + } + + self.remotePlayerPosition = position; + self.remotePlayerDuration = duration; + // 对伴唱,如果音乐播放器正在播放,那么当伴唱的播放进度与主唱的播放进度差距大于 40 ms 时,伴唱音乐播放器通过 seekToPosition 方法调整播放进度。 + if(self.config.type == KTVSongTypeChorus && self.config.role == KTVSingRoleCoSinger) { + if([self.rtcMediaPlayer getPlayerState] == AgoraMediaPlayerStatePlaying) { + NSInteger localNtpTime = [self getNtpTimeInMs]; + NSInteger localPosition = uptime() - self.localPlayerPosition; + NSInteger expectPosition = position + localNtpTime - remoteNtp + self.audioPlayoutDelay; + NSInteger threshold = expectPosition - localPosition; + if(labs(threshold) > 40) { + [self.rtcMediaPlayer seekToPosition:expectPosition]; + } + } + } + } +} +``` + +### 4.(可选)退出合唱 + +退出合唱时,需要结束音频自采集。伴唱还要调用 leaveChannelEx 离开 2nd 频道,恢复订阅主唱的 mixer 音频,按需调节播放器在本地和远端所听见的音量。 + +```objective-c +- (void)leaveChorus2ndChannel +{ + if(self.subChorusConnection == nil) { + return; + } + + [self.engine setDirectExternalAudioSource:NO]; + [self.engine setAudioFrameDelegate:nil]; + + KTVSingRole role = self.config.role; + if(role == KTVSingRoleMainSinger) { + .... + } else if(role == KTVSingRoleCoSinger) { + [self.engine leaveChannelEx:self.subChorusConnection leaveChannelBlock:nil]; + [self.engine muteRemoteAudioStream:self.config.mainSingerUid mute:NO]; + } + + [self adjustPlayoutVolume:self.playoutVolume]; + [self adjustPublishSignalVolume:self.publishSignalVolume]; + self.pushDirectAudioEnable = NO; + self.subChorusConnection = nil; +} +``` + +## 实现方法(听众) + +相比独唱场景下的听众,合唱场景下的听众只需要再订阅一路伴唱的人声。 From 9574605c1d04ea90459115a7c15a14435b9859df Mon Sep 17 00:00:00 2001 From: kelzr Date: Tue, 28 Feb 2023 18:13:50 +0800 Subject: [PATCH 08/20] =?UTF-8?q?[KTVI]=20=E5=90=88=E5=94=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../iOS/\345\220\210\345\224\261.md" | 61 +++++++++++++++---- .../iOS/\347\202\271\346\255\214.md" | 27 +++++++- .../iOS/\347\213\254\345\224\261.md" | 40 +++++++----- 3 files changed, 99 insertions(+), 29 deletions(-) diff --git "a/markdown/online-ktv/\346\224\271\350\277\233/iOS/\345\220\210\345\224\261.md" "b/markdown/online-ktv/\346\224\271\350\277\233/iOS/\345\220\210\345\224\261.md" index cb83ab86c6f..ee123758d79 100644 --- "a/markdown/online-ktv/\346\224\271\350\277\233/iOS/\345\220\210\345\224\261.md" +++ "b/markdown/online-ktv/\346\224\271\350\277\233/iOS/\345\220\210\345\224\261.md" @@ -8,17 +8,18 @@ 相比独唱,合唱场景中增加了一位或多位伴唱。主唱在发布音乐和人声的音频合流之外,还需要再发布一路人声的音频流,以供伴唱接收,方便伴唱跟随主唱人声进行合唱。 - +// TODO ## 实现方法(主唱) ### 1. 设置私有参数 +实时合唱场景对低延时和音质的要求很高。开启合唱前,你需要在初始化引擎的方法里设置私有参数,具体设置请参考如下示例代码。 ```objective-c - (void)initializeEngine { .... - // 设置声网私有参数,方便主唱通过 NTP 时间与伴唱进行实时进度同步 + // 设置声网私有参数,方便主唱通过 NTP 时间与伴唱进行实时进度同步 [engine setParameters:@"{\"rtc.ntp_delay_drop_threshold\":1000}"]; [engine setParameters:@"{\"che.audio.agc.enable\": true}"]; [engine setParameters:@"{\"rtc.video.enable_sync_render_ntp\": true}"]; @@ -28,6 +29,14 @@ ### 2. 开始合唱 +本节展示主唱端开始合唱的代码逻辑: + +- 发布播放器和人声的音频混流,让 K 歌房内听众听到。 +- 在另一个频道(即示例代码中的 2nd 频道)中发布自定义音频,并将人声作为自定义音源,以让 2nd 频道内的伴唱能跟唱。 +- 为了更好的合唱音频效果,调节音频参数: + - 合唱场景使用 AgoraAudioScenarioChorus 音频属性,退出合唱时调整为 AgoraAudioScenarioGameStreaming 音频属性。 + - 按需调节合唱场景下主唱、伴唱、听众端的本地播放音量。 + ```objective-c - (void)playSong:(NSInteger)songCode { @@ -42,15 +51,18 @@ options.autoSubscribeAudio = YES; options.autoSubscribeVideo = YES; options.publishMediaPlayerId = [self.rtcMediaPlayer getMediaPlayerId]; + // 发布播放器音乐 options.publishMediaPlayerAudioTrack = YES; + // 发布主唱人声 options.publishMicrophoneTrack = YES; // 合唱场景下,主唱端开启本地音频录制和采集 options.enableAudioRecordingOrPlayout = YES; [self.engine updateChannelWithMediaOptions:options]; [self joinChorus2ndChannel]; - // 调整播放器本地和远端所听见的音量 + // 调整播放器在本地所听见的音量 [self.rtcMediaPlayer adjustPlayoutVolume:50]; + // 调节播放器在远端所听见的音量 [self.rtcMediaPlayer adjustPublishSignalVolume:50]; } else if(role == KTVSingRoleCoSinger) { .... @@ -60,6 +72,7 @@ } } +// 加入 2nd 频道 - (void)joinChorus2ndChannel { if(self.subChorusConnection) { @@ -113,7 +126,7 @@ return; } - // 对合唱场景下的主唱或伴唱端,如果音乐播放器状态为 AgoraMediaPlayerStatePlaying,则本地播放音乐音量设为与远端伴唱或主唱的音量一致 + // 如果音乐播放器状态为 AgoraMediaPlayerStatePlaying,则将合唱场景下的主唱或伴唱本地播放音乐音量分别设为与远端伴唱或主唱的音量一致 int volume = self.playerState == AgoraMediaPlayerStatePlaying ? self.chorusRemoteUserVolume : 100; if (self.config.role == KTVSingRoleMainSinger) { [self.engine adjustPlaybackSignalVolume:volume]; @@ -142,8 +155,12 @@ ### 3. 同步播放进度 +本节展示主唱通过 sendStreamMessageWithDict 方法同步音乐播放器的进度、状态、NPT 时间等字典信息。 + +**Note**:audioPlayoutDelay 是音频播放延迟,即从音频数据发送至接收端,到数据在接收端开始播放所需的时间。因为不同机型设备上的 audioPlayoutDelay 不同,主唱端和听众端可能使用不同的机型设备,所以在发布主唱的音乐播放进度时,声网建议在主唱端将进度减去 audioPlayoutDelay,在接收端计算时再加上接收端的 audioPlayoutDelay。 + ```objective-c -// 获取音乐总时长 +// 获取主唱的音乐总时长 - (NSTimeInterval)getTotalTime { if (self.config.role == KTVSingRoleMainSinger) { NSTimeInterval time = self.playerDuration; @@ -173,8 +190,8 @@ - (void)AgoraRtcMediaPlayer:(id)playerKit didChangedToPosition:(NSInteger)position { self.localPlayerPosition = uptime() - position; + // 如果当前角色是合唱场景下的主播,且播放进度大于音频播放延迟,则通过发送数据流来同步播放进度。 // audioPlayoutDelay 是音频播放延迟,即从音频数据发送至接收端,到数据在接收端开始播放所需的时间。 - // 如果是合唱场景下的主播,且播放进度大于音频播放延迟,则通过发送数据流来同步进度。 if (self.config.role == KTVSingRoleMainSinger && position > self.audioPlayoutDelay) { NSDictionary *dict = @{ @"cmd":@"setLrcTime", @@ -192,6 +209,8 @@ ### 4.(可选)退出合唱 +本节展示主唱退出 2nd 频道并关闭自定义音频。 + ```objective-c - (void)leaveChorus2ndChannel { @@ -215,11 +234,18 @@ } ``` - ## 实现方法(伴唱) ### 1. 加入合唱 +本节展示伴唱端加入合唱的代码逻辑: + +- 发布人声的音频流,让 K 歌房内听众听到。 +- 在另一个频道(即示例代码中的 2nd 频道)中订阅自定义音频流,即订阅主唱人声的音频流。伴唱可以参考主唱人声开始跟唱。 +- 为了更好的合唱音频效果,调节音频参数: + - 合唱场景使用 AgoraAudioScenarioChorus 音频属性,退出合唱时调整为 AgoraAudioScenarioGameStreaming 音频属性。 + - 按需调节合唱场景下主唱、伴唱、听众端的本地播放音量。 + ```objective-c - (void)playSong:(NSInteger)songCode { @@ -268,7 +294,7 @@ options.clientRoleType = AgoraClientRoleBroadcaster; // 主唱在 2nd 频道里发布自定义音频流 // 伴唱在 2nd 频道里不发布自定义音频流 - options.publishDirectCustomAudioTrack = role == KTVSingRoleMainSinger ? YES : NO;; + options.publishDirectCustomAudioTrack = role == KTVSingRoleMainSinger ? YES : NO; AgoraRtcConnection* connection = [AgoraRtcConnection new]; connection.channelId = [NSString stringWithFormat:@"%@_ex", self.channelName]; @@ -294,7 +320,9 @@ } ``` -### 2. 播放音乐并记录关键时间 +### 2. 本地播放音乐并记录关键时间 + +伴唱唱歌时需要在本地播放音乐,避免将音乐发布到频道内。播放音乐时,通过 localPlayerPosition 记录伴唱音乐播放器的开始播放的时间。通过 localAudioStats 获取音频播放延迟,以备后续校准播放进度时使用。 ```objective-c - (void)AgoraRtcMediaPlayer:(id)playerKit didChangedToPosition:(NSInteger)position @@ -313,7 +341,11 @@ ### 3. 校准播放进度 -通过 receiveStreamMessageFromUid 回调让伴唱接收主唱的数据流。在该回调中,从接收到的数据解析出主唱的音乐播放进度、音乐总时长、NTP 时间、播放状态。通过 seekToPosition 方法将伴唱的播放进度朝着主唱的播放进度校准。 +通过 receiveStreamMessageFromUid 回调让伴唱接收主唱的数据流。在该回调中,从接收到的数据解析出主唱的音乐播放进度、音乐总时长、NTP 时间、播放状态。 + +当计算出伴唱的预期播放进度于主唱的实际播放进度差距大于 40 ms 时,通过 seekToPosition 方法将伴唱的播放进度朝着主唱的播放进度校准。 + +**Note**:audioPlayoutDelay 是音频播放延迟,即从音频数据发送至接收端,到数据在接收端开始播放所需的时间。因为不同机型设备上的 audioPlayoutDelay 不同,主唱端和听众端可能使用不同的机型设备,所以在发布主唱的音乐播放进度时,声网建议在主唱端将进度减去 audioPlayoutDelay,在接收端计算时再加上接收端的 audioPlayoutDelay。 ```objective-c - (void)rtcEngine:(AgoraRtcEngineKit *)engine receiveStreamMessageFromUid:(NSUInteger)uid streamId:(NSInteger)streamId data:(NSData *)data @@ -354,7 +386,7 @@ ### 4.(可选)退出合唱 -退出合唱时,需要结束音频自采集。伴唱还要调用 leaveChannelEx 离开 2nd 频道,恢复订阅主唱的 mixer 音频,按需调节播放器在本地和远端所听见的音量。 +本节展示伴唱退出合唱的代码逻辑。伴唱需要结束自定义音频,调用 leaveChannelEx 离开 2nd 频道,并恢复订阅主唱的音乐和人声的音频混流,最后按需调节播放器在本地和远端所听见的音量。 ```objective-c - (void)leaveChorus2ndChannel @@ -383,5 +415,10 @@ ## 实现方法(听众) -相比独唱场景下的听众,合唱场景下的听众只需要再订阅一路伴唱的人声。 +不管是合唱还是独唱场景,听众的代码逻辑都是一样的,主要分为两部分: + +- 订阅频道内的音频流。 +- 歌词同步。 + +示例代码请参考独唱场景下[实现方法(听众)](#audience)。 diff --git "a/markdown/online-ktv/\346\224\271\350\277\233/iOS/\347\202\271\346\255\214.md" "b/markdown/online-ktv/\346\224\271\350\277\233/iOS/\347\202\271\346\255\214.md" index a843aef0097..5664ad0e2a0 100644 --- "a/markdown/online-ktv/\346\224\271\350\277\233/iOS/\347\202\271\346\255\214.md" +++ "b/markdown/online-ktv/\346\224\271\350\277\233/iOS/\347\202\271\346\255\214.md" @@ -200,19 +200,42 @@ typedef void (^LoadMusicCallback)(AgoraMusicContentCenterPreloadStatus); ### 4. 打开并播放音乐资源 -调用 `AgoraMusicPlayerProtocol.openMediaWithSongCode` 打开已经预加载完的音乐资源。收到 `AgoraRtcMediaPlayerDelegate.didChangedToState` 回调报告的音乐播放器状态为 `AgoraMediaPlayerStateOpenCompleted(5)` 后,调用 `AgoraRtcMediaPlayerProtocol.play` 播放音乐。 +调用 `AgoraMusicPlayerProtocol.openMediaWithSongCode` 打开已经预加载完的音乐资源。通过 `AgoraRtcMediaPlayerDelegate.didChangedToState` 回调监听音乐播放器的状态变化,并作出相应处理: + - 状态为 AgoraMediaPlayerStateOpenCompleted 时,将系统时间作为播放器开始播放的时间,将 0 作为播放进度。对主唱,使用 dispatch_after 函数在 0.5 秒后在主线程中调用 play 播放音乐。推迟 0.5 秒播放可以避免对齐问题。//TODO + - 状态为 AgoraMediaPlayerStateStopped 和 AgoraMediaPlayerStatePlaying 时,将系统时间作为播放器开始播放的时间,将 0 作为播放进度。 + + +收到 `AgoraRtcMediaPlayerDelegate.didChangedToState` 回调报告的音乐播放器状态为 `AgoraMediaPlayerStateOpenCompleted(5)` 后,调用 `AgoraRtcMediaPlayerProtocol.play` 播放音乐。 ```objective-c - (void)openMusicMedia:(NSInteger)songCode{ [self.rtcMediaPlayer openMediaWithSongCode:songCode startPos:0]; } + #pragma mark - AgoraRtcMediaPlayerDelegate - (void)AgoraRtcMediaPlayer:(id)playerKit didChangedToState:(AgoraMediaPlayerState)state error:(AgoraMediaPlayerError)error { if (state == AgoraMediaPlayerStateOpenCompleted) { - [player play]; + self.localPlayerPosition = uptime(); + self.playerDuration = 0; + if (self.config.role == KTVSingRoleMainSinger) { + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + [playerKit play]; + }); + } + } else if (state == AgoraMediaPlayerStateStopped) { + self.localPlayerPosition = uptime(); + self.playerDuration = 0; + } else if (state == AgoraMediaPlayerStatePlaying) { + self.localPlayerPosition = uptime(); + self.playerDuration = 0; + } + if (self.config.role == KTVSingRoleMainSinger) { + [self syncPlayState:state]; } + self.playerState = state; + .... } ``` diff --git "a/markdown/online-ktv/\346\224\271\350\277\233/iOS/\347\213\254\345\224\261.md" "b/markdown/online-ktv/\346\224\271\350\277\233/iOS/\347\213\254\345\224\261.md" index 71118855fb4..66ca8195fab 100644 --- "a/markdown/online-ktv/\346\224\271\350\277\233/iOS/\347\213\254\345\224\261.md" +++ "b/markdown/online-ktv/\346\224\271\350\277\233/iOS/\347\213\254\345\224\261.md" @@ -81,24 +81,32 @@ SDK 会在音乐播放时每秒触发一次 didChangedToPosition 回调,你可以通过该回调调用 syncPlayerPosition 方法,达到每秒同步音乐播放进度的效果。 +**Note**:audioPlayoutDelay 是音频播放延迟,即从音频数据发送至接收端,到数据在接收端开始播放所需的时间。因为不同机型设备上的 audioPlayoutDelay 不同,主唱端和听众端可能使用不同的机型设备,所以在发布主唱的音乐播放进度时,声网建议在主唱端将进度减去 audioPlayoutDelay,在接收端计算时再加上接收端的 audioPlayoutDelay。 ```objective-c -- (void)syncPlayerPosition:(NSInteger)position duration:(NSInteger)duration { - NSDictionary *dict = @{ - @"cmd":@"playerPosition", - @"duration":@(duration), - @"time":@(position) - }; - NSData *messageData = [NSJSONSerialization dataWithJSONObject:dict options:0 error:nil]; - int code = [self.engine sendStreamMessage:self.dataStreamId - data:messageData]; -} - (void)AgoraRtcMediaPlayer:(id)playerKit didChangedToPosition:(NSInteger)position { - if (self.config.role == KTVSingRoleMainSinger) { - // 如果是独唱 - [self syncPlayerPosition:position duration:[playerKit getDuration]]; + self.localPlayerPosition = uptime() - position; + + if (self.config.role == KTVSingRoleMainSinger && position > self.audioPlayoutDelay) { + //if i am main singer + NSDictionary *dict = @{ + @"cmd":@"setLrcTime", + @"duration":@(self.playerDuration), + // 发布主唱的音乐播放进度时,声网建议在主唱端将进度减去 audioPlayoutDelay,在接收端计算时再加上接收端的 audioPlayoutDelay。 + @"time":@(position - self.audioPlayoutDelay), + @"ntp":@([self getNtpTimeInMs]), + @"playerState":@(self.playerState) + }; + [self sendStreamMessageWithDict:dict success:nil]; } + + .... +} + + +- (void)rtcEngine:(AgoraRtcEngineKit *)engine localAudioStats:(AgoraRtcLocalAudioStats *)stats { + self.audioPlayoutDelay = stats.audioPlayoutDelay; } ``` @@ -110,9 +118,10 @@ SDK 会在音乐播放时每秒触发一次 didChangedToPosition 回调,你可 [self.RTCkit adjustRecordingSignalVolume:isNowMicMuted ? 0 : 100]; ``` + ## 实现方法(听众) -### 1. 订阅主唱音频流 +### 1. 订阅音频流 听众需要将 autoSubscribeAudio 设为 YES,以订阅主唱发布的音乐和人声。为了不影响主唱的音乐播放,听众需要确保本地不播放音乐,即将 publishMediaPlayerAudioTrack 设为 NO。 @@ -154,6 +163,7 @@ SDK 会在音乐播放时每秒触发一次 didChangedToPosition 回调,你可 #pragma mark - AgoraLrcViewDelegate - (NSTimeInterval)getTotalTime { if (self.config.role == KTVSingRoleMainSinger) { + // 调用 getDuration 会耗时,建议只在播放、暂停等情况下调用 NSTimeInterval time = [_rtcMediaPlayer getDuration]; return time; } @@ -173,7 +183,7 @@ SDK 会在音乐播放时每秒触发一次 didChangedToPosition 回调,你可 - (void)rtcEngine:(AgoraRtcEngineKit *)engine receiveStreamMessageFromUid:(NSUInteger)uid streamId:(NSInteger)streamId data:(NSData *)data { NSDictionary *dict = [VLGlobalHelper dictionaryForJsonData:data]; - if ([dict[@"cmd"] isEqualToString:@"setLrcTime"]) { // 同步歌词 + if ([dict[@"cmd"] isEqualToString:@"setLrcTime"]) { NSInteger position = [dict[@"time"] integerValue]; NSInteger duration = [dict[@"duration"] integerValue]; self.remotePlayerPosition = position; From 8d770f64288ce4c27ca9b83db40414499bcb9f1f Mon Sep 17 00:00:00 2001 From: kelzr Date: Wed, 15 Mar 2023 10:37:07 +0800 Subject: [PATCH 09/20] =?UTF-8?q?[KTVI]=20=E5=9C=BA=E6=99=AF=E5=8C=96=20AP?= =?UTF-8?q?I=20=E6=96=B9=E6=A1=88=20added?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...257\345\214\226 Kotlin API for Android.md" | 265 ++++++++ .../\345\220\210\345\224\261.wsd" | 41 ++ .../\347\202\271\346\255\214.wsd" | 29 + .../\347\213\254\345\224\261.wsd" | 39 ++ .../\351\233\206\346\210\220.md" | 572 ++++++++++++++++ ...57\345\214\226 Objective-C API for iOS.md" | 252 +++++++ .../\345\220\210\345\224\261.wsd" | 41 ++ .../\347\202\271\346\255\214.wsd" | 27 + .../\347\213\254\345\224\261.wsd" | 39 ++ .../\351\233\206\346\210\220.md" | 623 ++++++++++++++++++ .../img/\345\220\210\345\224\261.drawio.svg" | 0 .../img/\347\202\271\346\255\214.drawio.svg" | 0 .../img/\347\213\254\345\224\261.drawio.svg" | 0 .../iOS/\345\220\210\345\224\261.md" | 0 .../iOS/\347\202\271\346\255\214.md" | 0 .../iOS/\347\202\271\346\255\214.wsd" | 0 .../iOS/\347\213\254\345\224\261.md" | 0 ...3\206\346\210\220\346\246\202\350\277\260" | 0 18 files changed, 1928 insertions(+) create mode 100644 "markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/Android/K \346\255\214\346\210\277\345\234\272\346\231\257\345\214\226 Kotlin API for Android.md" create mode 100644 "markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/Android/\345\256\236\347\216\260\346\226\207\346\241\243/\345\220\210\345\224\261.wsd" create mode 100644 "markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/Android/\345\256\236\347\216\260\346\226\207\346\241\243/\347\202\271\346\255\214.wsd" create mode 100644 "markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/Android/\345\256\236\347\216\260\346\226\207\346\241\243/\347\213\254\345\224\261.wsd" create mode 100644 "markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/Android/\345\256\236\347\216\260\346\226\207\346\241\243/\351\233\206\346\210\220.md" create mode 100644 "markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/iOS/K \346\255\214\346\210\277\345\234\272\346\231\257\345\214\226 Objective-C API for iOS.md" create mode 100644 "markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/iOS/\345\256\236\347\216\260\346\226\207\346\241\243/\345\220\210\345\224\261.wsd" create mode 100644 "markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/iOS/\345\256\236\347\216\260\346\226\207\346\241\243/\347\202\271\346\255\214.wsd" create mode 100644 "markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/iOS/\345\256\236\347\216\260\346\226\207\346\241\243/\347\213\254\345\224\261.wsd" create mode 100644 "markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/iOS/\345\256\236\347\216\260\346\226\207\346\241\243/\351\233\206\346\210\220.md" rename "markdown/online-ktv/\346\224\271\350\277\233/iOS/img/\345\220\210\345\224\261.drawio.svg" => "markdown/online-ktv/\346\224\271\350\277\233/\351\235\236\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/iOS/img/\345\220\210\345\224\261.drawio.svg" (100%) rename "markdown/online-ktv/\346\224\271\350\277\233/iOS/img/\347\202\271\346\255\214.drawio.svg" => "markdown/online-ktv/\346\224\271\350\277\233/\351\235\236\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/iOS/img/\347\202\271\346\255\214.drawio.svg" (100%) rename "markdown/online-ktv/\346\224\271\350\277\233/iOS/img/\347\213\254\345\224\261.drawio.svg" => "markdown/online-ktv/\346\224\271\350\277\233/\351\235\236\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/iOS/img/\347\213\254\345\224\261.drawio.svg" (100%) rename "markdown/online-ktv/\346\224\271\350\277\233/iOS/\345\220\210\345\224\261.md" => "markdown/online-ktv/\346\224\271\350\277\233/\351\235\236\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/iOS/\345\220\210\345\224\261.md" (100%) rename "markdown/online-ktv/\346\224\271\350\277\233/iOS/\347\202\271\346\255\214.md" => "markdown/online-ktv/\346\224\271\350\277\233/\351\235\236\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/iOS/\347\202\271\346\255\214.md" (100%) rename "markdown/online-ktv/\346\224\271\350\277\233/iOS/\347\202\271\346\255\214.wsd" => "markdown/online-ktv/\346\224\271\350\277\233/\351\235\236\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/iOS/\347\202\271\346\255\214.wsd" (100%) rename "markdown/online-ktv/\346\224\271\350\277\233/iOS/\347\213\254\345\224\261.md" => "markdown/online-ktv/\346\224\271\350\277\233/\351\235\236\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/iOS/\347\213\254\345\224\261.md" (100%) rename "markdown/online-ktv/\346\224\271\350\277\233/iOS/\351\233\206\346\210\220\346\246\202\350\277\260" => "markdown/online-ktv/\346\224\271\350\277\233/\351\235\236\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/iOS/\351\233\206\346\210\220\346\246\202\350\277\260" (100%) diff --git "a/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/Android/K \346\255\214\346\210\277\345\234\272\346\231\257\345\214\226 Kotlin API for Android.md" "b/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/Android/K \346\255\214\346\210\277\345\234\272\346\231\257\345\214\226 Kotlin API for Android.md" new file mode 100644 index 00000000000..85373b4e4b7 --- /dev/null +++ "b/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/Android/K \346\255\214\346\210\277\345\234\272\346\231\257\345\214\226 Kotlin API for Android.md" @@ -0,0 +1,265 @@ +本文提供在线 K 歌房场景定制化 Kotlin API。 + + +## 方法 + +### initWithRtcEngine + +```kotlin +fun initWithRtcEngine( + engine: RtcEngine, + channelName: String, + musicCenter: IAgoraMusicContentCenter, + player: IAgoraMusicPlayer, + streamId: Int, + ktvApiEventHandler: KTVApiEventHandler +) +``` + +初始化 KTV API。 + +调用该方法可以初始化 KTV API 模块内部变量和缓存数据,并注册相应的回调监听。 + +#### 注意事项 + +调用其他 KTV API 之前,你需要先调用本方法初始化。 + +#### 参数 + +- `engine`: [RtcEngine](https://docs.agora.io/cn/online-ktv/API%20Reference/java_ng/API/rtc_interface_class.html#class_irtcengine) 实例。 +- `channelName`: 待加入的频道名。 +- `musicCenter`: 版权音乐内容中心实例。详见 [IAgoraMusicContentCenter](https://docs.agora.io/cn/online-ktv/API%20Reference/java_ng/API/rtc_interface_class.html#class_imusiccontentcenter)。 +- `player`: 音乐播放器实例。详见 [IAgoraMusicPlayer](https://docs.agora.io/cn/online-ktv/API%20Reference/java_ng/API/rtc_interface_class.html#class_imusicplayer)。 +- `streamId`: 数据流(Data Stream)ID。 +- `ktvApiEventHandler`: K 歌场景化 API 的事件句柄。详见[回调](#onplayerstatechanged)。 + + +### release + +```kotlin +fun release() +``` + +释放 KTV API 资源。 + +调用该方法可以清空 KTV API 模块内部变量和缓存数据,取消 `ktvApiEventHandler` 的事件监听,取消网络请求等。 + +### 用法示例 + +```kotlin +// K 歌房 Activity 销毁时调用 ktvApiProtocol 释放,随后释放创建的实例 +@Override +protected void onDestroy() { + super.onDestroy(); + ktvApiProtocol.release() + // 释放 mRtcEngine、iAgoraMusicContentCenter、mPlayer、streamId +} +``` + +### loadSong + +```kotlin +fun loadSong( + songCode: Long, + config: KTVSongConfiguration, + onLoaded: (songCode: Long, lyricUrl: String, role: KTVSingRole, state: KTVLoadSongState) -> Unit +) +``` + +加载歌曲。 + +传入歌曲编号和 K 歌配置,调用 `loadSong` 加载歌曲。加载结果会异步地通过 `onLoaded` 回调通知你。 + +#### 参数 + +- `songCode`: 歌曲编号。 +- `config`: K 歌配置。详见 [KTVSongConfiguration](#ktvsongconfiguration)。 +- `onLoaded`: 歌词加载状态事件。 + + +### playSong + +```kotlin +fun playSong(songCode: Long) +``` + +播放歌曲。 + +建议在调用 `loadSong` 函数成功回调 `KTVLoadSongState.KTVLoadSongStateOK` 后再调用 `playSong`。 + +#### 参数 + +- `songCode`: 歌曲编号。 + +### stopSong + +```kotlin +fun stopSong() +``` + +结束播放歌曲。 + +### resumePlay + +```kotlin +fun resumePlay() +``` + +恢复播放歌曲。 + + +### pausePlay + +```kotlin +fun pausePlay() +``` + +暂停播放歌曲。 + +### seek + +```kotlin +fun seek(time: Long) +``` + +跳转到指定时间播放歌曲。 + +#### 参数 + +- `time`: 跳转的时间点。单位为毫秒。 + +### selectTrackMode + +```kotlin +fun selectTrackMode(mode: KTVPlayerTrackMode) +``` + +选择歌曲的音轨。 + +歌曲的音轨包含原唱和伴奏。调用该方法可以选择播放的音轨。 + +#### 参数 + +- `mode`: 音轨的类型。详见 [KTVPlayerTrackMode](#ktvplayertrackmode)。 + +### setLycView + +```kotlin +fun setLycView(view: LrcControlView) +``` + +设置歌词控制视图。 + +歌词控制视图用于显示歌词和控制歌词滚动等操作。调用该方法后,可以将歌词控制视图和 KTV 模块进行绑定,从而实现歌词的同步滚动。 + +#### 参数 + +- `view`: 歌词控制视图,`LrcControlView` 对象。 + + +## 回调 + +### onPlayerStateChanged + +```kotlin +interface KTVApiEventHandler { + fun onPlayerStateChanged(controller: KTVApi, state: Constants.MediaPlayerState, error: Constants.MediaPlayerError, isLocal: Boolean) +} +``` +播放器状态改变回调。 + +#### 参数 + +- `controller`: KTVApi 实例。 +- `state`: 播放器的当前状态。详见 [MediaPlayerState](https://docs.agora.io/cn/online-ktv/API%20Reference/java_ng/API/enum_mediaplayerstate.html?platform=Android)。 +- `error`: 播放器的错误码。详见 [MediaPlayerError](https://docs.agora.io/cn/online-ktv/API%20Reference/java_ng/API/enum_mediaplayererror.html?platform=Android)。 +- `isLocal`: 是否为本地事件: + - `true`: 是本地事件。 + - `false`: 不是本地事件。 + + +## Enum class + +### KTVSongType + +```kotlin +enum class KTVSongType { + KTVSongTypeSolo, + KTVSongTypeChorus +} +``` +K 歌场景类型: +- `KTVSongTypeSolo`: 独唱场景 +- `KTVSongTypeChorus`: 合唱场景 + +### KTVSingRole + +```kotlin +enum class KTVSingRole { + KTVSingRoleMainSinger, + KTVSingRoleCoSinger, + KTVSingRoleAudience +} +``` +K 歌用户角色类型: +- `KTVSingRoleMainSinger`: 主唱 +- `KTVSingRoleCoSinger`: 伴唱 +- `KTVSingRoleAudience`: 观众 + +### KTVPlayerTrackMode + +```kotlin +enum class KTVPlayerTrackMode { + KTVPlayerTrackOrigin, + KTVPlayerTrackAcc +} +``` + +K 歌播放音轨类型: +- `KTVPlayerTrackOrigin`: 原唱 +- `KTVPlayerTrackAcc`: 伴奏 + + +### KTVLoadSongState + +```kotlin +enum class KTVLoadSongState { + KTVLoadSongStateOK, + KTVLoadSongStateInProgress, + KTVLoadSongStateNoLyricUrl, + KTVLoadSongStatePreloadFail, + KTVLoadSongStateIdle +} +``` + +歌曲加载的状态: +- `KTVLoadSongStateOK`: 加载成功 +- `KTVLoadSongStateInProgress`: 正在加载中 +- `KTVLoadSongStateNoLyricUrl`: 歌曲无法加载,缺少歌词地址 +- `KTVLoadSongStatePreloadFail`: 加载失败 +- `KTVLoadSongStateIdle`: 空闲状态,未加载歌曲 + + +## Data class + +### KTVSongConfiguration + +```kotlin +data class KTVSongConfiguration( + val type: KTVSongType, + val role: KTVSingRole, + val songCode: Long, + val mainSingerUid: Int, + val coSingerUid: Int +) +``` + +K 歌配置: + +- `type`: K 歌场景类型,详见 [KTVSongType](#ktvsongtype) +- `role`: K 歌用户角色类型,详见 [KTVSingRole](#ktvsingrole) +- `songCode`: 歌曲的编号 +- `mainSingerUid`: 主唱的 UID +- `coSingerUid`: 伴唱的 UID + +UID 指用户 ID,用于标识频道内的用户,频道内的每个 UID 都必须是唯一。UID 是 32 位无符号整数,建议取值范围为 [1,232-1]。 \ No newline at end of file diff --git "a/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/Android/\345\256\236\347\216\260\346\226\207\346\241\243/\345\220\210\345\224\261.wsd" "b/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/Android/\345\256\236\347\216\260\346\226\207\346\241\243/\345\220\210\345\224\261.wsd" new file mode 100644 index 00000000000..7fa1af716fa --- /dev/null +++ "b/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/Android/\345\256\236\347\216\260\346\226\207\346\241\243/\345\220\210\345\224\261.wsd" @@ -0,0 +1,41 @@ +@startuml +title 独唱 API 时序图 +autonumber +skinparam monochrome true +participant "主唱/伴唱 App" as a +participant "声网 SDK" as b +participant "听众 App" as c +== 设置私有参数 == +a -> b: setParameters +== 加入频道 == +a -> b: joinChannel +c -> b: joinChannel +== 加载歌曲歌词 == +a -> b: loadSong +c -> b: loadSong +b -->> a: KTVLoadSongStateOK +b -->> c: KTVLoadSongStateOK +== 开始播放歌曲 == +a -> b: playSong(KTVSingRoleMainSinger/KTVSingRoleCoSinger) +b -->> a: onPlayerStateChanged(PLAYER_STATE_PLAYING) +c -> b: playSong(KTVSingRoleAudience) +b -->> c: onPlayerStateChanged(PLAYER_STATE_PLAYING) +note left +听众不播放歌曲,只是进入播放状态 +end note +== 停止播放 == +a -> b: stopSong +b -->> a: onPlayerStateChanged(PLAYER_STATE_STOPPED) +c -> b: stopSong +b -->> c: onPlayerStateChanged(PLAYER_STATE_STOPPED) +== 关闭麦克风 == +a -> b: adjustRecordingSignalVolume +== 更新媒体选项 == +a -> b: updateChannelMediaOptions +c -> b: updateChannelMediaOptions +note left +主唱、伴唱、上麦听众发布麦克风,角色为主播 + +普通听众不发布麦克风,角色为观众 +end note +@enduml \ No newline at end of file diff --git "a/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/Android/\345\256\236\347\216\260\346\226\207\346\241\243/\347\202\271\346\255\214.wsd" "b/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/Android/\345\256\236\347\216\260\346\226\207\346\241\243/\347\202\271\346\255\214.wsd" new file mode 100644 index 00000000000..16bebe8dc71 --- /dev/null +++ "b/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/Android/\345\256\236\347\216\260\346\226\207\346\241\243/\347\202\271\346\255\214.wsd" @@ -0,0 +1,29 @@ +@startuml +title 点歌 API 时序图 +autonumber +skinparam monochrome true +participant "App" as a +participant "声网 SDK" as b +== 初始化 KTV API 模块== +a -> b: initWithRtcEngine +== 获取歌曲列表(方式一:用关键词)== +a -> b: searchSong +b -->> a: onMusicCollectionResult +== 获取歌曲列表(方式二:用音乐榜单)== +a -> b: searchSongWithRankingChartId +b -->> a: onMusicChartsResult +== 加载歌曲 == +a -> b: loadSong +b -->> a: KTVLoadSongStateOK +== 开始播放歌曲 == +a -> b: playSong +b -->> a: onPlayerStateChanged(PLAYER_STATE_PLAYING) +== 控制歌曲播放 == +a ->b: seek/pause/resume/selectAudioTrack +b -->> a: onPlayerStateChanged(PLAYER_STATE_XX) +== 停止播放 == +a -> b: stopSong +b -->> a: onPlayerStateChanged(PLAYER_STATE_STOPPED) +== 释放 KTV API 模块资源 == +a ->b: release +@enduml \ No newline at end of file diff --git "a/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/Android/\345\256\236\347\216\260\346\226\207\346\241\243/\347\213\254\345\224\261.wsd" "b/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/Android/\345\256\236\347\216\260\346\226\207\346\241\243/\347\213\254\345\224\261.wsd" new file mode 100644 index 00000000000..6593bbf9cf4 --- /dev/null +++ "b/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/Android/\345\256\236\347\216\260\346\226\207\346\241\243/\347\213\254\345\224\261.wsd" @@ -0,0 +1,39 @@ +@startuml +title 独唱 API 时序图 +autonumber +skinparam monochrome true +participant "主唱 App" as a +participant "声网 SDK" as b +participant "听众 App" as c +== 加入频道 == +a -> b: joinChannel +c -> b: joinChannel +== 加载歌曲歌词 == +a -> b: loadSong +c -> b: loadSong +b -->> a: KTVLoadSongStateOK +b -->> c: KTVLoadSongStateOK +== 开始播放歌曲 == +a -> b: playSong(KTVSingRoleMainSinger) +b -->> a: onPlayerStateChanged(PLAYER_STATE_PLAYING) +c -> b: playSong(KTVSingRoleAudience) +b -->> c: onPlayerStateChanged(PLAYER_STATE_PLAYING) +note left +听众不播放歌曲,只是进入播放状态 +end note +== 停止播放 == +a -> b: stopSong +b -->> a: onPlayerStateChanged(PLAYER_STATE_STOPPED) +c -> b: stopSong +b -->> c: onPlayerStateChanged(PLAYER_STATE_STOPPED) +== 关闭麦克风 == +a -> b: adjustRecordingSignalVolume +== 更新媒体选项 == +a -> b: updateChannelMediaOptions +c -> b: updateChannelMediaOptions +note left +主唱和上麦听众发布麦克风,角色为主播 + +普通听众不发布麦克风,角色为观众 +end note +@enduml \ No newline at end of file diff --git "a/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/Android/\345\256\236\347\216\260\346\226\207\346\241\243/\351\233\206\346\210\220.md" "b/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/Android/\345\256\236\347\216\260\346\226\207\346\241\243/\351\233\206\346\210\220.md" new file mode 100644 index 00000000000..37d42bd0dcf --- /dev/null +++ "b/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/Android/\345\256\236\347\216\260\346\226\207\346\241\243/\351\233\206\346\210\220.md" @@ -0,0 +1,572 @@ +## 概述 + +为降低开发者集成难度,声网为 K 歌房场景提供了定制化场景 API。定制化场景 API 基于场景业务代码逻辑封装了声网音视频 SDK API,让你只需调用一个定制化 API,即可实现通过多个音视频 API 和复杂的代码逻辑才可实现的 K 歌业务功能,例如将合唱和伴唱端进行 NTP 时间同步。 + +本文介绍如何使用 K 歌定制化场景 API 实现点歌、独唱、合唱等基础业务功能。 + +
    开始前请确保你已参考项目配置集成所需 SDK。
    + +## 点歌 + +本节介绍如何实现点歌功能。用户需要在唱歌前进行点歌。点歌指用户通过浏览榜单或搜索关键词选定想唱的正版音乐,然后下载播放音乐。 + +### 方案介绍 + +下图展示点歌的 API 调用时序图: + +![](https://web-cdn.agora.io/docs-files/1678788543385) + +### 1. 初始化 KTV API 模块 + +实例化 `rtcEngine`、`musicCenter`、`mediaPlayer`、`streamId` 实例,并将它们通过 `initWithRtcEngine` 方法传入 KTV API 模块。调用 KTV API 模块的 API 前,请确保已调用 `initWithRtcEngine` 初始化 KTV API 实例。 + +
    考虑到数据流的消息通道有频率限制,为了确保 KTV 模块和其他模块不会相互影响,声网建议你在不同模块中为数据流创建的 streamId 都不同。例如,如果你在其他模块中也使用 sendStreamMessage,请确保两个模块中创建的 streamId 不同。同时,每个用户在每个频道中最多只能创建 5 个数据流,请不要超出上限。
    + +1. 调用 [`create`](https://docs.agora.io/cn/online-ktv/API%20Reference/java_ng/API/toc_core_method.html#api_irtcengine_initialize) 初始化 `RtcEngine`。 + + ```Kotlin + // 初始化 RtcEngine + val config = RtcEngineConfig() + config.mContext = null + config.mAppId = "" + config.mEventHandler = object : IRtcEngineEventHandler() {} + config.mChannelProfile = CHANNEL_PROFILE_LIVE_BROADCASTING + config.mAudioScenario = AUDIO_SCENARIO_CHORUS + val mRtcEngine = RtcEngine.create(config) as RtcEngineEx + ``` + +2. 调用 [`initialize`](https://docs.agora.io/cn/online-ktv/API%20Reference/java_ng/API/toc_drm.html#api_imusiccontentcenter_initialize) 初始化 `IAgoraMusicContentCenter`。示例代码中需要传入 RTM Token。你可以参考[获取 RTM Token](/cn/Agora%20Platform/get_appid_token?platform=All%20Platforms#获取-rtm-token) 了解什么是 RTM Token,如何获取测试用途的临时 RTM Token,如何从服务器生成 RTM Token。 + + ```Kotlin + // 初始化 IAgoraMusicContentCenter + val contentCenterConfiguration = MusicContentCenterConfiguration() + contentCenterConfiguration.appId = "" + contentCenterConfiguration.mccUid = + contentCenterConfiguration.token = "" + val iAgoraMusicContentCenter = IAgoraMusicContentCenter.create(mRtcEngine) + iAgoraMusicContentCenter.initialize(contentCenterConfiguration) + ``` + +3. 调用 [`createMusicPlayer`](https://docs.agora.io/cn/online-ktv/API%20Reference/java_ng/API/toc_mediaplayer.html#api_irtcengine_createmediaplayer) 创建 Media Player。 + + ```Kotlin + // 创建 media player + val mPlayer = iAgoraMusicContentCenter.createMusicPlayer() + ``` + +4. 调用 [`createDataStream`](https://docs.agora.io/cn/online-ktv/API%20Reference/java_ng/API/toc_network.html#api_irtcengine_createdatastream2) 创建数据流。 + + ```Kotlin + // 创建数据流 + val cfg = DataStreamConfig() + cfg.syncWithAudio = false + cfg.ordered = false + val streamId = mRtcEngine.createDataStream(cfg) + ``` + +5. 调用 `initWithRtcEngine` 初始化 KTV API 实例。 + + ```Kotlin + // 初始化 KTV API 实例 + val ktvApiProtocol = KTVApiImpl() + ktvApiProtocol.initWithRtcEngine(mRtcEngine, , iAgoraMusicContentCenter, mPlayer, streamId, object: KTVApiEventHandler {}) + ``` + +### 2. 获取歌曲列表 + +通过关键词搜索或音乐榜单获取歌曲列表。 + +```Kotlin +// 用关键词搜索歌曲 +fun searchSong(condition: String, page: Int) { + // 搜索过滤条件 + val jsonOption = "{\"pitchType\":1,\"needLyric\":true}" + val requestId = iAgoraMusicContentCenter.searchMusic(condition, page, 50, jsonOption) +} + +// IMusicContentCenterEventHandler +// 报告搜索结果 +override fun onMusicCollectionResult( + requestId: String?, + status: Int, + page: Int, + pageSize: Int, + total: Int, + list: Array? +) {} +``` + +```Kotlin +// 用音乐榜单获取歌曲 +fun searchSongWithRankingChartId(type: Int, page: Int) { + // 搜索过滤条件 + val jsonOption = "{\"pitchType\":1,\"needLyric\":true}" + val requestId = iAgoraMusicContentCenter.getMusicCollectionByMusicChartId(condition, page, 50, jsonOption) +} + +// IMusicContentCenterEventHandler +// 报告搜索结果 +override fun onMusicChartsResult( + requestId: String?, + status: Int, + list: Array? +) {} +``` + +### 3. 加载歌曲 + +调用 `loadSong` 加载歌曲。该方法中你需要参入编曲编号和 K 歌配置,例如当前的 K 歌唱的场景(合唱或伴唱)、用户角色、主唱伴唱的 UID。K 歌配置会决定 K 歌时歌曲的播放情况、各端用户的收发流情况等。歌曲加载结果会异步地通过 `onLoaded` 回调通知你。 + +```Kotlin +// isChorus: 歌曲是否合唱 +// isOwnSong: 是否为自己点的歌,点歌者默认为主唱 +// isChorusMem: 是否为伴唱点的歌 +val type = if (isChorus) KTVSongType.KTVSongTypeChorus else KTVSongType.KTVSongTypeSolo +val role = if (isOwnSong) KTVSingRole.KTVSingRoleMainSinger else if (isChorusMem) KTVSingRole.KTVSingRoleCoSinger else KTVSingRole.KTVSingRoleAudience +// 主唱 UID +val mainSingerUid = +// 伴唱 UID。如果是独唱场景,不存在伴唱角色,那么传入 0 即可。 +val coSingerUid = + +// 加载歌曲 +ktvApiProtocol.loadSong( + songCode, + KTVSongConfiguration(type, role, songCode, mainSingerUid, coSingerUid) +) { song, lyricUrl, singRole, singState -> + if (singState === KTVLoadSongState.KTVLoadSongStateOK) { + // 歌曲加载成功的操作 + ... + } else if (singState == KTVLoadSongState.KTVLoadSongStatePreloadFail) { + // 歌曲加载失败的操作 + ... + } else if (singState == KTVLoadSongState.KTVLoadSongStateNoLyricUrl) { + // 歌曲没有歌词的操作 + ... + } +} +``` + +### 4. 播放歌曲 + +收到 `onLoaded(KTVLoadSongStateOK)` 回调状态后,调用 `playSong` 开始播放歌曲。 + +```Kotlin +ktvApiProtocol.loadSong( + songCode, + KTVSongConfiguration(type, role, songCode, mainSingerUid, coSingerUid) +) { song, lyricUrl, singRole, singState -> + if (singState === KTVLoadSongState.KTVLoadSongStateOK) { + // 歌曲加载成功,则播放歌曲 + ktvApiProtocol.playSong() + } else { + // 否则进行其他操作 + ... + } +} +``` + +### 5. 监听并控制歌曲播放 + +歌曲播放时,音乐播放器会通过 `onPlayerStateChanged` 回调向业务层通知歌曲播放状态改变。收到 `onPlayerStateChanged(PLAYER_STATE_PLAYING)` 回调后,你可以使用 `seek`、`pause`、`resume`、`selectAudioTrack` 等方法控制播放器。 + +
    KTV API 模块内部会自动处理播放器同步,因此你也可以通过 onPlayerStateChanged 回调获取远端播放器的状态。
    + +```Kotlin +// 跳转到指定时间播放歌曲 +ktvApiProtocol.seek(time) +``` + +### 6. 停止歌曲播放 + +当歌曲播放完成或用户中途结束播放时,你需要主动调用 `stopSong` 停止播放。 + +```Kotlin +ktvApiProtocol.stopSong() +``` + +### 7. 释放资源 + +当退出 K 歌场景时,你需要调用 `release` 释放 KTV API 模块内的资源和取消注册事件回调。 + +```Kotlin +ktvApiProtocol.release() +``` + +## 独唱 + +本节介绍如何实现独唱功能。主唱点歌后,可以开始独唱,K 歌房内的听众都可以听到这位主唱唱歌。房间内想与主唱连麦语聊的听众可以上麦。 + +### 方案介绍 + +独唱场景下存在两种角色: + +- 主唱:加入频道,加载并播放歌曲。KTV API 模块内部控制音乐播放器播放音乐,发布音乐到远端,将音乐播放进度同步到远端,让歌词组件进入歌词滚动状态等逻辑。 +- 听众:加入频道,加载歌曲。KTV API 模块内部控制听众订阅主唱的人声和音乐的音频合流,同步主唱的音乐播放进度,让歌词组件进入歌词滚动状态等逻辑。如果普通观众需要上麦聊天,可以更新媒体选项。 + +![](https://web-cdn.agora.io/docs-files/1678784206362) + +下图展示独唱的 API 调用时序图: + +![](https://web-cdn.agora.io/docs-files/1678788551670) + +### 主唱实现 + +#### 1. 加入频道 + +调用 [`joinChannel`](https://docs.agora.io/cn/online-ktv/API%20Reference/java_ng/API/toc_core_method.html#api_irtcengine_joinchannel2) 让主唱加入频道。 + +```Kotlin +// 加入频道 +mRtcEngine.joinChannel( + , + , + , + // 媒体选项详见第 5 步操作 + channelMediaOption +) +``` + +#### 2. 播放歌曲 + +调用 `loadSong` 加载歌曲。歌曲加载结果会异步地通过 `onLoaded` 回调通知你。收到 `onLoaded(KTVLoadSongStateOK)` 回调状态后,调用 `playSong` 开始播放歌曲。 + +```Kotlin +ktvApiProtocol.loadSong( + "ABCDEFG", KTVSongConfiguration(KTVSongType.KTVSongTypeSolo, KTVSingRole.KTVSingRoleMainSinger, "ABCDEFG", 12345, 0) +) { songCode, lyricUrl, singRole, singState -> + if (singState === KTVLoadSongState.KTVLoadSongStateOK) { + // 歌曲加载成功,则播放歌曲 + ktvApiProtocol.playSong(songCode) + } +} +``` + +#### 3. 停止播放 + +当歌曲播放完成或用户中途结束播放时,你需要主动调用 `stopSong` 停止播放。 + +```Kotlin +ktvApiProtocol.stopSong() +``` + +#### 4. 关闭麦克风 + +主唱停止唱歌或希望暂时关闭麦克风时,可以调用 [`adjustRecordingSignalVolume`](https://docs.agora.io/cn/online-ktv/API%20Reference/java_ng/API/toc_audio_process.html#api_irtcengine_adjustrecordingsignalvolume),将音频采集信号音量设置为 0。 + +```Kotlin +mRtcEngine.adjustRecordingSignalVolume(0) +``` + +#### 5. 根据角色更新媒体选项 + +通过 [`updateChannelMediaOptions`](https://docs.agora.io/cn/online-ktv/API%20Reference/java_ng/API/toc_core_method.html#api_irtcengine_updatechannelmediaoptions) 方法在主播加入频道后更新频道媒体选项,例如是否开启本地音频采集,是否发布本地音频流等。 + +```Kotlin +val channelMediaOption = ChannelMediaOptions() +// 发布本地麦克风流 +channelMediaOption.publishMicrophoneTrack = true +// 启用音频采集和播放 +channelMediaOption.enableAudioRecordingOrPlayout = true +// 设置角色为主播 +channelMediaOption.clientRoleType = CLIENT_ROLE_BROADCASTER +// 更新媒体选项 +mRtcEngine.updateChannelMediaOptions(channelMediaOption) +``` + +### 听众实现 + +#### 1. 加入频道 + +调用 [`joinChannel`](https://docs.agora.io/cn/online-ktv/API%20Reference/java_ng/API/toc_core_method.html#api_irtcengine_joinchannel2) 让听众加入频道。 + +```Kotlin +// 加入频道 +mRtcEngine.joinChannel( + , + , + , + // 媒体选项详见第 4 步操作 + channelMediaOption +) +``` + +#### 2. 加载歌曲 + +调用 `loadSong` 加载歌曲。加载结果会异步地通过 `onLoaded` 回调通知你。收到 `onLoaded(KTVLoadSongStateOK)` 回调状态后,调用 `playSong`。 + +听众加入频道后,默认订阅主唱发布的音频合流,即主唱人声和音乐混合的音频流。听众只需调用 `playSong` 进入歌曲播放状态即可。 + +```Kotlin +ktvApiProtocol.loadSong( + "ABCDEFG", KTVSongConfiguration(KTVSongType.KTVSongTypeSolo, KTVSingRole.KTVSingRoleAudience, "ABCDEFG", 12345, 0) // 听众可以不填演唱者 UID +) { songCode, lyricUrl, singRole, singState -> + if (singState === KTVLoadSongState.KTVLoadSongStateOK) { + // 歌曲加载成功后,进入歌曲播放状态 + ktvApiProtocol.playSong(songCode) + } +} +``` + +#### 3. 停止播放 + +当歌曲播放完成或用户中途结束播放时,你需要主动调用 `stopSong` 停止播放。 + +```Kotlin +ktvApiProtocol.stopSong() +``` + +#### 4. 根据角色更新媒体选项 + +通过 [`updateChannelMediaOptions`](https://docs.agora.io/cn/online-ktv/API%20Reference/java_ng/API/toc_core_method.html#api_irtcengine_updatechannelmediaoptions) 方法在听众加入频道后更新频道媒体选项,例如是否开启本地音频采集,是否发布本地音频流等。 + +听众的用户角色为 AgoraClientRoleAudience,因此无法在频道内发布音频流。如果听众想上麦与主唱语聊,需要将用户角色修改为 AgoraClientRoleBroadcaster。修改角色后,SDK 默认发布该连麦听众的音频流,主唱和其他听众都能听到连麦听众的声音。 + +```Kotlin +// 针对需要上麦聊天的听众更新媒体选项 +val channelMediaOption = ChannelMediaOptions() +// 发布本地麦克风流 +channelMediaOption.publishMicrophoneTrack = true +// 启用音频采集和播放 +channelMediaOption.enableAudioRecordingOrPlayout = true +// 设置角色为主播 +channelMediaOption.clientRoleType = CLIENT_ROLE_BROADCASTER +// 更新媒体选项 +mRtcEngine.updateChannelMediaOptions(channelMediaOption) + + +// 针对未上麦的听众更新媒体选项 +val channelMediaOption = ChannelMediaOptions() +// 不发布本地麦克风流 +channelMediaOption.publishMicrophoneTrack = false +// 启用音频采集和播放 +channelMediaOption.enableAudioRecordingOrPlayout = true +// 设置角色为用户 +channelMediaOption.clientRoleType = CLIENT_ROLE_AUDIENCE +// 更新媒体选项 +mRtcEngine.updateChannelMediaOptions(channelMediaOption) +``` + +## 合唱 + +本节介绍如何实现合唱功能。主唱点歌开唱后,伴唱可以和主唱一起唱歌,K 歌房内的听众都可以听到合唱。房间内想与主唱或伴唱连麦语聊的听众可以上麦。 + +### 方案介绍 + +合唱场景下存在三种角色: + +- 主唱:加入频道,加载并播放歌曲,发布麦克风采集的音频流。KTV API 模块内部控制音乐播放器播放音乐,发布音乐到远端,将音乐播放进度同步到远端,让歌词组件进入歌词滚动状态等逻辑。 +- 伴唱:加入频道,加载并播放歌曲,发布麦克风采集的音频流。KTV API 模块内部控制音乐播放器播放音乐,同步主唱的音乐播放进度,让歌词组件进入歌词滚动状态等逻辑。 +- 听众:加入频道,加载歌曲。KTV API 模块内部控制听众订阅主唱的人声和音乐的音频合流,同步主唱的音乐播放进度,让歌词组件进入歌词滚动状态等逻辑。如果普通观众需要上麦聊天,可以更新媒体选项。 + +![](https://web-cdn.agora.io/docs-files/1678784685024) + +下图展示合唱的 API 调用时序图: + +![](https://web-cdn.agora.io/docs-files/1678788560007) + +### 主唱实现 + +#### 1. 设置合唱私有参数 + +实时合唱场景对低延时和音质的要求很高。开启合唱前,你需要在初始化 RtcEngine 引擎的方法里设置如下私有参数。 + +```Kotlin +mRtcEngine.setParameters("{\"rtc.ntp_delay_drop_threshold\":1000}"); +mRtcEngine.setParameters("{\"che.audio.agc.enable\": true}"); +mRtcEngine.setParameters("{\"rtc.video.enable_sync_render_ntp\": true}"); +mRtcEngine.setParameters("{\"rtc.net.maxS2LDelay\": 800}"); +``` + +#### 2. 加入频道 + +调用 [`joinChannel`](https://docs.agora.io/cn/online-ktv/API%20Reference/java_ng/API/toc_core_method.html#api_irtcengine_joinchannel2) 让主唱加入频道。 + +```Kotlin +// 加入频道 +mRtcEngine.joinChannel( + , + , + , + // 媒体选项详见第 5 步操作 + channelMediaOption +) +``` + +#### 3. 开始合唱 + +调用 `loadSong` 加载歌曲。歌曲加载结果会异步地通过 `onLoaded` 回调通知你。收到 `onLoaded(KTVLoadSongStateOK)` 回调状态后,调用 `playSong` 开始播放歌曲,开始合唱。 + +```Kotlin +ktvApiProtocol.loadSong( + "ABCDEFG", KTVSongConfiguration(KTVSongType.KTVSongTypeChorus, KTVSingRole.KTVSingRoleMainSinger, "ABCDEFG", 12345, 0) // 主唱可以不填伴唱 UID,传 0 即可。 +) { songCode, lyricUrl, singRole, singState -> + if (singState === KTVLoadSongState.KTVLoadSongStateOK) { + // 歌曲加载成功,则播放歌曲 + ktvApiProtocol.playSong(songCode) + } +} +``` + +#### 4. 停止合唱 + +当歌曲播放完成或用户中途结束播放时,你需要主动调用 `stopSong` 停止播放,停止合唱。 + +```Kotlin +ktvApiProtocol.stopSong() +``` + +#### 5. 根据角色更新媒体选项 + +通过 [`updateChannelMediaOptions`](https://docs.agora.io/cn/online-ktv/API%20Reference/java_ng/API/toc_core_method.html#api_irtcengine_updatechannelmediaoptions) 方法在主播加入频道后更新频道媒体选项,例如是否开启本地音频采集,是否发布本地音频流等。 + +```Kotlin +val channelMediaOption = ChannelMediaOptions() +// 发布本地麦克风流 +channelMediaOption.publishMicrophoneTrack = true +// 启用音频采集和播放 +channelMediaOption.enableAudioRecordingOrPlayout = true +// 设置角色为主播 +channelMediaOption.clientRoleType = CLIENT_ROLE_BROADCASTER +// 更新媒体选项 +mRtcEngine.updateChannelMediaOptions(channelMediaOption) +``` + +### 伴唱实现 + +#### 1. 设置合唱私有参数 + +实时合唱场景对低延时和音质的要求很高。开启合唱前,你需要在初始化 RtcEngine 引擎的方法里设置如下私有参数。 + +```Kotlin +mRtcEngine.setParameters("{\"rtc.ntp_delay_drop_threshold\":1000}"); +mRtcEngine.setParameters("{\"che.audio.agc.enable\": true}"); +mRtcEngine.setParameters("{\"rtc.video.enable_sync_render_ntp\": true}"); +mRtcEngine.setParameters("{\"rtc.net.maxS2LDelay\": 800}"); +``` + +#### 2. 加入频道 + +调用 [`joinChannel`](https://docs.agora.io/cn/online-ktv/API%20Reference/java_ng/API/toc_core_method.html#api_irtcengine_joinchannel2) 让伴唱加入频道。 + +```Kotlin +// 加入频道 +mRtcEngine.joinChannel( + , + , + , + // 媒体选项详见第 5 步操作 + channelMediaOption +) +``` + +#### 3. 开始合唱 + +调用 `loadSong` 加载歌曲。歌曲加载结果会异步地通过 `onLoaded` 回调通知你。收到 `onLoaded(KTVLoadSongStateOK)` 回调状态后,调用 `playSong` 开始播放歌曲,开始合唱。 + +```Kotlin +ktvApiProtocol.loadSong( + "ABCDEFG", KTVSongConfiguration(KTVSongType.KTVSongTypeChorus, KTVSingRole.KTVSingRoleCoSinger, "ABCDEFG", 12345, 54321) // 伴唱一定要填主唱 UID +) { songCode, lyricUrl, singRole, singState -> + if (singState === KTVLoadSongState.KTVLoadSongStateOK) { + // 歌曲加载成功,则播放歌曲 + ktvApiProtocol.playSong(songCode) + } +} +``` + +#### 4. 停止合唱 + +当歌曲播放完成或用户中途结束播放时,你需要主动调用 `stopSong` 停止播放,停止合唱。 + +```Kotlin +ktvApiProtocol.stopSong() +``` + +#### 5. 根据角色更新媒体选项 + +通过 [`updateChannelMediaOptions`](https://docs.agora.io/cn/online-ktv/API%20Reference/java_ng/API/toc_core_method.html#api_irtcengine_updatechannelmediaoptions) 方法在伴唱加入频道后更新频道媒体选项,例如是否开启本地音频采集,是否发布本地音频流等。 + +```Kotlin +val channelMediaOption = ChannelMediaOptions() +// 发布本地麦克风流 +channelMediaOption.publishMicrophoneTrack = true +// 启用音频采集和播放 +channelMediaOption.enableAudioRecordingOrPlayout = true +// 设置角色为主播 +channelMediaOption.clientRoleType = CLIENT_ROLE_BROADCASTER +// 更新媒体选项 +mRtcEngine.updateChannelMediaOptions(channelMediaOption) +``` + +### 听众实现 + +#### 1. 加入频道 + +调用 [`joinChannel`](https://docs.agora.io/cn/online-ktv/API%20Reference/java_ng/API/toc_core_method.html#api_irtcengine_joinchannel2) 让听众加入频道。 + +```Kotlin +// 加入频道 +mRtcEngine.joinChannel( + , + , + , + // 媒体选项详见第 4 步操作 + channelMediaOption +) +``` + +#### 2. 加载歌曲 + +调用 `loadSong` 加载歌曲。加载结果会异步地通过 `onLoaded` 回调通知你。收到 `onLoaded(KTVLoadSongStateOK)` 回调状态后,调用 `playSong`。 + +听众加入频道后,默认订阅主唱发布的音频合流,即主唱人声和音乐混合的音频流。听众只需调用 `playSong` 进入歌曲播放状态即可。 + +```Kotlin +ktvApiProtocol.loadSong( + "ABCDEFG", KTVSongConfiguration(KTVSongType.KTVSongTypeChorus, KTVSingRole.KTVSingRoleAudience, "ABCDEFG", 0, 0) // 听众可以不填演唱者 UID +) { songCode, lyricUrl, singRole, singState -> + if (singState === KTVLoadSongState.KTVLoadSongStateOK) { + // 歌曲加载成功后,进入歌曲播放状态 + ktvApiProtocol.playSong(songCode) + } +} +``` + +#### 3. 停止播放 + +当歌曲播放完成或用户中途结束播放时,你需要主动调用 `stopSong` 停止播放。 + +```Kotlin +ktvApiProtocol.stopSong() +``` + +#### 4. 根据角色更新媒体选项 + +通过 [`updateChannelMediaOptions`](https://docs.agora.io/cn/online-ktv/API%20Reference/java_ng/API/toc_core_method.html#api_irtcengine_updatechannelmediaoptions) 方法在听众加入频道后更新频道媒体选项,例如是否开启本地音频采集,是否发布本地音频流等。 + +听众的用户角色为 AgoraClientRoleAudience,因此无法在频道内发布音频流。如果听众想上麦与主唱语聊,需要将用户角色修改为 AgoraClientRoleBroadcaster。修改角色后,SDK 默认发布该连麦听众的音频流,主唱和其他听众都能听到连麦听众的声音。 + +```Kotlin +// 针对需要上麦聊天的听众更新媒体选项 +val channelMediaOption = ChannelMediaOptions() +// 发布本地麦克风流 +channelMediaOption.publishMicrophoneTrack = true +// 启用音频采集和播放 +channelMediaOption.enableAudioRecordingOrPlayout = true +// 设置角色为主播 +channelMediaOption.clientRoleType = CLIENT_ROLE_BROADCASTER +// 更新媒体选项 +mRtcEngine.updateChannelMediaOptions(channelMediaOption) + + +// 针对未上麦的听众更新媒体选项 +val channelMediaOption = ChannelMediaOptions() +// 不发布本地麦克风流 +channelMediaOption.publishMicrophoneTrack = false +// 启用音频采集和播放 +channelMediaOption.enableAudioRecordingOrPlayout = true +// 设置角色为用户 +channelMediaOption.clientRoleType = CLIENT_ROLE_AUDIENCE +// 更新媒体选项 +mRtcEngine.updateChannelMediaOptions(channelMediaOption) +``` \ No newline at end of file diff --git "a/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/iOS/K \346\255\214\346\210\277\345\234\272\346\231\257\345\214\226 Objective-C API for iOS.md" "b/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/iOS/K \346\255\214\346\210\277\345\234\272\346\231\257\345\214\226 Objective-C API for iOS.md" new file mode 100644 index 00000000000..10748d7fe74 --- /dev/null +++ "b/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/iOS/K \346\255\214\346\210\277\345\234\272\346\231\257\345\214\226 Objective-C API for iOS.md" @@ -0,0 +1,252 @@ +本文提供在线 K 歌房场景定制化 Objective-C API。 + +## 方法 + +### initWithRtcEngine + +```objective-c +- (id)initWithRtcEngine:(AgoraRtcEngineKit *)engine + channel:(NSString*)channelName + musicCenter:(AgoraMusicContentCenter*)musicCenter + player:(nonnull id)rtcMediaPlayer + dataStreamId:(NSInteger)streamId + delegate:(id)delegate; +) +``` + +初始化 KTV API。 + +调用该方法可以初始化 KTV API 模块内部变量和缓存数据,并注册相应的回调监听。 + +#### 注意事项 + +调用其他 KTV API 之前,你需要先调用本方法初始化。 + +#### 参数 + +- `engine`: [AgoraRtcEngineKit](https://docs.agora.io/cn/online-ktv/API%20Reference/ios_ng/API/rtc_interface_class.html#class_irtcengine) +- `channel`: 待加入的频道名。 +- `musicCenter`: 版权音乐内容中心实例。详见 [AgoraMusicContentCenter](https://docs.agora.io/cn/online-ktv/API%20Reference/ios_ng/API/rtc_interface_class.html#class_imusiccontentcenter)。 +- `player`: 音乐播放器实例。详见 [AgoraMusicPlayerProtocol](https://docs.agora.io/cn/online-ktv/API%20Reference/ios_ng/API/rtc_interface_class.html#class_imusicplayer)。 +- `dataStreamId`: 数据流(Data Stream)ID。 +- `delegate`: KTVApiDelegate + + +### release + +```objective-c +fun release() +``` + +释放 KTV API 资源。 + +调用该方法可以清空 KTV API 模块内部变量和缓存数据,取消 `ktvApiEventHandler` 的事件监听,取消网络请求等。 + +### 用法示例 + +```objective-c +// K 歌房 Activity 销毁时调用 ktvApiProtocol 释放,随后释放创建的实例 +@Override +protected void onDestroy() { + super.onDestroy(); + ktvApiProtocol.release() + // 释放 mRtcEngine、iAgoraMusicContentCenter、mPlayer、streamId +} +``` + +### loadSong + +```objective-c +- (void)loadSong:(NSInteger)songCode +withConfig:(nonnull KTVSongConfiguration *)config +withCallback:(void (^ _Nullable)(NSInteger songCode, NSString* lyricUrl, KTVSingRole role, KTVLoadSongState state))block +``` + +加载歌曲。 + +传入歌曲编号和 K 歌配置,调用 `loadSong` 加载歌曲。加载结果会异步地通过 `onLoaded` 回调通知你。 + +#### 参数 + +- `songCode`: 歌曲编号。 +- `config`: K 歌配置。详见 [KTVSongConfiguration](#ktvsongconfiguration)。 +- `block`: 歌词加载状态事件。 + + +### playSong + +```objective-c +- (void)playSong:(NSInteger)songCode +``` + +播放歌曲。 + +建议在调用 `loadSong` 函数成功回调 `KTVLoadSongStateOK` 状态后再调用 `playSong`。 + +#### 参数 + +- `songCode`: 歌曲编号。 + +### stopSong + +```objective-c +- (void)stopSong; +``` + +结束播放歌曲。 + +### resumePlay + +```objective-c +- (void)resumePlay; +``` + +恢复播放歌曲。 + + +### pausePlay + +```objective-c +- (void)pausePlay; +``` + +暂停播放歌曲。 + +### seek + +```objective-c +- (void)seek:(time: NSInteger); +``` + +跳转到指定时间播放歌曲。 + +#### 参数 + +- `time`: 跳转的时间点。单位为毫秒。 + +### selectTrackMode + +```objective-c +- (void)selectTrackMode:(KTVPlayerTrackMode)mode; +``` + +选择歌曲的音轨。 + +歌曲的音轨包含原唱和伴奏。调用该方法可以选择播放的音轨。 + +#### 参数 + +- `mode`: 音轨的类型。详见 [KTVPlayerTrackMode](#ktvplayertrackmode)。 + +### setKaraokeView + +```objective-c +@property(nonatomic, weak) KaraokeView* karaokeView; + +- (void)setKaraokeView:(KaraokeView *)karaokeView{ + _karaokeView = karaokeView; + _karaokeView.delegate = self; +} +``` + +设置歌词控制视图。 + +歌词控制视图用于显示歌词和控制歌词滚动等操作。调用该方法后,可以将歌词控制视图和 KTV 模块进行绑定,从而实现歌词的同步滚动。 + +#### 参数 + +- `karaokeView`: 歌词控制视图,`KaraokeView` 对象。 + +## 回调 + +//TODO + +## Enum + +### KTVSongType + +```objective-c +typedef enum : NSUInteger { + KTVSongTypeUnknown = 0, + KTVSongTypeSolo, + KTVSongTypeChorus +} KTVSongType; +``` +K 歌场景类型: +- `KTVSongTypeUnknown`: 未知 +- `KTVSongTypeSolo`: 独唱场景 +- `KTVSongTypeChorus`: 合唱场景 + +### KTVSingRole + +```objective-c +typedef enum : NSUInteger { + KTVSingRoleUnknown = 0, + KTVSingRoleMainSinger, + KTVSingRoleCoSinger, + KTVSingRoleAudience +} KTVSingRole; +``` +K 歌用户角色类型: +- `KTVSingRoleUnknown`: 未知 +- `KTVSingRoleMainSinger`: 主唱 +- `KTVSingRoleCoSinger`: 伴唱 +- `KTVSingRoleAudience`: 观众 + +### KTVPlayerTrackMode + +```objective-c +typedef enum : NSUInteger { + KTVPlayerTrackOrigin = 0, + KTVPlayerTrackAcc = 1 +} KTVPlayerTrackMode; +``` + +K 歌播放音轨类型: +- `KTVPlayerTrackOrigin`: 原唱 +- `KTVPlayerTrackAcc`: 伴奏 + + +### KTVLoadSongState + +```objective-c +typedef enum : NSUInteger { + KTVLoadSongStateIdle = 0, + KTVLoadSongStateOK, + KTVLoadSongStateInProgress, + KTVLoadSongStateNoLyricUrl, + KTVLoadSongStatePreloadFail, +} KTVLoadSongState; +``` + +歌曲加载的状态: +- `KTVLoadSongStateIdle`: 空闲状态,未加载歌曲 +- `KTVLoadSongStateOK`: 加载成功 +- `KTVLoadSongStateInProgress`: 正在加载中 +- `KTVLoadSongStateNoLyricUrl`: 歌曲无法加载,缺少歌词地址 +- `KTVLoadSongStatePreloadFail`: 加载失败 + +## Interface + +### KTVSongConfiguration + +```objective-c +@interface KTVSongConfiguration : NSObject +@property(nonatomic, assign)KTVSongType type; +@property(nonatomic, assign)KTVSingRole role; +@property(nonatomic, assign)NSInteger songCode; +@property(nonatomic, assign)NSInteger mainSingerUid; +@property(nonatomic, assign)NSInteger coSingerUid; ++(KTVSongConfiguration*)configWithSongCode:(NSInteger)songCode; +@end +``` + +K 歌配置: + +- `type`: K 歌场景类型,详见 [KTVSongType](#ktvsongtype) +- `role`: K 歌用户角色类型,详见 [KTVSingRole](#ktvsingrole) +- `songCode`: 歌曲的编号 +- `mainSingerUid`: 主唱的 UID +- `coSingerUid`: 伴唱的 UID + +UID 指用户 ID,用于标识频道内的用户,频道内的每个 UID 都必须是唯一。UID 是 32 位无符号整数,建议取值范围为 [1,232-1]。 \ No newline at end of file diff --git "a/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/iOS/\345\256\236\347\216\260\346\226\207\346\241\243/\345\220\210\345\224\261.wsd" "b/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/iOS/\345\256\236\347\216\260\346\226\207\346\241\243/\345\220\210\345\224\261.wsd" new file mode 100644 index 00000000000..fce77e6be43 --- /dev/null +++ "b/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/iOS/\345\256\236\347\216\260\346\226\207\346\241\243/\345\220\210\345\224\261.wsd" @@ -0,0 +1,41 @@ +@startuml +title 独唱 API 时序图 +autonumber +skinparam monochrome true +participant "主唱/伴唱 App" as a +participant "声网 SDK" as b +participant "听众 App" as c +== 设置私有参数 == +a -> b: setParameters +== 加入频道 == +a -> b: joinChannelByToken +c -> b: joinChannelByToken +== 加载歌曲歌词 == +a -> b: loadSong +c -> b: loadSong +b -->> a: KTVLoadSongStateOK +b -->> c: KTVLoadSongStateOK +== 开始播放歌曲 == +a -> b: playSong(KTVSingRoleMainSinger/KTVSingRoleCoSinger) +b -->> a: didChangedToState(AgoraMediaPlayerStatePlaying) +c -> b: playSong(KTVSingRoleAudience) +b -->> c: didChangedToState(AgoraMediaPlayerStatePlaying) +note left +听众不播放歌曲,只是进入播放状态 +end note +== 停止播放 == +a -> b: stopSong +b -->> a: didChangedToState(AgoraMediaPlayerStateStopped) +c -> b: stopSong +b -->> c: didChangedToState(AgoraMediaPlayerStateStopped) +== 关闭麦克风 == +a -> b: adjustRecordingSignalVolume +== 更新媒体选项 == +a -> b: updateChannelMediaOptions +c -> b: updateChannelMediaOptions +note left +主唱、伴唱、上麦听众发布麦克风,角色为主播 + +普通听众不发布麦克风,角色为观众 +end note +@enduml \ No newline at end of file diff --git "a/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/iOS/\345\256\236\347\216\260\346\226\207\346\241\243/\347\202\271\346\255\214.wsd" "b/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/iOS/\345\256\236\347\216\260\346\226\207\346\241\243/\347\202\271\346\255\214.wsd" new file mode 100644 index 00000000000..8b4cbe4155b --- /dev/null +++ "b/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/iOS/\345\256\236\347\216\260\346\226\207\346\241\243/\347\202\271\346\255\214.wsd" @@ -0,0 +1,27 @@ +@startuml +title 点歌 API 时序图 +autonumber +skinparam monochrome true +participant "App" as a +participant "声网 SDK" as b +== 初始化 KTV API 模块== +a -> b: initWithRtcEngine +== 获取歌曲列表(方式一:用关键词)== +a -> b: loadSearchDataWithKeyWord +b -->> a: onMusicCollectionResult +== 获取歌曲列表(方式二:用音乐榜单)== +a -> b: loadDataWithIndex +b -->> a: onMusicChartsResult +== 加载歌曲 == +a -> b: loadSong +== 开始播放歌曲 == +b -->> a: KTVLoadSongStateOK +a -> b: playSong +b -->> a: didChangedToState(AgoraMediaPlayerStatePlaying) +== 控制歌曲播放 == +a ->b: seek/pause/resume/selectTrackMode +b -->> a: didChangedToState(AgoraMediaPlayerStateXX) +== 停止播放 == +a -> b: stopSong +b -->> a: didChangedToState(AgoraMediaPlayerStateStopped) +@enduml \ No newline at end of file diff --git "a/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/iOS/\345\256\236\347\216\260\346\226\207\346\241\243/\347\213\254\345\224\261.wsd" "b/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/iOS/\345\256\236\347\216\260\346\226\207\346\241\243/\347\213\254\345\224\261.wsd" new file mode 100644 index 00000000000..257aab9c5c5 --- /dev/null +++ "b/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/iOS/\345\256\236\347\216\260\346\226\207\346\241\243/\347\213\254\345\224\261.wsd" @@ -0,0 +1,39 @@ +@startuml +title 独唱 API 时序图 +autonumber +skinparam monochrome true +participant "主唱 App" as a +participant "声网 SDK" as b +participant "听众 App" as c +== 加入频道 == +a -> b: joinChannelByToken +c -> b: joinChannelByToken +== 加载歌曲歌词 == +a -> b: loadSong +c -> b: loadSong +b -->> a: KTVLoadSongStateOK +b -->> c: KTVLoadSongStateOK +== 开始播放歌曲 == +a -> b: playSong(KTVSingRoleMainSinger) +b -->> a: didChangedToState(AgoraMediaPlayerStatePlaying) +c -> b: playSong(KTVSingRoleAudience) +b -->> c: didChangedToState(AgoraMediaPlayerStatePlaying) +note left +听众不播放歌曲,只是进入播放状态 +end note +== 停止播放 == +a -> b: stopSong +b -->> a: didChangedToState(AgoraMediaPlayerStateStopped) +c -> b: stopSong +b -->> c: didChangedToState(AgoraMediaPlayerStateStopped) +== 关闭麦克风 == +a -> b: adjustRecordingSignalVolume +== 更新媒体选项 == +a -> b: updateChannelMediaOptions +c -> b: updateChannelMediaOptions +note left +主唱和上麦听众发布麦克风,角色为主播 + +普通听众不发布麦克风,角色为观众 +end note +@enduml \ No newline at end of file diff --git "a/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/iOS/\345\256\236\347\216\260\346\226\207\346\241\243/\351\233\206\346\210\220.md" "b/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/iOS/\345\256\236\347\216\260\346\226\207\346\241\243/\351\233\206\346\210\220.md" new file mode 100644 index 00000000000..b8d1b9a6fdd --- /dev/null +++ "b/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/iOS/\345\256\236\347\216\260\346\226\207\346\241\243/\351\233\206\346\210\220.md" @@ -0,0 +1,623 @@ +## 概述 + +为降低开发者集成难度,声网为 K 歌房场景提供了定制化场景 API。定制化场景 API 基于场景业务代码逻辑封装了声网音视频 SDK API,让你只需调用一个定制化 API,即可实现通过多个音视频 API 和复杂的代码逻辑才可实现的 K 歌业务功能,例如将合唱和伴唱端进行 NTP 时间同步。 + +本文介绍如何使用 K 歌定制化场景 API 实现点歌、独唱、合唱等基础业务功能。 + +
    开始前请确保你已参考项目配置集成所需 SDK。
    + +## 点歌 + +本节介绍如何实现点歌功能。用户需要在唱歌前进行点歌。点歌指用户通过浏览榜单或搜索关键词选定想唱的正版音乐,然后下载播放音乐。 + +### 方案介绍 + +下图展示点歌的 API 调用时序图: + +![](https://web-cdn.agora.io/docs-files/1678788669061) + +### 1. 初始化 KTV API 模块 + +实例化 `rtcEngine`、`musicCenter`、`mediaPlayer`、`streamId` 实例,并将它们通过 `initWithRtcEngine` 方法传入 KTV API 模块。调用 KTV API 模块的 API 前,请确保已调用 `initWithRtcEngine` 初始化 KTV API 实例。 + +
    考虑到数据流的消息通道有频率限制,为了确保 KTV 模块和其他模块不会相互影响,声网建议你在不同模块中为数据流创建的 streamId 都不同。例如,如果你在其他模块中也使用 sendStreamMessage,请确保两个模块中创建的 streamId 不同。同时,每个用户在每个频道中最多只能创建 5 个数据流,请不要超出上限。
    + +1. 调用 `sharedEngineWithAppId` 初始化 `AgoraRtcEngineKit`。 + + ```objective-c + // 初始化 AgoraRtcEngineKit + // self.RTCkit 是定义的 AgoraRtcEngineKit 全局变量 + self.RTCkit = [AgoraRtcEngineKit sharedEngineWithAppId: delegate:self]; + [self.RTCkit setAudioScenario:AgoraAudioScenarioGameStreaming]; + [self.RTCkit setAudioProfile:AgoraAudioProfileMusicHighQuality]; + [self.RTCkit setChannelProfile:AgoraChannelProfileLiveBroadcasting]; + ``` + +2. 调用 [`sharedContentCenterWithConfig`](https://docs.agora.io/cn/online-ktv/API%20Reference/ios_ng/API/toc_drm.html#api_imusiccontentcenter_initialize) 初始化 `AgoraMusicContentCenter`。示例代码中需要传入 RTM Token。你可以参考[获取 RTM Token](/cn/Agora%20Platform/get_appid_token?platform=All%20Platforms#获取-rtm-token) 了解什么是 RTM Token,如何获取测试用途的临时 RTM Token,如何从服务器生成 RTM Token。 + + ```objective-c + // 初始化 AgoraMusicContentCenter + AgoraMusicContentCenterConfig *contentCenterConfiguration = [[AgoraMusicContentCenterConfig alloc] init]; + contentCenterConfiguration.rtcEngine = self.RTCkit; + contentCenterConfiguration.appId = ""; + contentCenterConfiguration.mccUid = ; + contentCenterConfiguration.token = ""; + // self.AgoraMcc 是定义的 AgoraMusicContentCenter 的全局变量 + self.AgoraMcc = [AgoraMusicContentCenter sharedContentCenterWithConfig:contentCenterConfiguration]; + [self.AgoraMcc registerEventDelegate:]; + [self.AgoraMcc enableMainQueueDispatch:YES]; + ``` + +3. 调用 [`createMusicPlayerWithDelegate`](https://docs.agora.io/cn/online-ktv/API%20Reference/ios_ng/API/toc_drm.html#api_imusiccontentcenter_createmusicplayer) 创建 Media Player + + ```objective-c + // 创建 media player + self.rtcMediaPlayer = [self.AgoraMcc createMusicPlayerWithDelegate:]; + ``` + +4. 调用 [`createDataStream`](https://docs.agora.io/cn/online-ktv/API%20Reference/ios_ng/API/toc_stream_management.html#api_irtcengine_createdatastream2) 创建数据流 + + ```objective-c + // 创建数据流 + AgoraDataStreamConfig *config = [AgoraDataStreamConfig new]; + config.ordered = false; + config.syncWithAudio = false; + // ktvStreamId 是定义的可保存 Stream ID 的全局变量 + [self.RTCkit createDataStream:&ktvStreamId + config:config]; + ``` + +5. 调用 `initWithRtcEngine` 初始化 KTV API 实例 + + ```objective-c + // 初始化 KTV API 实例 + val ktvApiProtocol = KTVApiImpl() + self.ktvApi = [[KTVApi alloc] initWithRtcEngine:self.RTCkit channel: musicCenter:self.AgoraMcc player:self.rtcMediaPlayer dataStreamId:ktvApiStreamId delegate:self]; + ``` + +### 2. 获取歌曲列表 + +通过关键词搜索或音乐榜单获取歌曲列表。 + +```objective-c +// 用关键词搜索歌曲 +- (void)loadSearchDataWithKeyWord:(NSString *)keyWord { + NSDictionary *dict = @{ + @"pitchType":@(1), + @"needLyric": @(YES), // 用来过滤没有歌词的歌曲 + }; + NSString *extra = [NSString convertToJsonData:dict]; + [self.agoraMcc searchMusicWithKeyWord:keyWord ? keyWord : @"" + page:self.page + pageSize:50 + jsonOption:extra]; +} +``` + +```objective-c +// 回调中获取通过关键词搜索的歌曲 +- (void)onMusicCollectionResult:(nonnull NSString *)requestId + status:(AgoraMusicContentCenterStatusCode)status + result:(nonnull AgoraMusicCollection *)result { +} +``` + +```objective-c +// 用音乐榜单获取歌曲 +- (void)loadDataWithIndex:(NSInteger)pageType { + NSArray* chartIds = @[@(3), @(4), @(2), @(6)]; + NSInteger chartId = [[chartIds objectAtIndex:MIN(pageType - 1, chartIds.count - 1)] intValue]; + NSDictionary *dict = @{ + @"pitchType":@(1), + @"needLyric": @(YES), // 用来过滤没有歌词的歌曲 + }; + NSString *extra = [NSString convertToJsonData:dict]; + [self.agoraMcc getMusicCollectionWithMusicChartId:chartId + page:self.page + pageSize:20 + jsonOption:extra]; +} +``` + +```objective-c +// 回调中获取通过音乐榜单得到的歌曲 +- (void)onMusicChartsResult:(NSString *)requestId + status:(AgoraMusicContentCenterStatusCode)status + result:(NSArray *)result { +} +``` + +### 3. 加载歌曲 + +调用 `loadSong` 加载歌曲。该方法中你需要参入编曲编号和 K 歌配置,例如当前的 K 歌唱的场景(合唱或伴唱)、用户角色、主唱伴唱的 UID。K 歌配置会决定 K 歌时歌曲的播放情况、各端用户的收发流情况等。歌曲加载结果会异步地通过 `withCallback` 回调通知你。 + + +```objective-c +// KTVSongType: 歌唱类型(合唱或独唱) +// KTVSingRole: 用户角色 +// songCode: 歌曲的编号 +// mainSingerUid: 主唱 UID +// coSingerUid: 伴唱 UID。如果是独唱场景,不存在伴唱角色,那么传入 0 即可。 +KTVSongConfiguration *config = [[KTVSongConfiguration alloc] init]; +config.type = KTVSongTypeChorus | KTVSongTypeSolo; +config.role = KTVSingRoleMainSinger | KTVSingRoleCoSinger | KTVSingRoleAudience; +config.songCode = ; +config.mainSingerUid = ; +config.coSingerUid = ; + +// 加载歌曲 +[self.ktvApi loadSong:songCode withConfig:config withCallback:^(NSInteger songCode, NSString * _Nonnull lyricUrl, KTVSingRole role, KTVLoadSongState state) { + KTVLogInfo(@"loadSong result: %ld", state); + if(state == KTVLoadSongStateOK) { + // 歌曲加载成功的操作 + ... + } else if(state == KTVLoadSongStateNoLyricUrl) { + // 歌曲加载失败,重试三次 + ... + } +}]; +``` + + +### 4. 播放歌曲 + +收到 `KTVLoadSongStateOK` 回调状态后,调用 `playSong` 开始播放歌曲。 + +```objective-c +[self.ktvApi loadSong:songCode withConfig:config withCallback:^(NSInteger songCode, NSString * _Nonnull lyricUrl, KTVSingRole role, KTVLoadSongState state) { + KTVLogInfo(@"loadSong result: %ld", state); + if(state == KTVLoadSongStateOK) { + // 歌曲加载成功,则播放歌曲 + [self.ktvApi playSong:songCode]; + } else if(state == KTVLoadSongStateNoLyricUrl) { + // 否则重试三次 + if(weakSelf.retryCount < 2) { + KTVLogInfo(@"songName: %@, songNo: %@, retryCount: %lu",model.songName, model.songNo,(unsigned long)weakSelf.retryCount); + [weakSelf loadSongWithModel:model config:config]; + weakSelf.retryCount++; + } + } +}]; +``` + +### 5. 监听并控制歌曲播放 + +歌曲播放时,音乐播放器会通过 `didChangedToState` 回调向业务层通知歌曲播放状态改变。收到 `didChangedToState(AgoraMediaPlayerStatePlaying)` 回调后,你可以使用 `seek`、`pause`、`resume`、`selectAudioTrack` 等方法控制播放器。 + +
    KTV API 模块内部会自动处理播放器同步,因此你也可以通过 `didChangedToState` 回调获取远端播放器的状态。
    + +```objective-c +// 跳转到指定时间播放歌曲 +[self.ktvApi seek: time]; +``` + +### 6. 停止歌曲播放 + +当歌曲播放完成或用户中途结束播放时,你需要主动调用 `stopSong` 停止播放。 + +```objective-c +[self.ktvApi stopSong]; +``` + + +## 独唱 + +本节介绍如何实现独唱功能。主唱点歌后,可以开始独唱,K 歌房内的听众都可以听到这位主唱唱歌。房间内想与主唱连麦语聊的听众可以上麦。 + +### 方案介绍 + +独唱场景下存在两种角色: + +- 主唱:加入频道,加载并播放歌曲,发布麦克风采集的音频流。KTV API 模块内部控制音乐播放器播放音乐,发布音乐到远端,将音乐播放进度同步到远端,让歌词组件进入歌词滚动状态等逻辑。 +- 听众:加入频道,加载歌曲。KTV API 模块内部控制听众订阅主唱的人声和音乐的音频合流,同步主唱的音乐播放进度,让歌词组件开始进入播放状态等逻辑。如果普通观众需要上麦聊天,可以更新媒体选项。 + +![](https://web-cdn.agora.io/docs-files/1678784206362) + +下图展示独唱的 API 调用时序图: + +![](https://web-cdn.agora.io/docs-files/1678788677280) + +### 主唱实现 + +#### 1. 加入频道 + +调用 [`joinChannelByToken`](https://docs.agora.io/cn/online-ktv/API%20Reference/ios_ng/API/toc_core_method.html#api_irtcengine_joinchannel2) 让主唱加入频道。 + +```objective-c +// 加入频道 +[self.RTCKit joinChannelByToken: + channelId: + info:nil + uid: + // 媒体选项详见第 5 步操作 + mediaOptions:mediaOption + joinSuccess:nil]; +``` + + +#### 2. 播放歌曲 + +调用 `loadSong` 加载歌曲。歌曲加载结果会异步地通过 `withCallback` 回调通知你。收到 `KTVLoadSongStateOK` 回调状态后,调用 `playSong` 开始播放歌曲。 + +```objective-c +KTVSongConfiguration *config = [[KTVSongConfiguration alloc] init]; +config.type = KTVSongTypeSolo; +config.role = KTVSingRoleMainSinger; +config.songCode = ; +config.mainSingerUid = ; +// 独唱场景不存在伴唱角色,那么传入 0 即可。 +config.coSingerUid = 0; +[self.ktvApi loadSong:songCode withConfig:config withCallback:^(NSInteger songCode, NSString * _Nonnull lyricUrl, KTVSingRole role, KTVLoadSongState state) { + KTVLogInfo(@"loadSong result: %ld", state); + if(state == KTVLoadSongStateOK) { + // 歌曲加载成功,则播放歌曲 + [self.ktvApi playSong:songCode]; + } else if(state == KTVLoadSongStateNoLyricUrl) { + // 歌曲加载失败,重试三次 + ... + } +}]; +``` + +#### 3. 停止播放 + +当歌曲播放完成或用户中途结束播放时,你需要主动调用 `stopSong` 停止播放。 + +```objective-c +[self.ktvApi stopSong]; +``` + +#### 4. 关闭麦克风 + +主唱停止唱歌或希望暂时关闭麦克风时,可以调用 [`adjustRecordingSignalVolume`](https://docs.agora.io/cn/online-ktv/API%20Reference/ios_ng/API/toc_audio_process.html#api_irtcengine_adjustrecordingsignalvolume),将音频采集信号音量设置为 0。 + +```objective-c +[self.RTCKit adjustRecordingSignalVolume: 0]; +``` + +#### 5. 根据角色更新媒体选项 + +通过 [`updateChannelWithMediaOptions`](https://docs.agora.io/cn/online-ktv/API%20Reference/ios_ng/API/toc_core_method.html#api_irtcengine_updatechannelmediaoptions) 方法在主播加入频道后更新频道媒体选项,例如是否开启本地音频采集,是否发布本地音频流等。 + +```objective-c +AgoraRtcChannelMediaOptions* options = [AgoraRtcChannelMediaOptions new]; +// 发布本地麦克风流 +options.publishMicrophoneTrack = YES; +// 启用音频采集和播放 +options.enableAudioRecordingOrPlayout = YES; +// 设置角色为主播 +options.clientRoleType = AgoraClientRoleBroadcaster; +// 更新媒体选项 +[self.RTCKit updateChannelWithMediaOptions:options]; +``` + +### 听众实现 + +#### 1. 加入频道 + +调用 [`joinChannelByToken`](https://docs.agora.io/cn/online-ktv/API%20Reference/ios_ng/API/toc_core_method.html#api_irtcengine_joinchannel2) 让听众加入频道。 + +```objective-c +// 加入频道 +[self.RTCKit joinChannelByToken: + channelId: + info:nil + uid: + // 媒体选项详见第 4 步操作 + mediaOptions:mediaOption + joinSuccess:nil]; +``` + +#### 2. 加载歌曲 + +调用 `loadSong` 加载歌曲。加载结果会异步地通过 `withCallback` 回调通知你。收到 `withCallback(KTVLoadSongStateOK)` 回调状态后,调用 `playSong`。 + +听众加入频道后,默认订阅主唱发布的音频合流,即主唱人声和音乐混合的音频流。听众只需调用 `playSong` 进入歌曲播放状态即可。 + +```objective-c +KTVSongConfiguration *config = [[KTVSongConfiguration alloc] init]; +config.type = KTVSongTypeSolo; +config.role = KTVSingRoleAudience; +config.songCode = ; +// 听众可以不传演唱者的 UID +config.mainSingerUid = 0; +config.coSingerUid = 0; +[self.ktvApi loadSong:songCode withConfig:config withCallback:^(NSInteger songCode, NSString * _Nonnull lyricUrl, KTVSingRole role, KTVLoadSongState state) { + KTVLogInfo(@"loadSong result: %ld", state); + if(state == KTVLoadSongStateOK) { + // 歌曲加载成功,则播放歌曲 + [self.ktvApi playSong:songCode]; + } else if(state == KTVLoadSongStateNoLyricUrl) { + // 歌曲加载失败,重试三次 + ... + } +}]; +``` + +#### 3. 停止播放 + +当歌曲播放完成或用户中途结束播放时,你需要主动调用 `stopSong` 停止播放。 + +```objective-c +[self.ktvApi stopSong]; +``` + +#### 4. 根据角色更新媒体选项 + +通过 [`updateChannelWithMediaOptions`](https://docs.agora.io/cn/online-ktv/API%20Reference/ios_ng/API/toc_core_method.html#api_irtcengine_updatechannelmediaoptions) 方法在听众加入频道后更新频道媒体选项,例如是否开启本地音频采集,是否发布本地音频流等。 + +听众的用户角色为 `AgoraClientRoleAudience`,因此无法在频道内发布音频流。如果听众想上麦与主唱语聊,需要将用户角色修改为 `AgoraClientRoleBroadcaster`。修改角色后,SDK 默认发布该连麦听众的音频流,主唱和其他听众都能听到连麦听众的声音。 + +```objective-c +// 针对需要上麦聊天的听众更新媒体选项 +AgoraRtcChannelMediaOptions* options = [AgoraRtcChannelMediaOptions new]; +// 发布本地麦克风流 +options.publishMicrophoneTrack = true +// 启用音频采集和播放 +options.enableAudioRecordingOrPlayout = true +// 设置角色为主播 +options.clientRoleType = AgoraClientRoleBroadcaster +// 更新媒体选项 +[self.RTCKit updateChannelWithMediaOptions:options]; + + +// 针对未上麦的听众更新媒体选项 +AgoraRtcChannelMediaOptions* options = [AgoraRtcChannelMediaOptions new]; +// 不发布本地麦克风流 +options.publishMicrophoneTrack = false +// 启用音频采集和播放 +options.enableAudioRecordingOrPlayout = true +// 设置角色为用户 +options.clientRoleType = AgoraClientRoleAudience +// 更新媒体选项 +[self.RTCKit updateChannelWithMediaOptions:options]; +``` + +## 合唱 + +本节介绍如何实现合唱功能。主唱点歌开唱后,伴唱可以和主唱一起唱歌,K 歌房内的听众都可以听到合唱。房间内想与主唱或伴唱连麦语聊的听众可以上麦。 + +### 方案介绍 + +合唱场景下存在三种角色: + +- 主唱:加入频道,加载并播放歌曲,发布麦克风采集的音频流。KTV API 模块内部控制音乐播放器播放音乐,发布音乐到远端,将音乐播放进度同步到远端,让歌词组件进入歌词滚动状态等逻辑。 +- 伴唱:加入频道,加载并播放歌曲,发布麦克风采集的音频流。KTV API 模块内部控制音乐播放器播放音乐,同步主唱的音乐播放进度,让歌词组件进入歌词滚动状态等逻辑。 +- 听众:加入频道,加载歌曲。KTV API 模块内部控制听众订阅主唱的人声和音乐的音频合流,同步主唱的音乐播放进度,让歌词组件进入歌词滚动状态等逻辑。如果普通观众需要上麦聊天,可以更新媒体选项。 + +![](https://web-cdn.agora.io/docs-files/1678784685024) + +下图展示合唱的 API 调用时序图: + +![](https://web-cdn.agora.io/docs-files/1678788686786) + +### 主唱实现 + +#### 1. 设置合唱私有参数 + +实时合唱场景对低延时和音质的要求很高。开启合唱前,你需要在初始化 AgoraRtcEngineKit 的方法里设置如下私有参数。 + +```objective-c +[self.RTCKit setParameters:@"{\"rtc.ntp_delay_drop_threshold\":1000}"]; +[self.RTCKit setParameters:@"{\"rtc.enable_nasa2\": false}"]; +[self.RTCKit setParameters:@"{\"rtc.video.enable_sync_render_ntp\": true}"]; +[self.RTCKit setParameters:@"{\"rtc.net.maxS2LDelay\": 800}"]; +``` + +#### 2. 加入频道 + +调用 [`joinChannelByToken`](https://docs.agora.io/cn/online-ktv/API%20Reference/ios_ng/API/toc_core_method.html#api_irtcengine_joinchannel2) 让主唱加入频道。 + +```objective-c +// 加入频道 +[self.RTCKit joinChannelByToken: + channelId: + info:nil + uid: + // 媒体选项详见第 5 步操作 + mediaOptions:mediaOption + joinSuccess:nil]; +``` + +#### 3. 开始合唱 + +调用 `loadSong` 加载歌曲。歌曲加载结果会异步地通过 `withCallback` 回调通知你。收到 `KTVLoadSongStateOK` 回调状态后,调用 `playSong` 开始播放歌曲,开始合唱。 + +```objective-c +KTVSongConfiguration *config = [[KTVSongConfiguration alloc] init]; +config.type = KTVSongTypeChorus; +config.role = KTVSingRoleMainSinger; +config.songCode = ; +config.mainSingerUid = ; +// 主唱可以不填伴唱 UID,传 0 即可。 +config.coSingerUid = 0; +[self.ktvApi loadSong:songCode withConfig:config withCallback:^(NSInteger songCode, NSString * _Nonnull lyricUrl, KTVSingRole role, KTVLoadSongState state) { + KTVLogInfo(@"loadSong result: %ld", state); + if(state == KTVLoadSongStateOK) { + // 歌曲加载成功,则播放歌曲 + [self.ktvApi playSong:songCode]; + } else if(state == KTVLoadSongStateNoLyricUrl) { + // 歌曲加载失败,重试三次 + ... + } +}]; +``` + +#### 4. 停止合唱 + +当歌曲播放完成或用户中途结束播放时,你需要主动调用 `stopSong` 停止播放,停止合唱。 + +```objective-c +[self.ktvApi stopSong]; +``` + +#### 5. 根据角色更新媒体选项 + +通过 [`updateChannelWithMediaOptions`](https://docs.agora.io/cn/online-ktv/API%20Reference/ios_ng/API/toc_core_method.html#api_irtcengine_updatechannelmediaoptions) 方法在主播加入频道后更新频道媒体选项,例如是否开启本地音频采集,是否发布本地音频流等。 + +```objective-c +AgoraRtcChannelMediaOptions* options = [AgoraRtcChannelMediaOptions new]; +// 发布本地麦克风流 +options.publishMicrophoneTrack = YES; +// 启用音频采集和播放 +options.enableAudioRecordingOrPlayout = YES; +// 设置角色为主播 +options.clientRoleType = AgoraClientRoleBroadcaster; +// 更新媒体选项 +[self.RTCKit updateChannelWithMediaOptions:options]; +``` + +### 伴唱实现 + +#### 1. 设置合唱私有参数 + +实时合唱场景对低延时和音质的要求很高。开启合唱前,你需要在初始化 AgoraRtcEngineKit 的方法里设置如下私有参数。 + +```objective-c +[self.RTCKit setParameters:@"{\"rtc.ntp_delay_drop_threshold\":1000}"]; +[self.RTCKit setParameters:@"{\"rtc.enable_nasa2\": false}"]; +[self.RTCKit setParameters:@"{\"rtc.video.enable_sync_render_ntp\": true}"]; +[self.RTCKit setParameters:@"{\"rtc.net.maxS2LDelay\": 800}"]; +``` + +#### 2. 加入频道 + +调用 [`joinChannelByToken`](https://docs.agora.io/cn/online-ktv/API%20Reference/ios_ng/API/toc_core_method.html#api_irtcengine_joinchannel2) 让伴唱加入频道。 + +```objective-c +// 加入频道 +[self.RTCKit joinChannelByToken: + channelId: + info:nil + uid: + // 媒体选项详见第 5 步操作 + mediaOptions:mediaOption + joinSuccess:nil]; +``` + +#### 3. 开始合唱 + +调用 `loadSong` 加载歌曲。歌曲加载结果会异步地通过 `withCallback` 回调通知你。收到 `KTVLoadSongStateOK` 回调状态后,调用 `playSong` 开始播放歌曲,开始合唱。 + +```objective-c +KTVSongConfiguration *config = [[KTVSongConfiguration alloc] init]; +config.type = KTVSongTypeChorus; +config.role = KTVSingRoleCoSinger; +config.songCode = ; +// 伴唱一定要填主唱 UID +config.mainSingerUid = ; +config.coSingerUid = ; +[self.ktvApi loadSong:songCode withConfig:config withCallback:^(NSInteger songCode, NSString * _Nonnull lyricUrl, KTVSingRole role, KTVLoadSongState state) { + KTVLogInfo(@"loadSong result: %ld", state); + if(state == KTVLoadSongStateOK) { + // 歌曲加载成功,则播放歌曲 + [self.ktvApi playSong:songCode]; + } else if(state == KTVLoadSongStateNoLyricUrl) { + // 歌曲加载失败,重试三次 + ... + } +}]; +``` + +#### 4. 停止合唱 + +当歌曲播放完成或用户中途结束播放时,你需要主动调用 `stopSong` 停止播放,停止合唱。 + +```objective-c +[self.ktvApi stopSong]; +``` + +#### 5. 根据角色更新媒体选项 + +通过 [`updateChannelWithMediaOptions`](https://docs.agora.io/cn/online-ktv/API%20Reference/ios_ng/API/toc_core_method.html#api_irtcengine_updatechannelmediaoptions) 方法在伴唱加入频道后更新频道媒体选项,例如是否开启本地音频采集,是否发布本地音频流等。 + +```objective-c +AgoraRtcChannelMediaOptions* options = [AgoraRtcChannelMediaOptions new]; +// 发布本地麦克风流 +options.publishMicrophoneTrack = YES; +// 启用音频采集和播放 +options.enableAudioRecordingOrPlayout = YES; +// 设置角色为主播 +options.clientRoleType = AgoraClientRoleBroadcaster; +// 更新媒体选项 +[self.RTCKit updateChannelWithMediaOptions:options]; +``` + +### 听众实现 + +#### 1. 加入频道 + +调用 [`joinChannelByToken`](https://docs.agora.io/cn/online-ktv/API%20Reference/ios_ng/API/toc_core_method.html#api_irtcengine_joinchannel2) 让听众加入频道。 + +```objective-c +// 加入频道 +[self.RTCKit joinChannelByToken: + channelId: + info:nil + uid: + // 媒体选项详见第 4 步操作 + mediaOptions:mediaOption + joinSuccess:nil]; +``` + +#### 2. 加载歌曲 + +调用 `loadSong` 加载歌曲。加载结果会异步地通过 `withCallback` 回调通知你。收到 `withCallback(KTVLoadSongStateOK)` 回调状态后,调用 `playSong`。 + +听众加入频道后,默认订阅主唱发布的音频合流,即主唱人声和音乐混合的音频流。听众只需调用 `playSong` 进入歌曲播放状态即可。 + +```objective-c +KTVSongConfiguration *config = [[KTVSongConfiguration alloc] init]; +config.type = KTVSongTypeChorus; +config.role = KTVSingRoleAudience; +config.songCode = ; +// 听众可以不传演唱者的 UID +config.mainSingerUid = 0; +config.coSingerUid = 0; +[self.ktvApi loadSong:songCode withConfig:config withCallback:^(NSInteger songCode, NSString * _Nonnull lyricUrl, KTVSingRole role, KTVLoadSongState state) { + KTVLogInfo(@"loadSong result: %ld", state); + if(state == KTVLoadSongStateOK) { + // 歌曲加载成功,则播放歌曲 + [self.ktvApi playSong:songCode]; + } +}]; +``` + +#### 3. 停止播放 + +当歌曲播放完成或用户中途结束播放时,你需要主动调用 `stopSong` 停止播放。 + +```objective-c +[self.ktvApi stopSong]; +``` + +#### 4. 根据角色更新媒体选项 + +通过 [`updateChannelWithMediaOptions`](https://docs.agora.io/cn/online-ktv/API%20Reference/ios_ng/API/toc_core_method.html#api_irtcengine_updatechannelmediaoptions) 方法在听众加入频道后更新频道媒体选项,例如是否开启本地音频采集,是否发布本地音频流等。 + +听众的用户角色为 `AgoraClientRoleAudience`,因此无法在频道内发布音频流。如果听众想上麦与主唱语聊,需要将用户角色修改为 `AgoraClientRoleBroadcaster`。修改角色后,SDK 默认发布该连麦听众的音频流,主唱和其他听众都能听到连麦听众的声音。 + +```objective-c +// 针对需要上麦聊天的听众更新媒体选项 +AgoraRtcChannelMediaOptions* options = [AgoraRtcChannelMediaOptions new]; +// 发布本地麦克风流 +options.publishMicrophoneTrack = true +// 启用音频采集和播放 +options.enableAudioRecordingOrPlayout = true +// 设置角色为主播 +options.clientRoleType = AgoraClientRoleBroadcaster +// 更新媒体选项 +[self.RTCKit updateChannelWithMediaOptions:options]; + + +// 针对未上麦的听众更新媒体选项 +AgoraRtcChannelMediaOptions* options = [AgoraRtcChannelMediaOptions new]; +// 不发布本地麦克风流 +options.publishMicrophoneTrack = false +// 启用音频采集和播放 +options.enableAudioRecordingOrPlayout = true +// 设置角色为用户 +options.clientRoleType = AgoraClientRoleAudience +// 更新媒体选项 +[self.RTCKit updateChannelWithMediaOptions:options]; +``` \ No newline at end of file diff --git "a/markdown/online-ktv/\346\224\271\350\277\233/iOS/img/\345\220\210\345\224\261.drawio.svg" "b/markdown/online-ktv/\346\224\271\350\277\233/\351\235\236\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/iOS/img/\345\220\210\345\224\261.drawio.svg" similarity index 100% rename from "markdown/online-ktv/\346\224\271\350\277\233/iOS/img/\345\220\210\345\224\261.drawio.svg" rename to "markdown/online-ktv/\346\224\271\350\277\233/\351\235\236\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/iOS/img/\345\220\210\345\224\261.drawio.svg" diff --git "a/markdown/online-ktv/\346\224\271\350\277\233/iOS/img/\347\202\271\346\255\214.drawio.svg" "b/markdown/online-ktv/\346\224\271\350\277\233/\351\235\236\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/iOS/img/\347\202\271\346\255\214.drawio.svg" similarity index 100% rename from "markdown/online-ktv/\346\224\271\350\277\233/iOS/img/\347\202\271\346\255\214.drawio.svg" rename to "markdown/online-ktv/\346\224\271\350\277\233/\351\235\236\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/iOS/img/\347\202\271\346\255\214.drawio.svg" diff --git "a/markdown/online-ktv/\346\224\271\350\277\233/iOS/img/\347\213\254\345\224\261.drawio.svg" "b/markdown/online-ktv/\346\224\271\350\277\233/\351\235\236\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/iOS/img/\347\213\254\345\224\261.drawio.svg" similarity index 100% rename from "markdown/online-ktv/\346\224\271\350\277\233/iOS/img/\347\213\254\345\224\261.drawio.svg" rename to "markdown/online-ktv/\346\224\271\350\277\233/\351\235\236\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/iOS/img/\347\213\254\345\224\261.drawio.svg" diff --git "a/markdown/online-ktv/\346\224\271\350\277\233/iOS/\345\220\210\345\224\261.md" "b/markdown/online-ktv/\346\224\271\350\277\233/\351\235\236\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/iOS/\345\220\210\345\224\261.md" similarity index 100% rename from "markdown/online-ktv/\346\224\271\350\277\233/iOS/\345\220\210\345\224\261.md" rename to "markdown/online-ktv/\346\224\271\350\277\233/\351\235\236\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/iOS/\345\220\210\345\224\261.md" diff --git "a/markdown/online-ktv/\346\224\271\350\277\233/iOS/\347\202\271\346\255\214.md" "b/markdown/online-ktv/\346\224\271\350\277\233/\351\235\236\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/iOS/\347\202\271\346\255\214.md" similarity index 100% rename from "markdown/online-ktv/\346\224\271\350\277\233/iOS/\347\202\271\346\255\214.md" rename to "markdown/online-ktv/\346\224\271\350\277\233/\351\235\236\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/iOS/\347\202\271\346\255\214.md" diff --git "a/markdown/online-ktv/\346\224\271\350\277\233/iOS/\347\202\271\346\255\214.wsd" "b/markdown/online-ktv/\346\224\271\350\277\233/\351\235\236\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/iOS/\347\202\271\346\255\214.wsd" similarity index 100% rename from "markdown/online-ktv/\346\224\271\350\277\233/iOS/\347\202\271\346\255\214.wsd" rename to "markdown/online-ktv/\346\224\271\350\277\233/\351\235\236\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/iOS/\347\202\271\346\255\214.wsd" diff --git "a/markdown/online-ktv/\346\224\271\350\277\233/iOS/\347\213\254\345\224\261.md" "b/markdown/online-ktv/\346\224\271\350\277\233/\351\235\236\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/iOS/\347\213\254\345\224\261.md" similarity index 100% rename from "markdown/online-ktv/\346\224\271\350\277\233/iOS/\347\213\254\345\224\261.md" rename to "markdown/online-ktv/\346\224\271\350\277\233/\351\235\236\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/iOS/\347\213\254\345\224\261.md" diff --git "a/markdown/online-ktv/\346\224\271\350\277\233/iOS/\351\233\206\346\210\220\346\246\202\350\277\260" "b/markdown/online-ktv/\346\224\271\350\277\233/\351\235\236\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/iOS/\351\233\206\346\210\220\346\246\202\350\277\260" similarity index 100% rename from "markdown/online-ktv/\346\224\271\350\277\233/iOS/\351\233\206\346\210\220\346\246\202\350\277\260" rename to "markdown/online-ktv/\346\224\271\350\277\233/\351\235\236\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/iOS/\351\233\206\346\210\220\346\246\202\350\277\260" From bd7f0c2453dcb39b54098e918fb349e128fda7af Mon Sep 17 00:00:00 2001 From: kelzr Date: Fri, 17 Mar 2023 14:44:33 +0800 Subject: [PATCH 10/20] =?UTF-8?q?[KTVI]=20=E5=9C=BA=E6=99=AF=E5=8C=96=20AP?= =?UTF-8?q?I=20=E6=96=B9=E6=A1=88=20fixed?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../\345\220\210\345\224\261.wsd" | 2 +- .../\351\233\206\346\210\220.md" | 2 +- ...57\345\214\226 Objective-C API for iOS.md" | 21 +++++++++++++++++-- .../\345\220\210\345\224\261.wsd" | 2 +- .../\351\233\206\346\210\220.md" | 2 +- 5 files changed, 23 insertions(+), 6 deletions(-) diff --git "a/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/Android/\345\256\236\347\216\260\346\226\207\346\241\243/\345\220\210\345\224\261.wsd" "b/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/Android/\345\256\236\347\216\260\346\226\207\346\241\243/\345\220\210\345\224\261.wsd" index 7fa1af716fa..ad9312341d0 100644 --- "a/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/Android/\345\256\236\347\216\260\346\226\207\346\241\243/\345\220\210\345\224\261.wsd" +++ "b/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/Android/\345\256\236\347\216\260\346\226\207\346\241\243/\345\220\210\345\224\261.wsd" @@ -1,5 +1,5 @@ @startuml -title 独唱 API 时序图 +title 合唱 API 时序图 autonumber skinparam monochrome true participant "主唱/伴唱 App" as a diff --git "a/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/Android/\345\256\236\347\216\260\346\226\207\346\241\243/\351\233\206\346\210\220.md" "b/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/Android/\345\256\236\347\216\260\346\226\207\346\241\243/\351\233\206\346\210\220.md" index 37d42bd0dcf..ec7ea688c70 100644 --- "a/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/Android/\345\256\236\347\216\260\346\226\207\346\241\243/\351\233\206\346\210\220.md" +++ "b/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/Android/\345\256\236\347\216\260\346\226\207\346\241\243/\351\233\206\346\210\220.md" @@ -362,7 +362,7 @@ mRtcEngine.updateChannelMediaOptions(channelMediaOption) 下图展示合唱的 API 调用时序图: -![](https://web-cdn.agora.io/docs-files/1678788560007) +![](https://web-cdn.agora.io/docs-files/1678866284780) ### 主唱实现 diff --git "a/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/iOS/K \346\255\214\346\210\277\345\234\272\346\231\257\345\214\226 Objective-C API for iOS.md" "b/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/iOS/K \346\255\214\346\210\277\345\234\272\346\231\257\345\214\226 Objective-C API for iOS.md" index 10748d7fe74..9e4300a0b49 100644 --- "a/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/iOS/K \346\255\214\346\210\277\345\234\272\346\231\257\345\214\226 Objective-C API for iOS.md" +++ "b/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/iOS/K \346\255\214\346\210\277\345\234\272\346\231\257\345\214\226 Objective-C API for iOS.md" @@ -29,7 +29,7 @@ - `musicCenter`: 版权音乐内容中心实例。详见 [AgoraMusicContentCenter](https://docs.agora.io/cn/online-ktv/API%20Reference/ios_ng/API/rtc_interface_class.html#class_imusiccontentcenter)。 - `player`: 音乐播放器实例。详见 [AgoraMusicPlayerProtocol](https://docs.agora.io/cn/online-ktv/API%20Reference/ios_ng/API/rtc_interface_class.html#class_imusicplayer)。 - `dataStreamId`: 数据流(Data Stream)ID。 -- `delegate`: KTVApiDelegate +- `delegate`: [KTVApiDelegate](#didchangedtostate)。 ### release @@ -159,7 +159,24 @@ withCallback:(void (^ _Nullable)(NSInteger songCode, NSString* lyricUrl, KTVSing ## 回调 -//TODO +### didChangedToState + +```objective-c +@protocol KTVApiDelegate + - (void)controller:(KTVApi*)controller song:(NSInteger)songCode didChangedToState:(AgoraMediaPlayerState)state local:(BOOL)local; +@end +``` +播放器状态改变回调。 + +#### 参数 + +- `controller`: KTVApi 实例。 +- `songCode`: 歌曲编号。 +- `state`: 播放器的当前状态。详见 [AgoraMediaPlayerState](https://docs.agora.io/cn/online-ktv/API%20Reference/ios_ng/API/enum_mediaplayerstate.html?platform=iOS)。 +- `local`: 是否为本地事件: + - `YES`: 是本地事件。 + - `NO`: 不是本地事件。 + ## Enum diff --git "a/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/iOS/\345\256\236\347\216\260\346\226\207\346\241\243/\345\220\210\345\224\261.wsd" "b/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/iOS/\345\256\236\347\216\260\346\226\207\346\241\243/\345\220\210\345\224\261.wsd" index fce77e6be43..9bd1c232159 100644 --- "a/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/iOS/\345\256\236\347\216\260\346\226\207\346\241\243/\345\220\210\345\224\261.wsd" +++ "b/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/iOS/\345\256\236\347\216\260\346\226\207\346\241\243/\345\220\210\345\224\261.wsd" @@ -1,5 +1,5 @@ @startuml -title 独唱 API 时序图 +title 合唱 API 时序图 autonumber skinparam monochrome true participant "主唱/伴唱 App" as a diff --git "a/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/iOS/\345\256\236\347\216\260\346\226\207\346\241\243/\351\233\206\346\210\220.md" "b/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/iOS/\345\256\236\347\216\260\346\226\207\346\241\243/\351\233\206\346\210\220.md" index b8d1b9a6fdd..c25a300dafc 100644 --- "a/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/iOS/\345\256\236\347\216\260\346\226\207\346\241\243/\351\233\206\346\210\220.md" +++ "b/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/iOS/\345\256\236\347\216\260\346\226\207\346\241\243/\351\233\206\346\210\220.md" @@ -389,7 +389,7 @@ options.clientRoleType = AgoraClientRoleAudience 下图展示合唱的 API 调用时序图: -![](https://web-cdn.agora.io/docs-files/1678788686786) +![](https://web-cdn.agora.io/docs-files/1678866367772) ### 主唱实现 From d802103987f0dd82e49e85107320ecbff145a902 Mon Sep 17 00:00:00 2001 From: kelzr Date: Tue, 28 Mar 2023 18:24:23 +0800 Subject: [PATCH 11/20] =?UTF-8?q?[KTVI]=20=E5=9C=BA=E6=99=AF=E5=8C=96=20AP?= =?UTF-8?q?I=20=E6=96=B9=E6=A1=88=20fixed?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...272\346\231\257\345\214\226 Kotlin API for Android.md" | 2 +- .../\351\233\206\346\210\220.md" | 8 +++++++- ...72\346\231\257\345\214\226 Objective-C API for iOS.md" | 2 +- .../\351\233\206\346\210\220.md" | 8 +++++++- 4 files changed, 16 insertions(+), 4 deletions(-) diff --git "a/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/Android/K \346\255\214\346\210\277\345\234\272\346\231\257\345\214\226 Kotlin API for Android.md" "b/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/Android/K \346\255\214\346\210\277\345\234\272\346\231\257\345\214\226 Kotlin API for Android.md" index 85373b4e4b7..cf2aac70fe7 100644 --- "a/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/Android/K \346\255\214\346\210\277\345\234\272\346\231\257\345\214\226 Kotlin API for Android.md" +++ "b/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/Android/K \346\255\214\346\210\277\345\234\272\346\231\257\345\214\226 Kotlin API for Android.md" @@ -44,7 +44,7 @@ fun release() 调用该方法可以清空 KTV API 模块内部变量和缓存数据,取消 `ktvApiEventHandler` 的事件监听,取消网络请求等。 -### 用法示例 +#### 用法示例 ```kotlin // K 歌房 Activity 销毁时调用 ktvApiProtocol 释放,随后释放创建的实例 diff --git "a/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/Android/\345\256\236\347\216\260\346\226\207\346\241\243/\351\233\206\346\210\220.md" "b/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/Android/\345\256\236\347\216\260\346\226\207\346\241\243/\351\233\206\346\210\220.md" index ec7ea688c70..bd70596595d 100644 --- "a/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/Android/\345\256\236\347\216\260\346\226\207\346\241\243/\351\233\206\346\210\220.md" +++ "b/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/Android/\345\256\236\347\216\260\346\226\207\346\241\243/\351\233\206\346\210\220.md" @@ -569,4 +569,10 @@ channelMediaOption.enableAudioRecordingOrPlayout = true channelMediaOption.clientRoleType = CLIENT_ROLE_AUDIENCE // 更新媒体选项 mRtcEngine.updateChannelMediaOptions(channelMediaOption) -``` \ No newline at end of file +``` + +## API 参考 + +本文集成步骤中使用如下 API: +- [RTC API](/cn/online-ktv/API%20Reference/java_ng/API/rtc_api_overview_ng.html) +- [场景化 API](/cn/online-ktv/ktv_api_kotlin?platform=Android) \ No newline at end of file diff --git "a/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/iOS/K \346\255\214\346\210\277\345\234\272\346\231\257\345\214\226 Objective-C API for iOS.md" "b/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/iOS/K \346\255\214\346\210\277\345\234\272\346\231\257\345\214\226 Objective-C API for iOS.md" index 9e4300a0b49..3d4e1de426f 100644 --- "a/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/iOS/K \346\255\214\346\210\277\345\234\272\346\231\257\345\214\226 Objective-C API for iOS.md" +++ "b/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/iOS/K \346\255\214\346\210\277\345\234\272\346\231\257\345\214\226 Objective-C API for iOS.md" @@ -42,7 +42,7 @@ fun release() 调用该方法可以清空 KTV API 模块内部变量和缓存数据,取消 `ktvApiEventHandler` 的事件监听,取消网络请求等。 -### 用法示例 +#### 用法示例 ```objective-c // K 歌房 Activity 销毁时调用 ktvApiProtocol 释放,随后释放创建的实例 diff --git "a/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/iOS/\345\256\236\347\216\260\346\226\207\346\241\243/\351\233\206\346\210\220.md" "b/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/iOS/\345\256\236\347\216\260\346\226\207\346\241\243/\351\233\206\346\210\220.md" index c25a300dafc..3155368c358 100644 --- "a/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/iOS/\345\256\236\347\216\260\346\226\207\346\241\243/\351\233\206\346\210\220.md" +++ "b/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/iOS/\345\256\236\347\216\260\346\226\207\346\241\243/\351\233\206\346\210\220.md" @@ -620,4 +620,10 @@ options.enableAudioRecordingOrPlayout = true options.clientRoleType = AgoraClientRoleAudience // 更新媒体选项 [self.RTCKit updateChannelWithMediaOptions:options]; -``` \ No newline at end of file +``` + +## API 参考 + +本文集成步骤中使用如下 API: +- [RTC API](/cn/online-ktv/API%20Reference/ios_ng/API/rtc_api_overview_ng.html) +- [场景化 API](/cn/online-ktv/ktv_api_oc?platform=iOS) \ No newline at end of file From 7bf53b3b607fa5237cd41aa71a652512b9d47c8d Mon Sep 17 00:00:00 2001 From: kelzr Date: Tue, 28 Mar 2023 18:25:12 +0800 Subject: [PATCH 12/20] =?UTF-8?q?[KTVI]=20=E5=9C=BA=E6=99=AF=E5=8C=96=20AP?= =?UTF-8?q?I=20=E6=96=B9=E6=A1=88=20fixed?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...72\346\231\257\344\273\213\347\273\215.md" | 114 ---- .../02 Demo \344\275\223\351\252\214.md" | 37 -- ...41\350\264\271\350\257\264\346\230\216.md" | 13 - ...\213\351\241\271\347\233\256 (Android).md" | 92 --- ...\276\213\351\241\271\347\233\256 (iOS).md" | 89 --- ...\205\347\213\254\345\224\261 (Android).md" | 0 ...\273\205\347\213\254\345\224\261 (iOS).md" | 0 ...\257\345\256\236\347\216\260 (Android).md" | 363 ------------ ...\253\257\345\256\236\347\216\260 (iOS).md" | 374 ------------ ...05\345\256\271\344\270\255\345\277\203.md" | 494 ---------------- ...\215\347\273\204\344\273\266 (Android).md" | 155 ----- ...\257\215\347\273\204\344\273\266 (iOS).md" | 302 ---------- ...\206\345\212\237\350\203\275 (Android).md" | 79 --- ...\210\206\345\212\237\350\203\275 (iOS).md" | 79 --- ...5\231\250 API \345\217\202\350\200\203.md" | 552 ------------------ 15 files changed, 2743 deletions(-) delete mode 100644 "markdown/online-ktv/01 \345\234\272\346\231\257\346\246\202\350\277\260/01 \345\234\272\346\231\257\344\273\213\347\273\215.md" delete mode 100644 "markdown/online-ktv/01 \345\234\272\346\231\257\346\246\202\350\277\260/02 Demo \344\275\223\351\252\214.md" delete mode 100644 "markdown/online-ktv/01 \345\234\272\346\231\257\346\246\202\350\277\260/03 \350\256\241\350\264\271\350\257\264\346\230\216.md" delete mode 100644 "markdown/online-ktv/02 \345\256\236\347\216\260\346\265\201\347\250\213/00 \350\277\207\346\227\266-\350\267\221\351\200\232\347\244\272\344\276\213\351\241\271\347\233\256 (Android).md" delete mode 100644 "markdown/online-ktv/02 \345\256\236\347\216\260\346\265\201\347\250\213/00 \350\277\207\346\227\266-\350\267\221\351\200\232\347\244\272\344\276\213\351\241\271\347\233\256 (iOS).md" delete mode 100644 "markdown/online-ktv/02 \345\256\236\347\216\260\346\265\201\347\250\213/01 \350\277\207\346\227\266-\345\256\242\346\210\267\347\253\257\345\256\236\347\216\260-\344\273\205\347\213\254\345\224\261 (Android).md" delete mode 100644 "markdown/online-ktv/02 \345\256\236\347\216\260\346\265\201\347\250\213/01 \350\277\207\346\227\266-\345\256\242\346\210\267\347\253\257\345\256\236\347\216\260-\344\273\205\347\213\254\345\224\261 (iOS).md" delete mode 100644 "markdown/online-ktv/02 \345\256\236\347\216\260\346\265\201\347\250\213/02 \345\256\242\346\210\267\347\253\257\345\256\236\347\216\260 (Android).md" delete mode 100644 "markdown/online-ktv/02 \345\256\236\347\216\260\346\265\201\347\250\213/02 \345\256\242\346\210\267\347\253\257\345\256\236\347\216\260 (iOS).md" delete mode 100644 "markdown/online-ktv/03 \350\277\233\351\230\266\345\212\237\350\203\275/01 Agora \345\206\205\345\256\271\344\270\255\345\277\203.md" delete mode 100644 "markdown/online-ktv/03 \350\277\233\351\230\266\345\212\237\350\203\275/02 \346\255\214\350\257\215\347\273\204\344\273\266 (Android).md" delete mode 100644 "markdown/online-ktv/03 \350\277\233\351\230\266\345\212\237\350\203\275/02 \346\255\214\350\257\215\347\273\204\344\273\266 (iOS).md" delete mode 100644 "markdown/online-ktv/03 \350\277\233\351\230\266\345\212\237\350\203\275/03 \350\257\204\345\210\206\345\212\237\350\203\275 (Android).md" delete mode 100644 "markdown/online-ktv/03 \350\277\233\351\230\266\345\212\237\350\203\275/03 \350\257\204\345\210\206\345\212\237\350\203\275 (iOS).md" delete mode 100644 "markdown/online-ktv/04 \345\217\202\350\200\203\346\226\207\346\241\243/01 \351\237\263\344\271\220\346\222\255\346\224\276\345\231\250 API \345\217\202\350\200\203.md" diff --git "a/markdown/online-ktv/01 \345\234\272\346\231\257\346\246\202\350\277\260/01 \345\234\272\346\231\257\344\273\213\347\273\215.md" "b/markdown/online-ktv/01 \345\234\272\346\231\257\346\246\202\350\277\260/01 \345\234\272\346\231\257\344\273\213\347\273\215.md" deleted file mode 100644 index ad27c085697..00000000000 --- "a/markdown/online-ktv/01 \345\234\272\346\231\257\346\246\202\350\277\260/01 \345\234\272\346\231\257\344\273\213\347\273\215.md" +++ /dev/null @@ -1,114 +0,0 @@ -## 场景描述 - -在线 K 歌房是 Agora 提供的线上 K 歌场景化解决方案,结合 Agora RTC SDK 和 Agora 内容中心等产品,将其复杂的 API 进行模块整合,实现了功能组件化,降低了开发门槛。在此方案中,歌房里的主播可以点歌成为主唱,跟随歌曲伴奏演唱给歌房内的听众,在网络情况优良的条件下还可以实现双人合唱,在演唱过程中: - -- 主唱可以控制歌曲的暂停、播放和切换,并且可以自己调节伴奏和人声音量。 -- 主唱可以选择合唱模式邀请房间内的其他用户进行合唱。 -- 主唱可以开启打分模式,会根据主唱音准的匹配程度,系统给出得分。 -- 歌房内有歌词板块,唱歌时会根据歌曲播放进度显示对应的歌词。 -- Agora 内容中心提供热门歌曲曲库,主播可以搜索想唱的歌曲,点歌并查看已点列表。 -- 听众可以通过上麦点歌进行排麦演唱,并随时与房主和其他连麦主播进行实时音频互动。 - -**房间内的角色及描述** - -| 角色 | 描述 | -| :------- | :----------------------------------------------------------- | -| 房主 | 歌房创建者 | -| 连麦主播 | 进入歌房后,通过上麦成为连麦主播 | -| 主唱 | 连麦主播点歌后成为主唱,并进行排麦演唱 | -| 合唱者 | 主唱在合唱模式下点歌可邀请房间内的连麦主播成为合唱者,一起低延时合唱 | -| 听众 | 进入歌房的倾听者 | - -## 功能列表 - -在线 K 歌房场景化解决方案提供以下核心功能: - -### 实时音频互动 - -超低延时下,听众实时接收连麦主播的音频流,保证互动的流畅性。 - -### 互动连麦 - -听众可自主上麦成为连麦主播,连麦主播之间可以进行实时音频互动,房间内用户可以实时观看连麦主播互动。 - -### 曲库组件 - -Agora 内容中心提供热门歌曲的曲库,曲库支持搜索功能。 - -### 实时合唱 - -升级实时合唱技术,支持两人及两人以上人合唱,各端相互独立,互不影响,真实还原线下体验 - -### 点歌排麦 - -连麦主播从 Agora 内容中心点歌,歌曲进入已点列表;当点歌人数大于1时,根据点歌顺序进行排麦演唱,开始播放歌曲后点歌用户成为主唱。 - -### 歌曲控制 - -主唱可以控制歌曲的播放、暂停,切换伴奏/原唱,以及控制伴奏和人声的音量。 - -### 歌词组件 - -- 歌曲播放时,根据播放进度显示对应的歌词; -- 滑动歌词可以改变歌曲进度; -- 主唱演唱时显示评分的动画效果; -- 主唱可以改变歌词背景。 - -### 麦位控制 - -房主可以对房间内成员进行上麦、下麦操作,同时房间内所有用户都能看到每个麦位的实时状态。 - -### 低延时耳返 - -支持耳返功能,在低延时的情况下可以给主唱一个比较真实的反馈。 - -### 麦位控制 - -房主可以对房间内成员进行上麦、下麦操作,同时房间内所有用户都能看到每个麦位的实时状态。 - -### 低延时耳返 - -支持耳返功能,在低延时的情况下可以给主唱一个比较真实的反馈。 - -## 技术方案 - -Agora 使用 Agora RTC SDK 和 Agora 内容中心共同搭建在线 K 歌房场景。 - -**在线 K 歌独唱方案架构图** - -![img](https://web-cdn.agora.io/docs-files/1630049922164) - -**在线 K 歌合唱方案架构图** - -![](https://web-cdn.agora.io/docs-files/1652092393522) - - -**各 SDK 或服务实现的功能如下:** - -| 产品 | 实现功能 | -| :------------- | :----------------------------------------------------------- | -|Agora SDK | 加入频道,发布和接收音频流。 | -| Agora 内容中心 | 提供在线 K 歌房场景所需歌曲及歌曲配套信息,如歌词、专辑封面、海报等。 | -| Agora 歌词组件 | 根据歌曲播放进度实时显示歌词。支持自定义歌词界面布局,更换背景图片等操作。 | -| 第三方云服务 | 实现麦位控制、房间信息和用户信息管理等功能。
    Agora 在线 K 歌房 demo 中使用的云服务是 Agora 实现的。如果你需要使用相同功能,需要自行部署第三方云服务。 | - -## 方案优势 - -### 版权曲库整合 - -曲库覆盖热门流行歌曲,一站式接入海量正版数字音乐。歌曲类型丰富多元,满⾜多样化⽤户喜好。 - -### 九大场景功能 - -- 支持搜歌点歌、歌词同步、原唱/伴唱切换、歌唱打分、美声音效、实时合唱等九大场景功能,还原线下 KTV 真实场景。 -- 50 ms 超低延时耳返,告别走音跑调。 - -### 模块化组件设计 - -- 模块化组件设计,降低开发成本,快速搭建 K 歌房。 -- 灵活接入各类娱乐社交应用场景,简单调用让 app 有声有色。 - -### 媲美专业设备音质音效 - -- 声网 Agora SOLO™、NOVA™ 语音引擎,支持 48 kHz 全频带采样,还原声音高保真度,音频 MOS 分高达 4.7。 -- 支持美声和音效,摆脱专业设备和人员束缚,让你的声音更动听、更有趣。 \ No newline at end of file diff --git "a/markdown/online-ktv/01 \345\234\272\346\231\257\346\246\202\350\277\260/02 Demo \344\275\223\351\252\214.md" "b/markdown/online-ktv/01 \345\234\272\346\231\257\346\246\202\350\277\260/02 Demo \344\275\223\351\252\214.md" deleted file mode 100644 index 6e5031fc774..00000000000 --- "a/markdown/online-ktv/01 \345\234\272\346\231\257\346\246\202\350\277\260/02 Demo \344\275\223\351\252\214.md" +++ /dev/null @@ -1,37 +0,0 @@ -Agora 为在线 K 歌房场景化解决方案提供 Android 和 iOS 的 Demo,你可以[下载 Demo](https://docs.agora.io/cn/online-ktv/downloads?platform=All%20Platforms) 进行体验。 - -在线 K 歌房内的用户角色划分如下: - -| 角色 | 描述 | -| :------- | :--------------------------------------------- | -| 房主 | 歌房创建者。 | -| 连麦主播 | 进入歌房后,通过上麦成为连麦主播。 | -| 主唱 | 连麦主播点歌后进行排麦演唱,正在演唱者成为主唱。 | -| 合唱者 | 主唱发起合唱邀请后,连麦主播接受邀请,成为合唱者,可以收、发音频流。 | -| 听众 | 进入歌房的倾听者。 | - -## Demo 功能体验 - -由于在线 K 歌房场景涉及到多个用户,Agora 建议你准备两台及以上设备进行体验。你可以参考如下步骤试用 Agora 在线 K 歌房独唱及合唱功能: - -1. 在所有用于体验的设备上下载、安装《声动互娱》App,注册账号并登陆进入;在首页选择“在线 K 歌房”。 -2. 从一台手机上进入 App,点击创建房间图标创建歌房,成为房主。在创建房间时,你可以自定义房间名,也可使用随机生成的房间名。 -3. 从另一台手机上进入 App,在“在线 K 歌房”场景内找到第 2 步创建的歌房,进入歌房成为听众。 -4. 歌房内除房主外还有 7 个麦位,听众可以点击空麦位,上麦后成为连麦主播。连麦主播之间可以语音互动。 -5. 成功上麦后,连麦主播可以点击右下角的点歌或合唱按钮进入点歌页面,搜索指定歌曲或滑动列表选择歌曲进行点歌。若已有人点歌,将进入排麦队列。 -6. 若当前歌曲为独唱歌曲,排到该首歌曲时系统会自动开始播放;若当前歌曲为合唱歌曲,排到该首歌时系统会向房间内的所有连麦主播发出合唱邀请,第一个选择加入合唱的连麦主播成为合唱者,与主唱一起低延时合唱;若在一定时间内无人选择加入合唱,则由主唱独自演唱该歌曲。 -7. 当已点的歌曲开始播放,点播当前歌曲的人成为主唱。房间设有歌词板块,主唱可以跟随歌曲(或伴奏)和歌词开始演唱。与此同时: - - - 主唱可以点击暂停/播放或切歌按钮对歌曲进行控制,点击原唱按钮进行原唱和伴奏的切换,点击屏幕左下角话筒按钮来开启或关闭麦克风。 - - 在主唱在演唱过程中,系统将根据音准值的匹配程度对歌唱进行打分。 - - App 支持超低延时耳返,主唱可以佩戴有线耳机,打开耳返开关进行体验。 - - 主唱在演唱时可以对人声音量、伴奏音量、升降调、音效、电音效果等进行调节。 - - 房主可以对非演唱者的连麦主播进行下麦操作,同时房间内所有用户都能看到每个麦位的实时状态。 - - - - - - - -
    diff --git "a/markdown/online-ktv/01 \345\234\272\346\231\257\346\246\202\350\277\260/03 \350\256\241\350\264\271\350\257\264\346\230\216.md" "b/markdown/online-ktv/01 \345\234\272\346\231\257\346\246\202\350\277\260/03 \350\256\241\350\264\271\350\257\264\346\230\216.md" deleted file mode 100644 index 8af941e9345..00000000000 --- "a/markdown/online-ktv/01 \345\234\272\346\231\257\346\246\202\350\277\260/03 \350\256\241\350\264\271\350\257\264\346\230\216.md" +++ /dev/null @@ -1,13 +0,0 @@ -本文展示 Agora 在线 K 歌房解决方案的计费方式。 - -
    请联系 Agora 商务获取最新的计费方式。
    - -## 概述 - -当你使用 Agora 在线 K 歌房解决方案时,Agora 收取实时音频费用和内容订阅费用。计费方式如下: - -- 实时音频费用:Agora 实时音视频服务的计费方式详见[计费文档](https://docs.agora.io/cn/InteractiveBroadcast/billing_rtc)。 -- 内容订阅费用:Agora 按月计算内容中心套餐包费用和套餐超额费用(如有),并[发布账单、进行扣款](https://docs.agora.io/cn/InteractiveBroadcast/faq/billing_account)。如果在当月开通、取消或变更套餐包,则按照当月使用天数占当月天数比例进行收费。 - -如果[账户冻结](https://docs.agora.io/cn/agora-chat/faq/billing_account?platform=AllPlatforms),Agora 提供 6 个月的应用数据保存服务,你需要及时解冻账户或在数据被删除前进行导出。 - diff --git "a/markdown/online-ktv/02 \345\256\236\347\216\260\346\265\201\347\250\213/00 \350\277\207\346\227\266-\350\267\221\351\200\232\347\244\272\344\276\213\351\241\271\347\233\256 (Android).md" "b/markdown/online-ktv/02 \345\256\236\347\216\260\346\265\201\347\250\213/00 \350\277\207\346\227\266-\350\267\221\351\200\232\347\244\272\344\276\213\351\241\271\347\233\256 (Android).md" deleted file mode 100644 index 82ee489ce51..00000000000 --- "a/markdown/online-ktv/02 \345\256\236\347\216\260\346\265\201\347\250\213/00 \350\277\207\346\227\266-\350\267\221\351\200\232\347\244\272\344\276\213\351\241\271\347\233\256 (Android).md" +++ /dev/null @@ -1,92 +0,0 @@ -本文介绍如何快速跑通示例项目,体验 Agora 在线 K 歌房。 - -Agora 在 GitHub 上提供如下开源的在线 K 歌房示例项目,你可以选择其一进行体验: - -- [独唱示例项目](https://github.com/AgoraIO-Usecase/Online-KTV/tree/master/Agora-Online-KTV-Android) -- [合唱示例项目](http://github.com/AgoraIO-Usecase/Online-KTV/tree/chorus/Agora-Online-KTV-Android) - -## 前提条件 - -开始前,请确保你的开发环境满足以下条件: - -- Android Studio 4.0.0 或以上版本。 -- Android 4.1 或以上版本的设备。Agora 推荐使用真机,部分模拟机可能无法支持本项目的全部功能。 -- [Python](https://www.python.org/) 3.X。 -- 第三方云服务[命令行工具](https://leancloud.cn/docs/leanengine_faq.html#hash-864044521)。 -- 有效的 Agora 开发者账号,并生成客户 ID 和密钥。获取详情请参考[开始使用 Agora 平台](https://docs.agora.io/cn/AgoraPlatform/get_appid_token?platform=AllPlatforms#生成客户-id-和密钥)。 -- 有效的 Agora 项目,获取项目的 App ID,并生成一个临时 Token。获取详情请参考[开始使用 Agora 平台](https://docs.agora.io/cn/AgoraPlatform/get_appid_token?platform=AllPlatforms)。 -- 如果你的网络环境部署了防火墙,请参考[应用企业防火墙限制](https://docs.agora.io/cn/AgoraPlatform/firewall?platform=iOS)以正常使用 Agora 服务。 - -## 操作步骤 -### 1. 获取和配置第三方云服务信息 - -
    如果你想跳过以下步骤快速体验示例项目,可以联系 Agora 技术支持获取第三方云服务测试信息。
    - -Agora 提供的在线 K 歌房示例项目使用了第三方云服务,因此你还需要获取该云服务的有关信息。具体步骤如下: - -1. 前往[第三方云服务控制台](https://console.leancloud.cn/)注册账号,创建一个新的应用。 -2. 应用创建成功后,点击左侧菜单栏的 **设置 > 应用凭证**,你可以点击右侧的复制按钮获取该应用的 AppID、AppKey 和 REST API 服务器地址。 - - ![img](https://web-cdn.agora.io/docs-files/1623232859967) - -3. 点击 **设置 > 数据存储 > 服务设置**,勾选启用 **LiveQuery**。 - -4. 在终端中运行以下命令: - - ```shell - pip3 install leancloud - ``` - -### 2. 配置示例项目 - -按照以下步骤配置示例项目: - -1. 克隆 [Online-KTV](https://github.com/AgoraIO-Usecase/Online-KTV) 示例项目到本地,进入 `Agora-Online-KTV-Android` 文件夹。 - -2. 打开 `Agora-Online-KTV-Android/data/src/main/res/values` 路径下的 `strings_config.xml` 文件,修改以下信息: - - - 将 "`app_id`" 替换为在 Agora 控制台获取的 Agora App ID,将 "`token`" 替换为在 Agora 控制台获取的临时 Token。 - - 将 "`leancloud_app_id`"、"`leancloud_app_key`"、"`leancloud_server_url`" 分别替换为在[第 1 步](/cn/online-ktv/run_ktv_android?platform=Android&versionId=a6b97eb0-0708-11ec-8e65-856920855d60#1-获取和配置第三方云服务信息)获取的 AppID、AppKey、REST API 服务器地址。 - -4. 打开 Online-KTV 文件夹下的 `LeanCloudHelp.py` 文件,将[第 1 步](/cn/online-ktv/run_ktv_android?platform=Android&versionId=a6b97eb0-0708-11ec-8e65-856920855d60#1-获取和配置第三方云服务信息)获取的 AppID 和 AppKey 分别填入 `appid` 和 `appkey`。 -将在 Agora 控制台获取的客户 ID、密钥及 Agora App ID 分别填入 `customer_key`、`customer_secret` 及 `agora_app_id`。 - -### 3. 集成 Agora SDK -按照以下步骤将 Agora Android SDK 集成到示例项目中。 -1. 下载 [Agora 音频 SDK](https://download.agora.io/sdk/release/Agora_Native_SDK_for_Android_v3.3.4.201_VOICE_20210706_6474_92790.zip) 并解压。 -2. 将 SDK 包中的 `libs/agora-rtc-sdk.jar` 文件复制到 `Agora-Online-KTV-Android/KTV/libs` 目录下。 -3. 将 SDK 包中 `libs` 目录下的 `x86_64`, `x86`, `arm64-v8a` 和 `armeabi-v7a` 文件夹复制到 `Agora-Online-KTV-Android/KTV/src/main/jniLibs` 目录下。 - -### 4. 集成内容中心 - -Agora 内容中心提供在线 K 歌房场景所需歌曲及歌曲配套信息,请按照如下步骤开通内容中心,并将内容中心集成到示例项目中: - -1. 在[控制台](https://sso2.agora.io/cn/)开通内容中心权限并完成相关配置,详见[开通内容中心服务](/cn/online-ktv/ktv_song_rest?platform=All%20Platforms)。 - -2. 将内容中心的歌曲数据导入至第三方云数据库。 - 打开终端,运行以下命令: - - ```shell - python3 ./LeanCloudHelp.py - ``` -
    请将运行 LeanCloudHelp.py 文件的机器外网地址添加到白名单中。
    - -3. 部署第三方云函数,更多信息请参考[第三方云服务命令行参考文档](https://leancloud.cn/docs/leanengine_cli.html)。 - 打开第三方云服务的命令行工具,进入 `Online-KTV/Backend/testproject` 目录下,运行如下命令: -```shell -# 登录云服务 - lean login -# 切换到你的云服务项目 - lean switch -# 部署云函数 - lean deploy --prod 1 - ``` - -### 5. 运行示例项目 - -按照以下步骤运行示例项目,体验在线 K 歌房: - -1. 连接 Android 设备,在 Android Studio 中打开示例项目的 `Agora-Online-KTV-Android` 文件夹。 -2. 点击 **Sync Project with Gradle Files** 按钮同步项目,然后在左下角侧边栏中点击 **Build Variants**,选择使用的第三方云服务。 -3. 点击 **Run** 按钮运行项目。运行成功后,你会在设备上看到安装好的在线 K 歌房应用。 -4. 打开应用即可体验在线 K 歌房。 \ No newline at end of file diff --git "a/markdown/online-ktv/02 \345\256\236\347\216\260\346\265\201\347\250\213/00 \350\277\207\346\227\266-\350\267\221\351\200\232\347\244\272\344\276\213\351\241\271\347\233\256 (iOS).md" "b/markdown/online-ktv/02 \345\256\236\347\216\260\346\265\201\347\250\213/00 \350\277\207\346\227\266-\350\267\221\351\200\232\347\244\272\344\276\213\351\241\271\347\233\256 (iOS).md" deleted file mode 100644 index 9c61842d5a2..00000000000 --- "a/markdown/online-ktv/02 \345\256\236\347\216\260\346\265\201\347\250\213/00 \350\277\207\346\227\266-\350\267\221\351\200\232\347\244\272\344\276\213\351\241\271\347\233\256 (iOS).md" +++ /dev/null @@ -1,89 +0,0 @@ -Agora 在 GitHub 上提供如下开源的在线 K 歌房示例项目,你可以选择其一进行体验: - -- [独唱示例项目](https://github.com/AgoraIO-Usecase/Online-KTV) -- [合唱示例项目](https://github.com/AgoraIO-Usecase/Online-KTV/tree/chorus/Agora-Online-KTV-iOS) - - -## 前提条件 - -- Xcode 12.0 或以上版本。 -- 已安装 [Cocoapods](https://guides.cocoapods.org/using/getting-started.html#getting-started)。 -- iOS 11.0 或以上版本的设备。部分模拟机可能无法支持本项目的全部功能,所以推荐使用真机。 -- [Python](https://www.python.org/) 3.X。 -- 第三方云服务[命令行工具](https://leancloud.cn/docs/leanengine_cli.html)。 -- 有效的 Agora 开发者账号,并生成客户 ID 和密钥。获取详情请参考[开始使用 Agora 平台](https://docs.agora.io/cn/AgoraPlatform/get_appid_token?platform=AllPlatforms#生成客户-id-和密钥)。 -- 有效的 Agora 项目,获取项目的 App ID,并生成一个临时 Token。详情请参考[开始使用 Agora 平台](https://docs.agora.io/cn/AgoraPlatform/get_appid_token?platform=AllPlatforms)。 -- 如果你的网络环境部署了防火墙,请参考[应用企业防火墙限制](https://docs.agora.io/cn/AgoraPlatform/firewall?platform=iOS)以正常使用 Agora 服务。 - -## 操作步骤 - -### 1. 获取和配置第三方云存储服务信息 - -
    如果你想跳过以下步骤快速体验示例项目,可以联系 Agora 技术支持获取第三方云服务测试信息。
    - -Agora 提供的在线 K 歌房示例项目使用了第三方云服务,因此你还需要获取该云服务的有关信息。具体步骤如下: - -1. 前往[第三方云服务控制台](https://console.leancloud.cn/)注册账号,创建一个新的应用。 -2. 应用创建成功后,点击左侧菜单栏的 **设置 > 应用凭证**,你可以点击右侧的复制按钮获取该应用的 AppID、AppKey 和 REST API 服务器地址。 - - **![img](https://web-cdn.agora.io/docs-files/1623232859967)** - -3. 点击 **设置 > 数据存储 > 服务设置**,勾选启用 **LiveQuery**。 - -4. 在终端中运行以下命令: - - ```shell - pip3 install leancloud - ``` - -### 2. 配置示例项目 - -按照以下步骤配置示例项目: - -1. 克隆在线 K 歌房合唱或独唱示例项目到本地,进入 `Agora-Online-KTV-iOS` 文件夹。 -2. 打开 `Agora-Online-KTV-iOS/Core` 路径下的 `Config.swift` 文件,修改以下信息: - - 将从 Agora 控制台获取的 Agora App ID 和临时 Token 分别填入 `AppId` 和 `Token`。 - - 将从[第 1 步](#1-获取和配置第三方云存储服务信息)获取的 AppID、AppKey、REST API 服务器地址分别填入 `LeanCloudAppId`、`LeanCloudAppKey` 和 `LeanCloudServerUrl`。 -3. 打开 Online-KTV 文件夹下的 `LeanCloudHelp.py` 文件,将[第 1 步](#1-获取和配置第三方云存储服务信息)获取的 AppID 和 AppKey 填入 `appid` 和 `appkey`。 -将在 Agora 控制台获取的客户 ID、密钥及 Agora App ID 分别填入 `customer_key`、`customer_secret` 及 `agora_app_id`。 - -### 3. 集成 Agora SDK - -按照以下步骤将 Agora iOS SDK 集成到示例项目中。 -1. 下载 [Agora 音频 SDK](https://download.agora.io/sdk/release/Agora_Native_SDK_for_iOS_v3.3.4.201_VOICE_20210706_6263_92788.zip) 并解压。 -2. 将 SDK 包中 `libs` 目录下的 `.framework` 文件复制到 `Agora-Online-KTV-iOS/AgoraSDK/libs` 目录下。 - -### 4. 集成内容中心 - -Agora 内容中心提供在线 K 歌房场景所需歌曲及歌曲配套信息,请按照如下步骤开通内容中心,并将内容中心集成到示例项目中: - -1. 在[控制台](https://sso2.agora.io/cn/)开通内容中心权限并完成相关配置。详见[内容中心开通](/cn/online-ktv/ktv_song_rest?platform=All%20Platforms#开通服务)。 - -2. 将内容中心的歌曲数据导入至第三方云数据库。 - 打开终端,运行以下命令: - - ```shell - python3 ./LeanCloudHelp.py - ``` -
    请将运行 LeanCloudHelp.py 文件的机器外网地址添加到白名单中。
    - -3. 部署第三方云函数。详见[第三方云服务命令行参考文档](https://leancloud.cn/docs/leanengine_cli.html)。 - 打开第三方云服务的命令行工具,进入 `Online-KTV/Backend/testproject` 目录下,运行如下命令: - - ```shell -# 登录云服务 - lean login -# 切换到你的云服务项目 - lean switch -# 部署云函数 - lean deploy --prod 1 - ``` - -### 5. 运行示例项目 - -按照以下步骤运行示例项目,体验在线 K 歌房: - -1. 在终端里进入`Agora-Online-KTV-iOS` 文件夹,运行 `pod install` 命令链接所需的依赖库。链接成功后,终端会显示 `Pod installation complete!`,此时项目文件夹下会生成一个 `xcworkspace` 文件。 -2. 在 Xcode 中打开刚刚生成的 `xcworkspace` 文件,连接 iOS 设备,设置开发者签名。 -3. 点击 **Build** 按钮运行项目。运行成功后,你会在设备上看到安装好的在线 K 歌房应用。 -4. 打开应用即可体验在线 K 歌房。 \ No newline at end of file diff --git "a/markdown/online-ktv/02 \345\256\236\347\216\260\346\265\201\347\250\213/01 \350\277\207\346\227\266-\345\256\242\346\210\267\347\253\257\345\256\236\347\216\260-\344\273\205\347\213\254\345\224\261 (Android).md" "b/markdown/online-ktv/02 \345\256\236\347\216\260\346\265\201\347\250\213/01 \350\277\207\346\227\266-\345\256\242\346\210\267\347\253\257\345\256\236\347\216\260-\344\273\205\347\213\254\345\224\261 (Android).md" deleted file mode 100644 index e69de29bb2d..00000000000 diff --git "a/markdown/online-ktv/02 \345\256\236\347\216\260\346\265\201\347\250\213/01 \350\277\207\346\227\266-\345\256\242\346\210\267\347\253\257\345\256\236\347\216\260-\344\273\205\347\213\254\345\224\261 (iOS).md" "b/markdown/online-ktv/02 \345\256\236\347\216\260\346\265\201\347\250\213/01 \350\277\207\346\227\266-\345\256\242\346\210\267\347\253\257\345\256\236\347\216\260-\344\273\205\347\213\254\345\224\261 (iOS).md" deleted file mode 100644 index e69de29bb2d..00000000000 diff --git "a/markdown/online-ktv/02 \345\256\236\347\216\260\346\265\201\347\250\213/02 \345\256\242\346\210\267\347\253\257\345\256\236\347\216\260 (Android).md" "b/markdown/online-ktv/02 \345\256\236\347\216\260\346\265\201\347\250\213/02 \345\256\242\346\210\267\347\253\257\345\256\236\347\216\260 (Android).md" deleted file mode 100644 index 655525519b7..00000000000 --- "a/markdown/online-ktv/02 \345\256\236\347\216\260\346\265\201\347\250\213/02 \345\256\242\346\210\267\347\253\257\345\256\236\347\216\260 (Android).md" +++ /dev/null @@ -1,363 +0,0 @@ -本文介绍如何通过 Agora 音频 SDK v4.x 在你的 Android 项目里实现在线 K 歌房的主要功能。 - -## 技术原理 - -在线 K 歌房内用户角色说明如下: - -| 角色 | 描述 | -| :------- | :----------------------------------------------------------- | -| 房主 | 歌房创建者。可以收、发音频流。 | -| 连麦主播 | 进入歌房后,通过上麦成为连麦主播,可以收、发音频流。 | -| 主唱 | 连麦主播点歌后进行排麦演唱,正在演唱者成为主唱,可以收、发音频流。 | -| 合唱者 | 主唱发起合唱后,连麦主播接受邀请,成为合唱者,可以收、发音频流。 | -| 听众 | 进入歌房的倾听者,只能接收音频流。 | - -实现在线 K 歌房场景中不同角色流程如下: - -### 房主实现流程 - -1. 房主通过第三方云服务发起指令创建歌房,创建成功后调用 `joinChannel`方法加入歌房。 - - 加入歌房前,调用 `create [2/2]` 方法创建并初始化 `RtcEngine` 实例,并将实例的音频场景设置为合唱(`CHORUS`)。 - - 调用 `setClientRole` 方法将用户角色设置为主播( `BROADCASTER`)。 -2. 房主自动上麦成为连麦主播,可以点歌成为主唱,并邀请其他连麦主播进行合唱;也可以接受主唱的邀请,成为合唱者。 -3. 房主可以通过第三方云服务对连麦主播发起下麦指令,同时房间内所有用户会收到该连麦主播的下麦信息并更新房间内麦位信息。 -4. 房主离开歌房后,房间对象自动销毁,其他成员自动离开歌房。 - -### 主唱实现流程 - -参考下图实现在线 K 歌房场景的主唱端流程: - -![](https://web-cdn.agora.io/docs-files/1652095268172) - -1. 用户选择歌房,调用 `joinChannel` 方法加入歌房。 - 加入歌房前,调用 `create [2/2]` 方法创建并初始化 `RtcEngine` 实例,并将实例的音频场景设置为合唱(`CHORUS`)。 -2. 用户通过第三方云服务发起上麦指令,并调用 `setClientRole` 方法将用户角色设置为主播( `BROADCASTER`)。上麦后成为连麦主播,此时可以与房间内其他连麦主播进行语音互动。同时房间内所有用户收到主播端的上麦信息并更新房间内麦位信息。 -3. 用户可以点歌,从 [Agora 内容中心](https://docs.agora.io/cn/online-ktv/ktv_song_rest?platform=All%20Platforms)获取歌单,并下载歌曲文件和歌词文件,成为主唱。 -4. 主唱可以独唱,也可以发起合唱邀请指令,邀请房间内其他连麦主播一起合唱歌曲。 -5. 主唱调用 `IMediaPlayer` 类的方法播放歌曲,并伴随歌曲唱歌。如需切换原唱或伴奏模式,可以调用 `setAudioDualMonoMode` 方法。同时,主唱调用 `joinChannelEx` 方法,实现合唱。 -6. 主唱在唱歌的同时,调用 `sendStreamMessage` 方法获将当前歌曲播放进度同步到房间内所有用户。合唱者和听众可以结合歌词组件实现歌词同步显示,并自定义歌词界面,详见[歌词组件教程](https://docs.agora.io/cn/online-ktv/ktv_lrcview_android?platform=Android)。 -7. 主唱通过第三方云服务发起下麦指令,并调用 `leaveChannel` 方法离开歌房。 - -### 合唱者实现流程 - -![](https://web-cdn.agora.io/docs-files/1652095336541) - -1. 用户选择歌房,调用 `joinChannel` 方法加入歌房。 - 加入歌房前,调用 `create [2/2]` 方法创建并初始化 `RtcEngine` 实例,并将实例的音频场景设置为合唱(`CHORUS`)。 -2. 用户通过第三方云服务发起上麦指令,并调用 `setClientRole` 方法将用户角色设置为主播( `BROADCASTER`)。上麦后成为连麦主播,此时可以与房间内其他连麦主播进行语音互动。同时房间内所有用户收到主播端的上麦信息并更新房间内麦位信息。 -3. 主唱点歌并发出合唱邀请指令后,连麦主播可以接受主唱的邀请,并下载歌曲文件和歌词文件,成为合唱者。 -4. 合唱者调用 `IMediaPlayer` 类的方法播放本地歌曲,并伴随歌曲与主唱一起唱歌,并调用 `muteRemoteAudioStream` 方法静音主唱的歌曲。 -5. 合唱者通过 `onStreamMessage` 回调接收主唱发送的数据流,实现歌曲同步。同时结合歌词组件实现歌词同步显示,并自定义歌词界面,详见[歌词组件教程](https://docs.agora.io/cn/online-ktv/ktv_lrcview_android?platform=Android)。 -6. 合唱者通过第三方云服务发起下麦指令,并调用 `leaveChannel` 方法离开歌房。 - -### 听众实现流程 - -1. 用户选择歌房,调用 `joinChannel` 方法加入歌房。 - - 加入歌房前,调用 `create [2/2]` 方法创建并初始化 `RtcEngine` 实例,并将实例的音频场景设置为合唱(`CHORUS`)。 - - 调用 `setClientRole` 方法将用户角色设置为听众( `AUDIENCE`)。 - -1. 主播上麦成为主唱后,听众通过第三方云服务同步房间内麦位信息。 - -2. 主唱发起指令设置当前歌曲 ID,听众接收主唱指令并更新当前歌曲 ID。主唱唱歌时,听众接收主唱的音频流。 - - 听众如需上麦,需要再次调用 `setClientRole` 方法将用户角色设为 `BROADCASTER`,才可以在房间内发布音频流。 - -3. 听众通过第三方云服务发起获取当前歌词指令,并下载歌词。同时结合 `onStreamMessage` 回调和歌词组件,实时同步歌词进度。 - -4. 听众调用 `leaveChannel` 方法离开歌房。 - -## 前提条件 - -开始前,请确保你的开发环境满足以下条件: - -- Android Studio 4.0.0 或以上版本。 -- Android 4.1 或以上版本的设备。Agora 推荐使用真机,部分模拟机可能无法支持本项目的全部功能。 -- [Python](https://www.python.org/) 3.X。 -- 第三方云服务[命令行工具](https://leancloud.cn/docs/leanengine_faq.html#hash-864044521)。 -- 有效的 Agora [开发者账号](https://docs.agora.io/cn/AgoraPlatform/sign_in_and_sign_up)。 -- 有效的 Agora 项目,获取项目的 App ID 以及一个 RTM Token。详情请参考[开始使用 Agora 平台](https://docs.agora.io/cn/Agora%20Platform/get_appid_token?platform=All%20Platforms)。 -- 如果你的网络环境部署了防火墙,请参考[应用企业防火墙限制](https://docs.agora.io/cn/AgoraPlatform/firewall?platform=iOS)以正常使用 Agora 服务。 - -## 项目配置 - -根据下表提供的链接,下载对应平台的 SDK 并集成到你的项目中,并[开通内容中心服务](https://docs.agora.io/cn/online-ktv/ktv_song_rest?platform=All%20Platforms#开通服务)。 - -| 产品 | SDK 下载 | 集成文档 | -| :----------------------------------------------------------- | :----------------------------------------------------------- | :----------------------------------------------------------- | -| Agora 音频 SDK v4.x | [Agora 音频 SDK v4.x ](https://docs.agora.io/cn/voice-call-4.x/downloads?platform=Android) | [实现音频通话](https://docs.agora.io/cn/voice-call-4.x/start_call_audio_android_ng?platform=Android) | -| 第三方云服务 | [第三方云服务 SDK](https://leancloud.cn/docs/sdk_down.html) | [SDK 安装指南](https://leancloud.cn/docs/start.html)
    你可以联系 Agora 技术支持获取第三方云服务测试环境,或自行集成第三方云服务。
    | -| Agora 歌词组件 |[源码](https://github.com/AgoraIO-Community/LrcView-Android) | [歌词组件教程](https://docs.agora.io/cn/online-ktv/ktv_lrcview_android?platform=Android) | - -## 实现在线 K 歌 - -参考如下 API 时序图实现在线 K 歌房场景,同时你可以[提交工单](https://agora-ticket.agora.io/)获取示例项目。 - -**创建和加入歌房** - -![](https://web-cdn.agora.io/docs-files/1662706194762) - -**实时独唱** -![](https://web-cdn.agora.io/docs-files/1662706208064) - -**实时合唱** - -![](https://web-cdn.agora.io/docs-files/1662706221913) - -### 核心 API 参考 - -下表提供 Agora 音频 SDK v4.x 的核心 API 参考,与第三方云服务相关的功能需要自行实现。 - -#### 初始设置 - -加入歌房前,调用以下方法进行初始设置: - -| API | 实现功能 | -| :------------- | :----------------------------------------------------------- | -| [`create [2/2]`](https://docs.agora.io/cn/voice-call-4.x/API%20Reference/java_ng/API/class_irtcengine.html#api_create2) | 调用其他 API 之前,需要调用该方法创建并初始化 `RtcEngine`,并将 [`RtcEngineConfig`](https://docs.agora.io/cn/video-call-4.x/API%20Reference/java_ng/API/rtc_api_data_type.html#class_rtcengineconfig_ng) 中的 `mAudioScenario` 设置为 `AUDIO_SCENARIO_CHORUS (7)`。 | - -示例代码如下: - -```java -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`](https://docs.agora.io/cn/voice-call-4.x/API%20Reference/java_ng/API/class_irtcengine.html#api_joinchannel) | 加入歌房。用户加入歌房后才能接收或发布音频流。 | -| [`updateChannelMediaOptions`](https://docs.agora.io/cn/voice-call-4.x/API%20Reference/java_ng/API/class_irtcengine.html#api_updatechannelmediaoptions) | 加入歌房后更新媒体选项。 | -| [`leaveChannel`](https://docs.agora.io/cn/voice-call-4.x/API%20Reference/java_ng/API/class_irtcengine.html#api_leavechannel) | 离开歌房。房主离开歌房后,房间对象自动销毁,其他成员会自动离开歌房。 | -| [`joinChannelEx`](https://docs.agora.io/cn/voice-call-4.x/API%20Reference/java_ng/API/class_irtcengineex.html#api_irtcengineex_joinchannelex) | 合唱场景下,主唱需要调用 `joinChannel` 发布播放器音频流,调用 `joinChannelEx` 发布麦克风采集音频流。两次调用必须设置不同的 `uid`。 | -| [`updateChannelMediaOptionsEx`](https://docs.agora.io/cn/voice-call-4.x/API%20Reference/java_ng/API/class_irtcengineex.html#api_irtcengineex_updatechannelmediaoptionsex) | 合唱场景下,主唱加入另一个频道后更新媒体选项。 | -| [`leaveChannelEx`](https://docs.agora.io/cn/voice-call-4.x/API%20Reference/java_ng/API/class_irtcengineex.html#api_irtcengineex_leavechannelex) | 合唱场景下,主唱停止合唱后离开频道。 | - -独唱场景下,主唱发布播放器音频流和麦克风采集音频流,示例代码如下: - -```java -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` 发布另一路音频流,示例代码如下: - -```java -mediaPlayer = engine.createMediaPlayer(); -mediaPlayer.registerPlayerObserver(this); -mediaPlayer.open(url, 0); -ChannelMediaOptions mediaOptions = new ChannelMediaOptions(); -mediaOptions.autoSubscribeAudio = true; -mediaOptions.autoSubscribeVideo = true; -// 发布麦克风采集音频流 -mediaOptions.publishMicrophoneTrack = true; -mediaOptions.publishCameraTrack = false; -mediaOptions.publishMediaPlayerId = mediaPlayer.getMediaPlayerId(); -// 发布播放器音频流 -mediaOptions.publishMediaPlayerAudioTrack = true; -options.enableAudioRecordingOrPlayout = false; -mediaOptions.clientRoleType = Constants.CLIENT_ROLE_BROADCASTER; -rtcConnection.channelId = channel2; -rtcConnection.localUid = channel2Uid; -int ret = engine.joinChannelEx(null, rtcConnection, mediaOptions, iRtcEngineEventHandler2); -``` - -#### 上麦/下麦 - -调用以下方法进行上麦或下麦: - -| API | 实现功能 | -| :----------------------------------------------------------- | :----------------------------------------------------------- | -| [`setClientRole`](https://docs.agora.io/cn/voice-call-4.x/API%20Reference/java_ng/API/class_irtcengine.html#api_setclientrole) | 设置用户角色。
    加入歌房时,需要将主唱的用户角色设为 `BROADCASTER`、听众的用户角色设为 `AUDIENCE`。听众成功上麦后,需要先调用该方法将用户角色切换为 `BROADCASTER`,才能在房间里发布音频流。 | - -#### 音频相关 - -主唱调用以下方法设置音频流: - -| API | 实现功能 | -| :----------------------------------------------------------- | :----------------------------- | -| [`setAudioProfile [2/2]`](https://docs.agora.io/cn/voice-call-4.x/API%20Reference/java_ng/API/class_irtcengine.html#api_setaudioprofile2) | 设置音频编码属性。 | -| [`muteLocalAudioStream`](https://docs.agora.io/cn/voice-call-4.x/API%20Reference/java_ng/API/class_irtcengine.html#api_mutelocalaudiostream) | 主唱可以关闭或开启本地麦克风。 | -| [`adjustRecordingSignalVolume`](https://docs.agora.io/cn/voice-call-4.x/API%20Reference/java_ng/API/class_irtcengine.html#api_adjustrecordingsignalvolume) | 调节人声音量。 | - -#### 播放器相关 - -参考以下示例代码使用音乐播放器播放音乐资源。 - -1. 注册曲库并获取 `IAgoraMusicContentCenter`。 - - ```java -RtcEngine.loadExtensionProvider("agora_drm_loader_extension"); -mcc = IAgoraMusicContentCenter.create(rtcEngine()); -``` - -2. 初始化 `IAgoraMusicContentCenter`。 - - ```java -AgoraMusicContentCenterConfiguration contentCenterConfiguration = new AgoraMusicContentCenterConfiguration(); -// 已开启音乐内容中心项目的 App ID。 -contentCenterConfiguration.appId = "填入你的 App ID"; -// 使用音乐内容中心的用户 ID。该 ID 可以和你加入 RTC 频道时使用的 uid 一致,但不能为 0。 -contentCenterConfiguration.mccUid = 填入当前使用音乐内容中心用户的 uid; -// 用于鉴权的 RTM Token。 -contentCenterConfiguration.rtmToken = "xxxxxxxxxxxx"; -// 创建 IAgoraMusicContentCenterEventHandler,用于 SDK 向客户端发送音乐内容中心事件通知。 -contentCenterConfiguration.eventHandler = new IAgoraMusicContentCenterEventHandler(); -// 初始化 IAgoraMusicContentCenter。 -mcc.initialize(contentCenterConfiguration); -``` - -3. 创建音乐播放器。如果你需要播放音乐内容中心的音乐资源,需要使用此播放器进行播放。 - - ``` -musicPlayer = mcc.createMusicPlayer(); -``` - -4. 获取音乐榜单列表或搜索音乐资源。 - - 你可以通过以下 API 来获取音乐资源: - - `getMusicCharts`:获取曲库中全部的音乐榜单。 - - `getMusicCollectionByMusicChartId`:通过音乐榜单的 ID 获取指定榜单的音乐资源列表。 - - `searchMusic` :通过关键词搜索并获取音乐资源。 - - ```java -mcc.getMusicCharts(); -mcc.getMusicCollectionByMusicChartId(0,0,10); -mcc.searchMusic("hello",0,10); -``` - -5. 预加载音乐资源。 - - 调用 `preload` 方法来预加载需要播放的音乐资源。加载音乐资源之后,你可以调用 `isPreload` 方法来检测音乐资源是否已被预加载,该方法可同步调用且不包含耗时操作。 - -
    在播放音乐资源之前,请确保你已收到 `onPreLoadEvent` 回调报告音乐资源加载完成。
    - - ```java -mcc.preload(songCode, null); -mcc.isPreload(songCode); -``` - -6. 打开音乐资源。 - - 调用 `open` 方法打开音乐资源。 - - ```java -musicPlayer.open(songCode, 0); -``` - -
    调用 open 后,请确保收到 onPlayerStateChanged 回调报告状态为 PLAYER_STATE_OPEN_COMPLETED(2) 再调用 play 来进行播放。
    - -| 音乐播放器核心 API | 实现功能 | -| :--------------------------------- | :--------------------------------------------- | -| `initialize` | 初始化 `IAgoraMusicContentCenter` 。 | -| `createMusicPlayer` | 创建音乐播放器。 | -| `getMusicCharts` | 获取全部的音乐榜单。 | -| `getMusicCollectionByMusicChartId` | 通过音乐榜单的 ID 获取指定榜单的音乐资源列表。 | -| `searchMusic` | 搜索音乐资源。 | -| `preload` | 预加载音乐资源。 | -| `isPreloaded` | 检测音乐资源是否已被预加载。 | -| `getLyric` | 获取音乐资源的歌词下载地址。 | -| `open` | 打开音乐资源。 | -| `onPreLoadEvent` | 报告预加载音乐资源的事件。 | -| `onMusicCollectionResult` | 音乐资源列表回调。 | -| `onMusicChartsResult` | 音乐榜单回调。 | -| `onLyricResult` | 歌词下载地址回调。 | - - -你可以参考[音乐播放器 API 文档](https://docs.agora.io/cn/online-ktv/ktv_api_android?platform=Android)了解更多详细信息,音乐播放器的参数调节、推流设置、事件回调等请参考 [IMediaPlayer 类](https://docs.agora.io/cn/video-call-4.x/API%20Reference/java_ng/API/class_imediaplayer.html)。 - -#### 歌曲/歌词同步 - -| API | 实现功能 | -| :----------------------------------------------------------- | :----------------------------------------------------------- | -| [`createDataStream [2/2]`](https://docs.agora.io/cn/voice-call-4.x/API%20Reference/java_ng/API/class_irtcengine.html#api_createdatastream) | 主唱创建数据流。用于同步歌曲播放进度。 | -| [`sendStreamMessage`](https://docs.agora.io/cn/voice-call-4.x/API%20Reference/java_ng/API/class_irtcengine.html#api_sendstreammessage) | 主唱发送数据流到歌房内所有用户。 | -| [`onStreamMessage`](https://docs.agora.io/cn/voice-call-4.x/API%20Reference/java_ng/API/class_irtcengineeventhandler.html#callback_onstreammessage) | 歌房内所有用户接收主唱发送的数据流,结合歌词控件实现歌词进度的同步更新。 | - -示例代码如下: - -```java -DataStreamConfig cfg = new DataStreamConfig(); -cfg.syncWithAudio = true; -cfg.ordered = true; -mStreamId = getRtcEngine().createDataStream(cfg); -private void sendSyncLrc(String lrcId, long duration, long time) { - Map 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(); - } - } -} -``` - -## 参考信息 - -### 附加功能 - -#### 人声音效 - -调用 [`setAudioEffectPreset`](https://docs.agora.io/cn/live-streaming-premium-4.x/API%20Reference/java_ng/API/class_irtcengine.html?platform=Android#api_setaudioeffectpreset) 方法,在不改变原声的性别特征的前提下,设置人声音效。设置音效后,频道内所有用户都能听到该效果。详见[设置人声效果](https://docs.agora.io/cn/voice-call-4.x/voice_changer_android_ng?platform=Android)。 - -#### 耳返 - -调用 [`enableInEarMonitoring [2/2]`](https://docs.agora.io/cn/live-streaming-premium-4.x/API%20Reference/java_ng/API/class_irtcengine.html?platform=Android#api_enableinearmonitoring2) 方法开启主唱的耳返功能。 - -#### 歌唱评分 - -调用 [`enableAudioVolumeIndication`](https://docs.agora.io/cn/live-streaming-premium-4.x/API%20Reference/java_ng/API/class_irtcengine.html#api_enableaudiovolumeindication) 方法开启歌唱评分功能,并通过 [`onAudioVolumeIndication`](https://docs.agora.io/cn/live-streaming-premium-4.x/API%20Reference/java_ng/API/class_irtcengineeventhandler.html#callback_onaudiovolumeindication) 回调,结合 Agora 歌词组件实现歌唱评分效果,详见[评分功能教程](https://docs.agora.io/cn/online-ktv/ktv_score_android?platform=All%20Platforms)。 - -### 常见问题 - -[如何使用连麦鉴权功能?](https://docs.agora.io/cn/InteractiveBroadcast/faq/token_cohost) - -### 相关文档 - -- [Agora 内容中心](https://docs.agora.io/cn/online-ktv/ktv_song_rest?platform=All%20Platforms) -- [歌词组件](https://docs.agora.io/cn/online-ktv/ktv_lrcview_android?platform=Android) \ No newline at end of file diff --git "a/markdown/online-ktv/02 \345\256\236\347\216\260\346\265\201\347\250\213/02 \345\256\242\346\210\267\347\253\257\345\256\236\347\216\260 (iOS).md" "b/markdown/online-ktv/02 \345\256\236\347\216\260\346\265\201\347\250\213/02 \345\256\242\346\210\267\347\253\257\345\256\236\347\216\260 (iOS).md" deleted file mode 100644 index 66d001d416f..00000000000 --- "a/markdown/online-ktv/02 \345\256\236\347\216\260\346\265\201\347\250\213/02 \345\256\242\346\210\267\347\253\257\345\256\236\347\216\260 (iOS).md" +++ /dev/null @@ -1,374 +0,0 @@ -本文介绍如何通过 Agora 音频 SDK 在你的 iOS 项目里实现在线 K 歌房的主要功能。 - -## 技术原理 - -在线 K 歌房内用户角色说明如下: - -| 角色 | 描述 | -| :------- | :----------------------------------------------------------- | -| 房主 | 歌房创建者。可以收、发音频流。 | -| 连麦主播 | 进入歌房后,通过上麦成为连麦主播,可以收、发音频流。 | -| 主唱 | 连麦主播点歌后进行排麦演唱,正在演唱者成为主唱,可以收、发音频流。 | -| 合唱者 | 主唱发起合唱后,连麦主播接受邀请,成为合唱者,可以收、发音频流。 | -| 听众 | 进入歌房的倾听者,只能接收音频流。 | - -实现在线 K 歌房场景中不同角色流程如下: - -### 房主实现流程 - -1. 房主通过第三方云服务发起指令创建歌房,创建成功后调用 `joinChannelByToken`方法加入歌房。 - - 加入歌房前,调用 `sharedEngineWithConfig` 方法创建并初始化 `AgoraRtcEngineKit` 实例,并将实例的音频场景设置为合唱(`AgoraAudioScenarioChorus`)。 - - 调用 `setClientRole` 方法将用户角色设置为主播( `AgoraClientRoleBroadcaster`)。 -2. 房主自动上麦成为连麦主播,可以点歌成为主唱,并邀请其他连麦主播进行合唱;也可以接受主唱的邀请,成为合唱者。 -3. 房主可以通过第三方云服务对连麦主播发起下麦指令,同时房间内所有用户会收到该连麦主播的下麦信息并更新房间内麦位信息。 -4. 房主离开歌房后,房间对象自动销毁,其他成员自动离开歌房。 - -### 主唱实现流程 - -参考下图实现在线 K 歌房场景的主唱端流程: - -![](https://web-cdn.agora.io/docs-files/1652095268172) - -1. 用户选择歌房,调用 `joinChannelByToken` 方法加入歌房。 - 加入歌房前,调用 `sharedEngineWithConfig` 方法创建并初始化 `AgoraRtcEngineKit` 实例,并将实例的音频场景设置为合唱(`AgoraAudioScenarioChorus`)。 -2. 用户通过第三方云服务发起上麦指令,并调用 `setClientRole` 方法将用户角色设置为主播( `AgoraClientRoleBroadcaster`)。上麦后成为连麦主播,此时可以与房间内其他连麦主播进行语音互动。同时房间内所有用户收到主播端的上麦信息并更新房间内麦位信息。 -3. 用户可以点歌,从 [Agora 内容中心](https://docs.agora.io/cn/online-ktv/ktv_song_rest?platform=All%20Platforms)获取歌单,并下载歌曲文件和歌词文件,成为主唱。 -4. 主唱可以独唱,也可以发起合唱邀请指令,邀请房间内其他连麦主播一起合唱歌曲。 -5. 主唱调用 `AgoraRtcMediaPlayerProtocol` 类的方法播放歌曲,并伴随歌曲唱歌。如需切换原唱或伴奏模式,可以调用 `setAudioDualMonoMode` 方法。同时,主唱调用 `joinChannelExByToken` 方法,实现合唱。 -6. 主唱在唱歌的同时,调用 `sendStreamMessage` 方法获将当前歌曲播放进度同步到房间内所有用户。合唱者和听众可以结合歌词组件实现歌词同步显示,并自定义歌词界面,详见[歌词组件教程](https://docs.agora.io/cn/online-ktv/ktv_lrcview_ios?platform=iOS)。 -7. 主唱通过第三方云服务发起下麦指令,并调用 `leaveChannel` 方法离开歌房。 - -### 合唱者实现流程 - -![](https://web-cdn.agora.io/docs-files/1652095336541) - -1. 用户选择歌房,调用 `joinChannelByToken` 方法加入歌房。 - 加入歌房前,调用 `sharedEngineWithConfig` 方法创建并初始化 `AgoraRtcEngineKit` 实例,并将实例的音频场景设置为合唱(`AgoraAudioScenarioChorus`)。 -2. 用户通过第三方云服务发起上麦指令,并调用 `setClientRole` 方法将用户角色设置为主播( `AgoraClientRoleBroadcaster`)。上麦后成为连麦主播,此时可以与房间内其他连麦主播进行语音互动。同时房间内所有用户收到主播端的上麦信息并更新房间内麦位信息。 -3. 主唱点歌并发出合唱邀请指令后,连麦主播可以接受主唱的邀请,并下载歌曲文件和歌词文件,成为合唱者。 -4. 合唱者调用 `AgoraRtcMediaPlayerProtocol` 类的方法播放本地歌曲,并伴随歌曲与主唱一起唱歌,并调用 `muteRemoteAudioStream` 方法静音主唱的歌曲。 -5. 合唱者通过 `receiveStreamMessageFromUid` 回调接收主唱发送的数据流,实现歌曲同步。同时结合歌词组件实现歌词同步显示,并自定义歌词界面,详见[歌词组件教程](https://docs.agora.io/cn/online-ktv/ktv_lrcview_ios?platform=iOS)。 -6. 合唱者通过第三方云服务发起下麦指令,并调用 `leaveChannel` 方法离开歌房。 - -### 听众实现流程 - -1. 用户选择歌房,调用 `joinChannelByToken` 方法加入歌房。 - - 加入歌房前,调用 `sharedEngineWithConfig` 方法创建并初始化 `AgoraRtcEngineKit` 实例,并将实例的音频场景设置为合唱(`AgoraAudioScenarioChorus`)。 - - 调用 `setClientRole` 方法将用户角色设置为听众( `AgoraClientRoleAudience`)。 - -1. 主播上麦成为主唱后,听众通过第三方云服务同步房间内麦位信息。 - -2. 主唱发起指令设置当前歌曲 ID,听众接收主唱指令并更新当前歌曲 ID。主唱唱歌时,听众接收主唱的音频流。 - - 听众如需上麦,需要再次调用 `setClientRole` 方法将用户角色设为 `AgoraClientRoleBroadcaster`,才可以在房间内发布音频流。 - -3. 听众通过第三方云服务发起获取当前歌词指令,并下载歌词。同时结合 `receiveStreamMessageFromUid` 回调和歌词组件,实时同步歌词进度。 - -4. 听众调用 `leaveChannel` 方法离开歌房。 - -## 前提条件 - -开始前,请确保你的开发环境满足以下条件: - -- Xcode 12.0 或以上版本。 -- 已安装 [Cocoapods](https://guides.cocoapods.org/using/getting-started.html#getting-started)。 -- iOS 11.0 或以上版本的设备。部分模拟机可能无法支持本项目的全部功能,所以推荐使用真机。 -- [Python](https://www.python.org/) 3.X。 -- 第三方云服务[命令行工具](https://leancloud.cn/docs/leanengine_cli.html)。 -- 有效的 Agora [开发者账号](https://docs.agora.io/cn/AgoraPlatform/sign_in_and_sign_up)。 -- 有效的 Agora 项目,获取项目的 App ID 以及一个 RTM Token。详情请参考[开始使用 Agora 平台](https://docs.agora.io/cn/Agora%20Platform/get_appid_token?platform=All%20Platforms)。 -- 如果你的网络环境部署了防火墙,请参考[应用企业防火墙限制](https://docs.agora.io/cn/AgoraPlatform/firewall?platform=iOS)以正常使用 Agora 服务。 - -## 项目配置 - -根据下表提供的链接,下载对应平台的 SDK 并集成到你的项目中,并[开通内容中心服务](https://docs.agora.io/cn/online-ktv/ktv_song_rest?platform=All%20Platforms#开通服务)。 - -| 产品 | SDK 下载 | 集成文档 | -| :----------------------------------------------------------- | :----------------------------------------------------------- | :----------------------------------------------------------- | -| Agora 音频 SDK v4.x | [Agora 音频 SDK v4.x](https://docs.agora.io/cn/voice-call-4.x/downloads?platform=iOS) | [实现音频通话](https://docs.agora.io/cn/voice-call-4.x/start_call_audio_ios_ng?platform=iOS) | -| 第三方云服务 | [第三方云服务 SDK](https://leancloud.cn/docs/sdk_down.html) | [SDK 安装指南](https://leancloud.cn/docs/start.html)
    你可以联系 Agora 技术支持获取第三方云服务测试环境,或自行集成第三方云服务。
    | -| Agora 歌词组件 |[源码](https://github.com/AgoraIO-Community/LrcView-iOS) | [歌词组件教程](https://docs.agora.io/cn/online-ktv/ktv_lrcview_ios?platform=iOS) | - -## 实现在线 K 歌 - -参考如下 API 时序图实现在线 K 歌房场景,同时你可以[提交工单](https://agora-ticket.agora.io/)获取示例项目。 - -**创建和加入歌房** - -![](https://web-cdn.agora.io/docs-files/1662704390165) - - -**实时独唱** - -![](https://web-cdn.agora.io/docs-files/1662706596133) - -**实时合唱** - -![](https://web-cdn.agora.io/docs-files/1662704412019) - -### 核心 API 参考 - -下表提供 Agora 音频 SDK 的核心 API 参考,与第三方云服务相关的功能需要自行实现。 - -#### 初始设置 - -加入歌房前,调用以下方法进行初始设置: - -| API | 实现功能 | -| :------------- | :----------------------------------------------------------- | -| [`sharedEngineWithConfig`](https://docs.agora.io/cn/voice-call-4.x/API%20Reference/ios_ng/API/class_irtcengine.html#api_create2) | 调用其他 API 之前,需要调用该方法创建并初始化 `AgoraRtcEngineKit`,并将 [`AgoraRtcEngineConfig`](https://docs.agora.io/cn/voice-call-4.x/API%20Reference/ios_ng/API/rtc_api_data_type.html#class_rtcengineconfig_ng) 中的 `audioScenario` 设置为 `AgoraAudioScenarioChorus(7)`。 | - -示例代码如下: - -```swift -let config = AgoraRtcEngineConfig(); -config.appId = BuildConfig.AppId -config.audioScenario = .chorus -config.channelProfile = .liveBroadcasting -rtcEngine = AgoraRtcEngineKit.sharedEngine(with: config, delegate: self) -``` - -#### 加入/离开歌房 - -调用以下方法加入或离开歌房: - -| API | 实现功能 | -| :----------------------------------------------------------- | :----------------------------------------------------------- | -| [`joinChannelByToken`](https://docs.agora.io/cn/voice-call-4.x/API%20Reference/ios_ng/API/class_irtcengine.html#api_joinchannel) | 加入歌房。用户加入歌房后才能接收或发布音频流。 | -| [`updateChannelWithMediaOptions`](https://docs.agora.io/cn/voice-call-4.x/API%20Reference/ios_ng/API/class_irtcengine.html#api_updatechannelmediaoptions) | 加入歌房后更新媒体选项。 | -| [`leaveChannel`](https://docs.agora.io/cn/voice-call-4.x/API%20Reference/ios_ng/API/class_irtcengine.html#api_leavechannel) | 离开歌房。房主离开歌房后,房间对象自动销毁,其他成员会自动离开歌房。 | -| [`joinChannelExByToken`](https://docs.agora.io/cn/voice-call-4.x/API%20Reference/ios_ng/API/class_irtcengineex.html#api_irtcengineex_joinchannelex) | 合唱场景下,主唱需要调用 `joinChannel` 发布播放器音频流,调用 `joinChannelEx` 发布麦克风采集音频流。两次调用必须设置不同的 `uid`。 | -| [`updateChannelExWithMediaOptions`](https://docs.agora.io/cn/voice-call-4.x/API%20Reference/ios_ng/API/class_irtcengineex.html#api_irtcengineex_updatechannelmediaoptionsex) | 合唱场景下,主唱加入另一个频道后更新媒体选项。 | -| [`leaveChannelEx`](https://docs.agora.io/cn/voice-call-4.x/API%20Reference/ios_ng/API/class_irtcengineex.html#api_irtcengineex_leavechannelex) | 合唱场景下,主唱停止合唱后离开频道。 | - -独唱场景下,主唱发布播放器音频流和麦克风采集音频流,示例代码如下: - -```swift -rtcMediaPlayer = rtcEngine.createMediaPlayer(with: self) -rtcMediaPlayer.open(url, 0); -let option = AgoraRtcChannelMediaOptions() -option.autoSubscribeAudio = AgoraRtcBoolOptional.of(true) -option.autoSubscribeVideo = AgoraRtcBoolOptional.of(true) -option.publishMicrophoneTrack = .of(true) -``` - -合唱场景下,主唱调用 `joinChannelExByToken` 发布另一路音频流,示例代码如下: - -```swift -rtcMediaPlayer = rtcEngine.createMediaPlayer(with: self) -rtcMediaPlayer.open(url, 0); -let option = AgoraRtcChannelMediaOptions() -option.autoSubscribeAudio = AgoraRtcBoolOptional.of(true) -option.autoSubscribeVideo = AgoraRtcBoolOptional.of(true) -option.publishMicrophoneTrack = .of(true) -option.publishMediaPlayerId = rtcMediaPlayer.getMediaPlayerId(); -// 发布播放器音频流 -option.publishMediaPlayerAudioTrack = .of(true) -option.enableAudioRecordingOrPlayout = .of(false) -let connection = AgoraRtcConnection() -connection.channelId = "" -connection.localUid = 0 - -int ret = rtcEngine?.joinChannelEx(byToken: nil, connection: connection, delegate: self, mediaOptions: mediaOption, joinSuccess: nil) -``` - -#### 上麦/下麦 - -调用以下方法进行上麦或下麦: - -| API | 实现功能 | -| :----------------------------------------------------------- | :----------------------------------------------------------- | -| [`setClientRole`](https://docs.agora.io/cn/voice-call-4.x/API%20Reference/ios_ng/API/class_irtcengine.html#api_setclientrole) | 设置用户角色。
    加入歌房时,需要将主唱的用户角色设为 `AgoraClientRoleBroadcaster`、听众的用户角色设为 `AgoraClientRoleAudience`。听众成功上麦后,需要先调用该方法将用户角色切换为 `AgoraClientRoleBroadcaster`,才能在房间里发布音频流。 | - -#### 音频相关 - -主唱调用以下方法设置音频流: - -| API | 实现功能 | -| :----------------------------------------------------------- | :----------------------------- | -| [`setAudioProfile [2/2]`](https://docs.agora.io/cn/voice-call-4.x/API%20Reference/ios_ng/API/class_irtcengine.html#api_setaudioprofile2) | 设置音频编码属性。 | -| [`muteLocalAudioStream`](https://docs.agora.io/cn/voice-call-4.x/API%20Reference/ios_ng/API/class_irtcengine.html#api_mutelocalaudiostream) | 主唱可以关闭或开启本地麦克风。 | -| [`adjustRecordingSignalVolume`](https://docs.agora.io/cn/voice-call-4.x/API%20Reference/ios_ng/API/class_irtcengine.html#api_adjustrecordingsignalvolume) | 调节人声音量。 | - -#### 播放器相关 - -参考以下示例代码使用音乐播放器播放音乐资源。 - -1. 注册曲库并初始化 `AgoraMusicContentCenter`。 - -```swift -let config = AgoraMusicContentCenterConfig() -amcc = AgoraMusicContentCenter.sharedContentCenter(with: config) -// 填入已开启音乐内容中心项目的 App ID -config.appId = xxx - // 用户 ID,该 ID 可以和你加入 RTC 频道时使用的 uid 一致,但不能为 0。 - config.mccUid = xxx - // 填入用于鉴权的 RTM Token。 - config.rtmToken = xxx - // AgoraRtcEngineKit 实例。 - config.rtcEngine = xxx -``` - -2. 创建音乐播放器。如果你需要播放音乐内容中心的音乐资源,需要使用此播放器进行播放。 - -```swift -musicPlayer = amcc?.createMusicPlayer(with:sdkEvent as! AgoraRtcMediaPlayerDelegate) -``` - -3. 获取音乐榜单列表或搜索音乐资源。 - -你可以通过以下 API 来获取音乐资源: -- `getMusicCharts`:获取曲库中全部的音乐榜单。 -- `getMusicCollectionWith`:通过音乐榜单的 ID 获取指定榜单的音乐资源列表。 -- `searchMusicWith`:通过关键词搜索并获取音乐资源。 - -```swift -amcc?.getMusicCharts() -amcc?.getMusicCollection(with:musicChartType, page:page, pageSize:pageSize, jsonOption:jsonOption) -amcc?.searchMusic(with:keyword, page:page, pageSize:pageSize, jsonOption:jsonOption) -``` - -4. 预加载音乐资源。 - -调用 `preloadWith` 方法来预加载需要播放的音乐资源。加载音乐资源之后,你可以调用 `isPreloadedWith` 方法来检测音乐资源是否已被预加载,该方法可同步调用且不包含耗时操作。 - -
    在播放音乐资源之前,请确保你已收到 onPreLoadEvent 回调报告音乐资源加载完成。
    - -```swift -amcc?.preload(with: songCode, jsonOption: null) -amcc?.isPreloaded(with: songCode) -``` - -5. 打开音乐资源。 - -调用 `openMediaWithSongCode` 方法打开音乐资源。 - -```swift -mPlayer.openMedia(withSongCode: songCode, startPos: startPos) -``` - -
    调用 openMediaWithSongCode 后,请确保收到 didChangedToState 回调报告状态为 AgoraMediaPlayerStateOpenCompleted 再调用 play 来进行播放。
    - -| 音乐播放器核心 API | 实现功能 | - | :--------------------------------- | :--------------------------------------------- | - | `initialize` | 初始化 `IAgoraMusicContentCenter` 。 | - | `createMusicPlayer` | 创建音乐播放器。 | - | `getMusicCharts` | 获取全部的音乐榜单。 | - | `getMusicCollectionByMusicChartId` | 通过音乐榜单的 ID 获取指定榜单的音乐资源列表。 | - | `searchMusic` | 搜索音乐资源。 | - | `preload` | 预加载音乐资源。 | - | `isPreloaded` | 检测音乐资源是否已被预加载。 | - | `getLyric` | 获取音乐资源的歌词下载地址。 | - | `open` | 打开音乐资源。 | - | `onPreLoadEvent` | 报告预加载音乐资源的事件。 | - | `onMusicCollectionResult` | 音乐资源列表回调。 | - | `onMusicChartsResult` | 音乐榜单回调。 | - | `onLyricResult` | 歌词下载地址回调。 | - -你可以参考[音乐播放器 API 文档](https://docs.agora.io/cn/online-ktv/ktv_api_ios?platform=iOS)了解更多详细信息,音乐播放器的参数调节、推流设置、事件回调等请参考 [IMediaPlayer 类](https://docs.agora.io/cn/video-call-4.x/API%20Reference/ios_ng/API/class_imediaplayer.html)。 - -#### 歌曲/歌词同步 - -| API | 实现功能 | -| :----------------------------------------------------------- | :----------------------------------------------------------- | -| [`createDataStream [2/2]`](https://docs.agora.io/cn/voice-call-4.x/API%20Reference/ios_ng/API/class_irtcengine.html#api_createdatastream2) | 主唱创建数据流。用于同步歌曲播放进度。 | -| [`sendStreamMessage`](https://docs.agora.io/cn/voice-call-4.x/API%20Reference/ios_ng/API/class_irtcengine.html#api_sendstreammessage) | 主唱发送数据流到歌房内所有用户。 | -| [`receiveStreamMessageFromUid`](https://docs.agora.io/cn/voice-call-4.x/API%20Reference/ios_ng/API/class_irtcengineeventhandler.html#callback_onstreammessage) | 歌房内所有用户接收主唱发送的数据流,结合歌词组件实现歌词进度的同步更新。 | - - - -示例代码如下: - -```swift -let config = AgoraDataStreamConfig() - -config.ordered = false - -config.syncWithAudio = false - -rtc.createDataStream(&dataStreamId, config: config) - -if dataStreamId == -1 { - -​ Logger.log(self, message: "error dataStreamId == -1", level: .error) - -} - -let jsonEncoder = JSONEncoder() - -do { - -​ let jsonData = try jsonEncoder.encode(msg) - -​ let str = NSString(data: jsonData, encoding: String.Encoding.utf8.rawValue) - -​ let code = rtcEngine.sendStreamMessage(streamId, data: jsonData) - -​ if code != 0 { - -​ Logger.log(self, message: "sendRtcStreamMessage error((code)", level: .error) - -​ } - -} catch { - -​ Logger.log(self, message: error.localizedDescription, level: .error) - -} - -​ func rtcEngine(_: AgoraRtcEngineKit, receiveStreamMessageFromUid uid: UInt, streamId: Int, data: Data) { - -​ do { - -​ guard let content: NSDictionary = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as? NSDictionary, - -​ let cmd: String = content["cmd"] as? String else { return } - -​ var state: RtcMusicState? - -​ switch cmd { - -​ case "setLrcTime": - -​ let duration = content["duration"] as? Int ?? 0 - -​ let position = content["time"] as? Int ?? -1 - -​ state = RtcMusicState(uid: uid, streamId: getOrderedDataStreamId(), position: position, duration: duration, state: .playing, type: .position) - -​ } - -​ } catch { - -​ Logger.log(self, message: error.localizedDescription, level: .error) - -​ } - - } -``` - -## 参考信息 - -### 附加功能 - -#### 人声音效 - -调用 [`setAudioEffectPreset`](https://docs.agora.io/cn/voice-call-4.x/API%20Reference/ios_ng/API/class_irtcengine.html#api_setaudioeffectpreset) 方法,在不改变原声的性别特征的前提下,设置人声音效。设置音效后,频道内所有用户都能听到该效果。详见[设置人声效果](https://docs.agora.io/cn/voice-call-4.x/voice_changer_apple_ng?platform=iOS)。 - -#### 耳返 - -调用 [`enableInEarMonitoring [2/2]`](https://docs.agora.io/cn/voice-call-4.x/API%20Reference/ios_ng/API/class_irtcengine.html#api_enableinearmonitoring2) 方法开启主唱的耳返功能。 - -#### 歌唱评分 - -调用 [`enableAudioVolumeIndication`](https://docs.agora.io/cn/voice-call-4.x/API%20Reference/ios_ng/API/class_irtcengine.html#api_enableaudiovolumeindication) 方法开启歌唱评分功能,并通过 [`reportAudioVolumeIndicationOfSpeakers`](https://docs.agora.io/cn/voice-call-4.x/API%20Reference/ios_ng/API/class_irtcengineeventhandler.html#callback_onaudiovolumeindication) 回调,结合 Agora 歌词组件实现歌唱评分效果,详见[评分功能教程](https://docs.agora.io/cn/online-ktv/ktv_score_android?platform=All%20Platforms)。 - - -### 常见问题 - -[如何使用连麦鉴权功能?](https://docs.agora.io/cn/InteractiveBroadcast/faq/token_cohost) - -### 相关文档 - -- [Agora 内容中心](https://docs.agora.io/cn/online-ktv/ktv_song_rest?platform=All%20Platforms) -- [歌词组件](https://docs.agora.io/cn/online-ktv/ktv_lrcview_ios?platform=iOS) \ No newline at end of file diff --git "a/markdown/online-ktv/03 \350\277\233\351\230\266\345\212\237\350\203\275/01 Agora \345\206\205\345\256\271\344\270\255\345\277\203.md" "b/markdown/online-ktv/03 \350\277\233\351\230\266\345\212\237\350\203\275/01 Agora \345\206\205\345\256\271\344\270\255\345\277\203.md" deleted file mode 100644 index 609e71cc262..00000000000 --- "a/markdown/online-ktv/03 \350\277\233\351\230\266\345\212\237\350\203\275/01 Agora \345\206\205\345\256\271\344\270\255\345\277\203.md" +++ /dev/null @@ -1,494 +0,0 @@ -Agora 内容中心提供在线 K 歌房场景所需歌曲内容,如歌曲、歌词、MV、热歌榜等。内容由音乐合作方提供,如需进一步了解 Agora 内容中心,请联系 [sales@agora.io](mailto:support@agora.io)。 - -点歌流程即调用 RESTful API 从内容中心获取曲库所有歌曲及相关信息,再获取指定歌曲及相关信息。你还可以通过调用 RESTful API 使用 Agora 内容中心的进阶功能。 - -
    目前仅支持中国大陆地区的 K 歌场景。
    - -## 前提条件 - -### 开通服务 - -请联系 [sales@agora.io](mailto:sales@agora.io) 开通 Agora 内容中心服务。 - -### HTTP 认证 - -点歌 RESTful API 仅支持 HTTPS 协议。发送请求时,你需要通过 Basic HTTP 认证,并将生成的凭证填入 HTTP 请求头部的 `Authorization` 字段。具体生成 `Authorization` 字段的方法请参考[ HTTP 基本认证](https://docs.agora.io/cn/cloud-recording/faq/restful_authentication)。 - -## 实现点歌功能 - -通过获取曲库所有歌曲列表和获取指定歌曲两个步骤来实现点歌。 - -### 数据格式 - -所有的请求都发送给域名:`api.agora.io`。 - -- 请求:请求的格式详见下面各个 API 中的示例 -- 响应:响应内容的格式为 JSON - -> 所有的请求 URL 和请求包体内容都是区分大小写的。 - -### 公共参数 -内容中心 RESTful API 的公共参数及描述如下: - -#### 请求参数 -| 参数 | 类型 | 描述 | -| :------- | :----- | :----------------------------------------------------------- | -| `appid` | (必填)String | 你的项目使用的 [App ID](https://docs.agora.io/cn/AgoraPlatform/get_appid_token?platform=AllPlatforms#获取-app-id),该 App ID 需要已开通内容中心权限。 | -| `requestId` | (必填)String | 本次请求的唯一标识。
  • 必须设置为 32 位字符串,支持大写字母、小写字母、数字、"_"(下划线)、"-"(中划线),不区分大小写。
  • 一个项目下 `requestId` 唯一。 | - -#### 响应参数 -| 参数 | 类型 | 描述 | -| :------- | :----- | :----------------------------------------------------------- | -| `code` | Int64 | 响应状态码,`0` 表示请求成功。 | -| `msg` | String | 返回消息名称,`ok` 表示请求成功。 | -| `requestId` | String | 请求唯一标识,与请求包体中的 `requestId` 一致。 | -| `ext` | String | 预留字段 | - - -### 获取曲库所有歌曲列表 - -首先,你需要调用该方法获取曲库中所有歌曲的列表。 - -#### HTTP 请求 -- 方法:GET -- 接入点:cn/v1.0/projects/{appid}/ktv-service/api/serv/songs - -**路径参数** - -`appid` :详见[公共参数](#param)。 - -**查询参数** - -| 参数 | 类型 | 描述 | -| :-------- | :----- | :----------------------------------------------------------- | -| `requestId` | (必填)String | 本次请求的唯一标识。详见[公共参数](#param)。 | -| `pageType` | (可选)Int64 | 翻页方式:
  • `0`:(默认)下一页
  • `1`:上一页 | -| `pageCode` |(可选) Int64 | 作为翻页锚点的歌曲编号。
  • 第一次获取曲库无需设置该参数。
  • 默认为当前页歌曲列表第一首歌曲的编号。 | -| `size` | (可选)Int64 | 每页歌曲显示的最大数量。默认为 `10`,取值范围为 [1, 1000]。 | -| `status` |(可选) Int64 | 歌曲状态:
  • `1`:(默认)已上架
  • `0`:已下架
  • `-1`:已删除
  • `9`:全部状态 | -| `songType` |(可选) Int64 |歌曲资源类型:
  • `1`:左声道伴奏,右声道原唱的单音轨纯音频歌曲。
  • `2`:只有伴唱的单音轨纯音频歌曲。
  • `3`:只有原唱的单音轨纯音频歌曲。
  • `4`:既有多音轨纯音频又有多音轨 MV 资源的歌曲。
  • `5`:只有多音轨 MV 资源的歌曲。
  • `6`:既有多音轨纯音频又有多音轨 MV 资源的歌曲 (该音源受数字版权保护)。
    默认获取所有类型。 | -| `vendorId` |(可选) Int64 |歌曲版权方编号:
  • `1`:咪咕
  • `2`:敖拜
  • `3`:其他
    默认获取所有版权方。 | - - -#### HTTP 响应 -如果响应状态码为 `0`,表示请求成功,响应包体中包含以下字段: - -| 字段 | 类型 | 描述 | -| :-------------------- | :--------- | :------------------------------------------ | -| `data` | JSON | 信息详情。 | -|`msg` | String | 本次请求返回的消息。`ok` 表示请求成功。| - |`requestId`|String | 请求 ID。本次请求的唯一标识。| - | `ext` | String | 预留字段 | -| `data.pageType` | Int64 | 翻页方式:
  • `0`:下一页
  • `1`:上一页 | -| `data.pageCode` | Int64 | 翻页锚点的歌曲编号。 | -| `data.size` | Int | 每页歌曲显示的最大数量。 | -| `data.count` | Int | 本次请求返回的歌曲数量。 | -| `data.list` | JSON Array | 当前曲库中所有的歌曲列表。 | -| `data.list.songCode` | Int64 | 歌曲编号。 | -| `data.list.name` | String | 歌曲名称。 | -| `data.list.singer` | String | 歌手名称。 | -| `data.list.poster` | String | 歌手封面图片 URL。 | -| `data.list.lyricType` | Number Array | 歌词格式类型:
  • `0`:xml 格式
  • `1`:lrc 格式。
    如果为空则表示没有歌词。 | -| `data.list.type` | Int64 | 歌曲资源类型:
  • `1`:左声道伴奏,右声道原唱的单音轨纯音频歌曲。
  • `2`:只有伴唱的单音轨纯音频歌曲。
  • `3`:只有原唱的单音轨纯音频歌曲。
  • `4`:既有多音轨纯音频又有多音轨MV资源的歌曲。
  • `5`:只有多音轨 MV 资源的歌曲。
  • `6`:既有多音轨纯音频又有多音轨MV资源的歌曲(受数字版权保护)。 | -| `data.list.duration` | Int64 | 歌曲时长(秒)。 | -| `data.list.status` | Int64 | 歌曲状态:
  • `1`:已上架
  • `0`:已下架
  • `-1`:已删除 | -| `data.list.updateTime` | Int64 | 歌曲更新的 Unix 时间戳(秒)。 | -| `data.list.releaseTime` | String | 歌曲发布时间。 | -| `data.list.vendorId` | Int64 | 歌曲版权方编号:
  • `1`:咪咕
  • `2`:敖拜
  • `3`:其他 | -| `data.list.pitchType` | Int64 | 歌曲是否支持演唱评分功能:
  • `1`:歌曲支持演唱评分功能
  • `2`:歌曲不支持演唱评分功能 | -| `data.list.mv` | JSON Array | 当前曲库中所有的 MV(Music Video)信息列表。 | -| `data.list.mv.resolution` | String | MV 的分辨率,目前支持分辨率包括 480P、720P 和 1080P。 | -| `data.list.mv.bw` | String | MV 的带宽,单位为 Kbps。 | -|`data.list.highPart` | Array | 音乐高潮片段 | -|`data.list.highPart.highStartTime`| Int64 | 音乐高潮片段的开始时间点,单位毫秒。| -|`data.list.highPart.highEndTime`| Int64 | 音乐高潮片段的结束时间点,单位毫秒。| - -其他响应字段及说明详见[公共参数](#param)。 -如果返回的 HTTP 状态码非 `0`,表示请求失败。你可以参考[响应状态码汇总表](#code)了解可能的原因。 - -#### 请求示例 - -```shell -curl 'https://api.agora.io/cn/v1.0/projects/{appid}/ktv-service/api/serv/songs?requestId=lzTZsruXVL3VUi2UVHHDTPE0PRvF8P4V&pageType=0&pageCode=6246262727281830&size=2' \ --H 'Authorization: {AuthorizationHeader}' -``` - -#### 响应示例 - -```json -{ - "code": 0, - "msg": "ok", - "requestId": "lzTZsruXVL3VUi2UVHHDTPE0PRvF8P4V", - "ext": "", - "data": { - "pageType": 0, - "size": 2, - "pageCode": 6246262727281830, - "count": 2, - "list": [ - { - "songCode": 6246262727281850, - "name": "龙拳", - "singer": "周杰伦", - "poster": "https://accpic.sd-rtn.com/pic/release/jpg/1/7a8941/ChmFZ1S0immAVtSDAAODVRzMNHg454.jpg", - "duration": 274, - "lyricType": [ - 0, - 1 - ], - "type": 1, - "releaseTime": "2014/11/27 9:32", - "status": 1, - "updateTime": 1638141088, - "vendorId": 1, - "pitchType": 1, - "mv": [{ - "resolution": "720P", - "bw": "0" - }, { - "resolution": "480P", - "bw": "0" - }], - "highPart": [{ - "highStartTime": 121122, - "highEndTime": "134213" - }, { - "highStartTime": 154534, - "highEndTime": "162134" - }], - }, - { - "songCode": 6246262727281860, - "name": "最长的电影", - "singer": "周杰伦", - "poster": "https://accpic.sd-rtn.com/pic/release/jpg/1/7a8941/ChmFZ1S0immAVtSDAAODVRzMNHg454.jpg", - "duration": 230, - "lyricType": null, - "type": 1, - "releaseTime": "2014/11/27 9:32", - "status": 1, - "vendorId": 1, - "pitchType": 1, - "updateTime": 1635229782, - "mv": null, - "highPart": [{ - "highStartTime": 121221, - "highEndTime": "132132" - }, { - "highStartTime": 154534, - "highEndTime": "162134" - }], - } - ] - } -} -``` - -## 进阶功能 -### 获取增量歌曲列表 -你可以调用该方法定期查询曲库中增量的歌曲。建议每隔 24 小时查询一次。 - -#### HTTP 请求 - -- 方法:GET -- 接入点:cn/v1.0/projects/{appid}/ktv-service/api/serv/songs-incr - -**路径参数** - -`appid` :详见[公共参数](#param)。 - -**查询参数** - -| 参数 | 类型 | 描述 | -| :-------- | :----- | :----------------------------------------------------------- | -| `requestId` | (必填)String | 本次请求的唯一标识。详见[公共参数](#param)。 | -| `lastUpdateTime` |(必填) Int64 | 最近一次更新曲库的 Unix 时间戳(秒)。 | -| `page` |(可选) Int64 | 目标页编号。默认为 `1`。取值范围为 [1, (232-1)]。 | -| `size` |(可选) Int64 | 目标页歌曲显示的最大数量。默认为 10,取值范围为 [1, 1000]。 | -| `status` | (可选)Int | 歌曲状态:
  • `1`:(默认)已上架
  • `0`:已下架
  • `-1`:已删除
  • `9`:全部状态 | -| `songType` |(可选) Int64 |歌曲资源类型:
  • `1`:左声道伴奏,右声道原唱的单音轨纯音频歌曲。
  • `2`:只有伴唱的单音轨纯音频歌曲。
  • `3`:只有原唱的单音轨纯音频歌曲。
  • `4`:既有多音轨纯音频又有多音轨MV资源的歌曲。
  • `5`:只有多音轨 MV 资源的歌曲。
  • `6`:既有多音轨纯音频又有多音轨MV资源的歌曲(受数字版权保护)。
    默认为所有类型。 | -| `vendorId` |(可选) Int64 |歌曲版权方编号:
  • `1`:咪咕
  • `2`:敖拜
  • `3`:其他
    默认获取所有版权方。 | - - -**注意** -- 调用该方法,你会得到指定页更新时间大于 `lastUpdateTime` 的歌曲列表。 -- 如果你是第一次调用该方法,Agora 建议你将 `lastUpdateTime` 设置为 `0`,即获取曲库全部歌曲列表;如果不是第一次调用该方法,Agora 建议你将 `lastUpdateTime` 设置为前一次调用该方法获得的响应字段中 `updateTime` 的最大值。 -- 你需要将每个 `page` 的 `lastUpdateTime` 设置为一致,否则会遗漏增量歌曲。 - -#### HTTP 响应 -如果响应状态码为 `0`,表示请求成功,响应包体中包含以下字段: - -| 字段 | 类型 | 字段说明 | -| :-------------- | :----- | :----------------------------------------------------------- | -| `data` | JSON | 信息详情。 | -|`msg` |String |本次请求返回的消息,`ok` 表示请求成功。| -|`requestId`|String | 请求 ID。本次请求的唯一标识。| - | `ext` | String | 预留字段。 | -| `data.page` | Int64 | 当前页编号。 | -| `data.size` | Int64 | 当前页歌曲显示的最大数量。 | -| `data.count` | Int64 | 本次请求返回的歌曲数量。 | -| `data.list` | JSON Array | 本次请求的歌曲列表。 | -| `data.list.songCode` | Int64 | 歌曲编号。 | -| `data.list.name` | String | 歌曲名称。 | -| `data.list.singer` | String | 歌手名称。 | -| `data.list.poster` | String | 歌手封面图片 URL。 | -| `data.list.lyricType` | Number Array | 歌词格式类型:
  • `0`:xml 格式
  • `1`:lrc 格式。
    如果为空则表示没有歌词。 | -| `data.list.type` | Int | 歌曲资源类型:
  • `1`:左声道伴奏,右声道原唱的单音轨纯音频歌曲。
  • `2`:只有伴唱的单音轨纯音频歌曲。
  • `3`:只有原唱的单音轨纯音频歌曲。
  • `4`:既有多音轨纯音频又有多音轨MV资源的歌曲。
  • `5`:只有多音轨 MV 资源的歌曲。
  • `6`:既有多音轨纯音频又有多音轨MV资源的歌曲(受数字版权保护)。 | - | `data.list.duration` | Int64 | 歌曲时长(秒)。 | - | `data.list.status` | Int | 歌曲状态:
  • `1`:已上架
  • `0`:已下架
  • `-1`:已删除 | - | `data.list.updateTime` | Int64 | 歌曲更新的 Unix 时间戳(秒)。 | -| `data.list.releaseTime` | String | 歌曲发布时间。 | - | `data.list.vendorId` | Int64 | 歌曲版权方编号:
  • `1`:咪咕
  • `2`:敖拜
  • `3`:其他 | -| `data.list.pitchType` | Int64 | 歌曲是否支持演唱评分功能:
  • `1`:歌曲支持演唱评分功能
  • `2`:歌曲不支持演唱评分功能 | - | `data.list.mv` | JSON Array | 当前曲库中所有的 MV(Music Video)信息列表。 | - | `data.list.mv.resolution` | String | MV 的分辨率,目前支持分辨率包括 480P、720P 和 1080P。 | - | `data.list.mv.bw` | String | MV 的带宽,单位为 Kbps。 | -|`data.list.highPart` | Array | 音乐高潮片段 | -|`data.list.highPart.highStartTime`| Int64 | 音乐高潮片段的开始时间点,单位毫秒。| -|`data.list.highPart.highEndTime`| Int64 | 音乐高潮片段的结束时间点,单位毫秒。| - -其他响应字段及说明详见[公共参数](#param)。 -如果返回的 HTTP 状态码非 `0`,表示请求失败。你可以参考[响应状态码汇总表](#code)了解可能的原因。 - -#### 请求示例 - -```shell -curl 'https://api.agora.io/cn/v1.0/projects/{appid}/ktv-service/api/serv/songs-incr?requestId=lzTZsruXVL3VUi2UVHHDTPE0PRvF8P4V&pageType=0&lastUpdateTime=1635229837&page=1&size=2' \ --H 'Authorization: {AuthorizationHeader}' -``` - -#### 响应示例 - -```json -{ - "code": 0, - "msg": "ok", - "requestId": "lzTZsruXVL3VUi2UVHHDTPE0PRvF8P4V", - "ext": "", - "data": { - "size": 2, - "page": 1, - "count": 2, - "list": [{ - "songCode": 6246262727295570, - "name": "没有目的地爱了", - "singer": "杨千嬅", - "poster": "https://accpic.sd-rtn.com/pic/release/jpg/1/a35420/ChmFaVS9H2uARX7BAAFV0nstAAM724.jpg", - "duration": 267, - "lyricType": null, - "type": 2, - "releaseTime": "2014/12/15 22:13", - "vendorId": 1, - "pitchType": 1, - "status": 1, - "updateTime": 1635229838, - "mv": null, - "highPart": [{ - "highStartTime": 121221, - "highEndTime": "132132" - }, { - "highStartTime": 154534, - "highEndTime": "162134" - }], - }, { - "songCode": 6246262727295610, - "name": "热血青年", - "singer": "杨千嬅", - "poster": "https://accpic.sd-rtn.com/pic/release/jpg/1/a35420/ChmFaVS9H2uARX7BAAFV0nstAAM724.jpg", - "duration": 411, - "lyricType": [ - 0 - ], - "type": 1, - "releaseTime": "2015/1/12 11:00", - "vendorId": 1, - "pitchType": 1, - "status": 1, - "updateTime": 1635229838, - "mv": null, - "highPart": [{ - "highStartTime": 121221, - "highEndTime": "132132" - }, { - "highStartTime": 154534, - "highEndTime": "162134" - }], - }] - } -} -``` - -### 获取热歌榜单类型 - -你可以调用该方法获取热歌榜单名称和类型。 - -#### HTTP 请求 - -- 方法:GET -- 接入点:/cn/v1.0/projects/{appid}/ktv-service/api/serv/hot-type - -**路径参数** - -`appid` :详见[公共参数](#param)。 - -**查询参数** - -| 参数 | 类型 | 描述 | -| :---------- | :------------- | :----------------------------------------------------------- | -| `requestId` | (必填)String | 本次请求的唯一标识。详见[公共参数](#param)。 | - - -#### HTTP 响应 - -如果响应状态码为 `0`,表示请求成功,响应包体中包含以下字段: - -| 字段 | 类型 | 字段说明 | -| :------------------- | :--------- | :---------------------------------------------- | -| `code` | Int64 | 响应状态码,`0` 表示请求成功。 | -| `msg` | String | 返回消息名称,`ok` 表示请求成功。 | -| `requestId` | String | 请求唯一标识,与请求包体中的 `requestId` 一致。 | -| `ext` | String | 预留字段。 | -| `data` | JSON | 信息详情。 | -| `data.list` | JSON Array | 热歌榜单信息列表。 | -| `data.list.hotName` | String | 热歌榜单名称。 | -| `data.list.hotType` | Int64 | 热歌榜单类型。 | - -其他响应字段及说明详见[公共参数](#param)。 -如果返回的 HTTP 状态码非 `0`,表示请求失败。你可以参考[状态码汇总表](#code)了解可能的原因。 - -#### 请求示例 - -```shell -curl 'https://api.agora.io/cn/v1.0/projects/{appid}/ktv-service/api/serv/hot-type?requestId=lzTZsruXVL3VUi2UVHHDTPE0PRvF8P4V' \ --H 'Authorization: {AuthorizationHeader}' -``` -#### 响应示例 - -```json -{ - "code": 0, - "msg": "ok", - "requestId": "lzTZsruXVL3VUi2UVHHDTPE0PRvF8P4V", - "ext": "", - "data": { - list:[{ - "hotName": "声网榜单", - "hotType": 1 - }, { - "hotName": "新歌榜", - "hotType": 2 - }] - } -} -``` - -### 获取热歌榜单详情 - -你可以调用该方法获取热歌榜单详情。 - -> - 热歌为日点播次数大于或等于 10 次的歌曲。 -> - 调用该方法获取的热歌榜单仅显示日点播次数前 100 的热歌,如果热歌数量少于 100 首则按实际数量显示。 -> - 每日 05:00 统计 0:00 之前的数据。 - -#### HTTP 请求 - -- 方法:GET -- 接入点:/cn/v1.0/projects/{appid}/ktv-service/api/serv/song-hot - -**路径参数** - -`appid` :详见[公共参数](#param)。 - -**查询参数** - -| 参数 | 类型 | 描述 | -| :---------- | :------------- | :----------------------------------------------------------- | -| `requestId` | (必填)String | 本次请求的唯一标识。详见[公共参数](#param)。 | -| `hotType` | (可选)Int64 | 榜单类型:
  • `0`:(默认)整体榜单
  • `2`:新歌榜
  • `3`:嗨唱推荐
  • `4`:抖音热歌
  • `5`:古风热歌
  • `6`:KTV 必唱 | - -#### HTTP 响应 - -如果响应状态码为 `0`,表示请求成功,响应包体中包含以下字段: - -| 字段 | 类型 | 字段说明 | -| :------------------- | :--------- | :---------------------------------------------- | -| `data` | JSON | 信息详情。 | -| `msg` | String | 返回消息名称,`ok` 表示请求成功。 | -| `requestId` | String | 请求唯一标识,与请求包体中的 `requestId` 一致。 | -| `data.list` | JSON Array | 当前曲库中所有的歌曲列表。 | -| `data.list.songCode` | Int64 | 歌曲编号。 | -| `data.list.num` | Int64 | 点播次数。 | - | `ext` | String | 预留字段。 | - -其他响应字段及说明详见[公共参数](#param)。 -如果返回的 HTTP 状态码非 `0`,表示请求失败。你可以参考[状态码汇总表](#code)了解可能的原因。 - -#### 请求示例 - -```shell -curl 'https://api.agora.io/cn/v1.0/projects/{appid}/ktv-service/api/serv/song-hot?requestId=lzTZsruXVL3VUi2UVHHDTPE0PRvF8P4V&hotType=1' \ --H 'Authorization: {AuthorizationHeader}' -``` -#### 响应示例 - -```json -{ - "code": 0, - "msg": "ok", - "requestId": "lzTZsruXVL3VUi2UVHHDTPE0PRvF8P4V", - "ext": "", - "data": { - "list": [{ - "songCode": 6246262727281920, - "num": 157 - }, { - "songCode": 6246262727281870, - "num": 149 - }, { - "songCode": 6246262727282110, - "num": 145 - }, { - "songCode": 6246262727282151, - "num": 133 - }, { - "songCode": 6246262727282060, - "num": 132 - }, { - "songCode": 6246262727282040, - "num": 123 - }, { - "songCode": 6246262727281930, - "num": 99 - }, { - "songCode": 6246262727282010, - "num": 95 - }, { - "songCode": 6246262727282061, - "num": 84 - }, { - "songCode": 6246262727282180, - "num": 73 - }] - } -} -``` - -## 保障 REST 服务高可用 - -~78021d80-6bc4-11ed-8dae-bf25bf08a626~ - -## 参考信息 -### 响应状态码汇总表 - -| code | 说明 | -| :--- | :--------------------- | -| `0 ` | 正常 | -| `1000` | 查询数据结果为空。 | -| `1001` | 操作异常。 | -| `1002` | [App ID](https://docs.agora.io/cn/Agora%20Platform/get_appid_token?platform=All%20Platforms#获取-app-id) 异常,如过期、关闭,该 App ID 未开通内容中心等,或白名单中的 IP 地址不合法。 | -| `1003` | 系统异常,请联系技术支持。 | -| `1004` | 系统繁忙,请稍后再试。 | -| `1005` | 参数错误。 | -| `1008` | 没有该歌曲资源权限。 | -| `1009` | 该歌曲资源已下架。 | \ No newline at end of file diff --git "a/markdown/online-ktv/03 \350\277\233\351\230\266\345\212\237\350\203\275/02 \346\255\214\350\257\215\347\273\204\344\273\266 (Android).md" "b/markdown/online-ktv/03 \350\277\233\351\230\266\345\212\237\350\203\275/02 \346\255\214\350\257\215\347\273\204\344\273\266 (Android).md" deleted file mode 100644 index e809b909c68..00000000000 --- "a/markdown/online-ktv/03 \350\277\233\351\230\266\345\212\237\350\203\275/02 \346\255\214\350\257\215\347\273\204\344\273\266 (Android).md" +++ /dev/null @@ -1,155 +0,0 @@ -Agora 歌词组件支持在歌曲播放时同步显示 LRC 或 XML 格式的歌词,同时支持将演唱者和歌曲原唱的音调进行对比,并实时将匹配度以动画和分数的方式呈现在界面上。 - -本文介绍如何在项目中集成并使用 Agora 歌词组件。 - - - -## 功能描述 - -### 歌词展示与同步 - -- 歌曲播放时,根据当前播放进度显示对应的歌词,当手势拖动到指定时间的歌词,歌曲进度随之改变。 -- 自定义歌词视图和歌词背景图。 - -### 歌唱音准和评分 - -- 歌曲播放时,根据当前播放进度展示对应的原唱音准线。 -- 实时展示演唱者和原唱音准线的匹配效果,以及演唱者的分数。 -- 自定义评分视图的部分元素。 - -
    仅 XML 格式的歌词支持歌唱音准和评分功能。
    - -## 前提条件 - -在使用歌词组件前,请确保你已在项目中集成 Agora 视频 SDK v4.x,并实现在线 K 歌功能,详见[客户端实现](https://docs.agora.io/cn/online-ktv/chorus_client_android?platform=Android)。 - -## 实现方法 - -### 使用 JitPack 自动集成歌词组件 - -1. 如果未添加 JitPack,请在 `/Gradle Scripts/build.gradle(Project: )` 中添加如下代码,将 JitPack 添加到仓库列表中: - -```java - -all projects { - repositories { - ... - maven { url 'https://www.jitpack.io' } - } -} -``` - -2. 在 `/Gradle Scripts/build.gradle(Module: .app)` 中,添加如下代码,将歌词组件集成到你的项目中: - -```java -... -dependencies { - ... - implementation 'com.github.AgoraIO-Community:lrcview-android:1.0.13' -} -``` - -### 声明和初始化歌词组件对象 - -在项目的 Activity 中,声明和初始化歌词组件对象。示例代码如下: - -```java -public class LiveActivity extends RtcBaseActivity { - private LrcView mLrcView; - - - - @Override - protected void onCreate(Bundle savedInstanceState) { - ... - mLrcView = findViewById(R.id.lrc_view); - ... - } -} -``` - -### 实现歌词显示及同步 - -参考如下歌词组件 API 使用时序见,实现歌词显示及同步功能: - -![](https://web-cdn.agora.io/docs-files/1630054208808) - -
    由于 Android UI 系统限制,以上 API 的调用顺序必须严格按照 API 时序图顺序,且必须在 UI 线程中调用,否则会导致 App 崩溃。
    - -## 进阶功能 - -### 实现评分功能 - -按照如下步骤,实现歌词组件评分功能: - -1. 调用 Agora SDK 的 `enableAudioVolumeIndication` 方法开启歌唱评分功能,通过监听 `onAudioVolumeIndication` 回调获取本地用户的人声音调(`voicePitch`),并传入歌词组件。详见[评分功能](https://docs.agora.io/cn/online-ktv/ktv_score_android?platform=Android)。 - -2. 设置歌词组件的评分相关的回调监听。 - -```java -public interface OnActionListener { -// 原唱标准音调回调,详见 API 参考 -void onOriginalPitch(double pitch, int totalCount); -// 评分回调,详见 API 参考 -void onScore(double score, double cumulativeScore, double totalScore); -} -``` - -### 自定义歌词组件界面 - -在项目的 Activity 中,自定义歌词组件的界面元素。示例代码如下: - -```java - -``` - -## 相关信息 - -### API 参考 - -歌词组件的核心 API 如下: - -| API | 实现功能 | -| :------------------------------------ | :----------------------------------------------------------- | -| `setActionListener` | 订阅 `OnActionListener` 回调事件。`OnActionListener` 为 lrcview 控件回调类,包括:歌词加载完成回调、进度改变回调、开始拖动歌词回调、结束拖动歌词回调。 | -| `setTotalDuration` | 设置歌词总时长,单位毫秒。必须与歌曲时长一致。 | -| `loadLrc(mainLrcText, secondLrcText)` | 加载本地歌词文件。
  • 支持加载 LRC 格式的双语歌词,`mainLrcText` 是中文歌词对象,`secondLrcText` 是英文歌词对象。
  • 对于非双语歌词, 将 `mainLrcText` 或 `secondLrcText` 设置为 null。 | -| `onLoadLrcCompleted` | 歌词文件加载完成回调。 | -| `setEnableDrag` | 设置是否允许上下拖动歌词。 | -| `updateTime` | 根据当前歌曲播放进度更新歌词进度,单位为毫秒。 | -| `hasLrc` | 获取歌词文件状态。
  • true:歌词有效
  • false:歌词无效,无法播放 | -| `reset` | 重置内部状态,清空已经加载的歌词。 | -| `onOriginalPitch` | 原唱标准音调回调。你可以通过该回调自行实现评分逻辑。包含如下参数:
  • `pitch`:当前歌词音调对应的 pitch 值,每个音调回调一次。
  • `totalCount`:整个 XML 歌词的 pitch 个数,用于计算平均分。 | -| `onScore` | 评分回调。每句歌词(sentence)结束时触发该回调。你可以通过该回调自行实现分数计算。包含如下参数:
  • `score`:当前句分数,范围为 [40,100]。
  • `cumulativeScore`:当前累计分数。
  • `totalScore`:歌曲总分数 = 初始分数(默认为 0 分) + 歌词句段数量 * 100
    当开启评分回调后, 歌词可拖动功能默认失效。
    | - -### 示例项目 - -Agora 在 GitHub 上提供歌词组件开源的[示例项目](https://github.com/AgoraIO-Community/LrcView-Android)供你参考。 \ No newline at end of file diff --git "a/markdown/online-ktv/03 \350\277\233\351\230\266\345\212\237\350\203\275/02 \346\255\214\350\257\215\347\273\204\344\273\266 (iOS).md" "b/markdown/online-ktv/03 \350\277\233\351\230\266\345\212\237\350\203\275/02 \346\255\214\350\257\215\347\273\204\344\273\266 (iOS).md" deleted file mode 100644 index 5fd959c08dc..00000000000 --- "a/markdown/online-ktv/03 \350\277\233\351\230\266\345\212\237\350\203\275/02 \346\255\214\350\257\215\347\273\204\344\273\266 (iOS).md" +++ /dev/null @@ -1,302 +0,0 @@ -Agora 歌词组件支持在歌曲播放时同步显示 LRC 或 XML 格式的歌词,同时支持将演唱者和歌曲原唱的音调进行对比,并实时将匹配度以动画和分数的方式呈现在界面上。 - -本文介绍如何在项目中集成并使用 Agora 歌词组件。 - - - -## 功能描述 - -### 歌词展示与同步 - -- 歌曲播放时,根据当前播放进度显示对应的歌词,当手势拖动到指定时间的歌词,歌曲进度随之改变。 -- 自定义歌词视图和歌词背景图。 - -### 歌唱音准和评分 - -- 歌曲播放时,根据当前播放进度展示对应的原唱音准线。 -- 实时展示演唱者和原唱音准线的匹配效果,以及演唱者的分数。 -- 自定义评分视图的部分元素。 - -
    仅 XML 格式的歌词支持歌唱音准和评分功能。
    - -## 前提条件 - -在使用歌词组件前,请确保你已在项目中集成 Agora 视频 SDK v4.x,并实现在线 K 歌功能,详见[客户端实现](https://docs.agora.io/cn/online-ktv/chorus_client_ios?platform=iOS)。 - -## 实现方法 - -![](https://web-cdn.agora.io/docs-files/1668754460096) - -### 集成歌词组件 - -请按照如下步骤,使用 CocoaPods 自动集成歌词组件: - -1. 开始前请确保你已安装 Cocoapods。参考 [Getting Started with CocoaPods](https://guides.cocoapods.org/using/getting-started.html#getting-started) 安装说明。 - -2. 在终端里进入项目根目录,并运行 pod init 命令。项目文件夹下会生成一个 **Podfile** 文本文件。 - -3. 打开 **Podfile** 文件,修改文件为如下内容。注意将 `Your App` 替换为你的 Target 名称,并将 `version` 替换为你需集成的歌词组件版本。 - - ``` - target 'Your App' do - // version 请填写具体的版本号,最新版本为 1.0.8.3。 - pod 'AgoraLyricsScore', '~> version' - end - ``` - -4. 运行 `pod install` 命令安装歌词组件。成功安装后,**Terminal** 中会显示 `Pod installation complete!`,此时项目文件夹下会生成一个 `xcworkspace` 文件。 - -5. 打开新生成的 `xcworkspace` 文件。 - -**初始化歌词组件对象** - -声明并初始化歌词组件对象。示例代码如下: - -```swift -private lazy var lrcScoreView: AgoraLrcScoreView = { - let lrcScoreView = AgoraLrcScoreView(delegate: self) - let config = AgoraLrcScoreConfigModel() - // config.isHiddenScoreView = true - let scoreConfig = AgoraScoreItemConfigModel() - scoreConfig.tailAnimateColor = .yellow - scoreConfig.scoreViewHeight = 100 - scoreConfig.emitterColors = [.systemPink] - config.scoreConfig = scoreConfig - let lrcConfig = AgoraLrcConfigModel() - lrcConfig.lrcFontSize = .systemFont(ofSize: 15) - lrcConfig.isHiddenWatitingView = false - lrcConfig.isHiddenBottomMask = true - lrcConfig.lrcHighlightFontSize = .systemFont(ofSize: 18) - lrcConfig.lrcTopAndBottomMargin = 10 - lrcConfig.tipsColor = .white - config.lrcConfig = lrcConfig - lrcScoreView.config = config - return lrcScoreView -}()lrcConfig -``` - -### 开始、停止播放歌词 - -设置你需要播放的歌词 URL 地址,并开始、停止滚动播放歌词,示例代码如下: - -```swift -// 设置歌词地址。歌词下载完成后会收到 downloadLrcFinished 回调。 -lrcScoreView.setLrcUrl(url: "https://example.com") -// 开始滚动歌词 (需在收到 downloadLrcFinished 回调后调用)。 -lrcScoreView.start() -// 停止滚动歌词。 -lrcScoreView.stop() -// 重置歌词界面。 -lrcScoreView.reset() -// 重置歌词时间。 -lrcScoreView.resetTime() -``` - -下载歌词到本地会导致 app 体积不断增加,为保证 app 运行性能,Agora 建议你定期清理歌词组件的缓存,示例代码如下: - -```swift -AgoraLrcScoreView.cleanCache() -``` - -### 设置代理事件回调监听器 - -#### 歌词回调 - -在 `AgoraLrcViewDelegate` 中设置歌词相关方法,实现歌词与歌曲播放同步。示例代码如下: - -```swift -weak var delegate: AgoraLrcViewDelegate? - -protocol AgoraLrcViewDelegate { - // 获取当前歌曲播放进度(毫秒) - func getPlayerCurrentTime() -> TimeInterval - // 获取歌曲总时长(毫秒) - func getTotalTime() -> TimeInterval - // 定位到指定播放位置(毫秒) - @objc - optional func seekToTime(time: TimeInterval) - // 获取当前播放的歌词和进度 - @objc - optional func currentPlayerLrc(lrc: String, progress: CGFloat) - // 获取歌词对应的原唱标准音调 - @objc - optional func agoraWordPitch(pitch: Int, totalCount: Int) -} -``` - -| API | 实现功能 | 是否必须 | -| :------------------- | :----------------------------------------------------------- | :------- | -| `getPlayerCurrentTime` | 获取当前歌曲播放进度(毫秒)。
    你需要自定实现如下逻辑:通过 `AgoraRtcMediaPlayerProtocol` 类的 [`getPosition`](https://docs.agora.io/cn/voice-call-4.x/API%20Reference/ios_ng/API/class_imediaplayer.html#api_imediaplayer_getplayposition) 获取当前播放进度,并将返回值传入歌词组件。 | 是 | -| `getTotalTime` | 获取歌曲总时长(毫秒)。
    你需要自定实现如下逻辑:通过 `AgoraRtcMediaPlayerProtocol` 的 [`getDuration`](https://docs.agora.io/cn/live-streaming-premium-4.x/API%20Reference/ios_ng/API/class_imediaplayer.html?platform=iOS#api_imediaplayer_getduration) 获取歌曲总时长,并将返回值传入歌词组件。
    | 是 | -| `seekToTime` | 定位到指定的播放位置(毫秒)。 | 否 | -| `currentPlayerLrc` | 获取当前播放的歌词和进度。 | 否 | -| `agoraWordPitch` | 获取每个歌词文字的标准 `pitch`。 | 否 | - - - -#### 歌词下载回调 - -在 `AgoraLrcDownloadDelegate` 中设置歌词下载状态回调,实现歌词下载进度通知。示例代码如下: - -```swift -weak var downloadDelegate: AgoraLrcDownloadDelegate? - -protocol AgoraLrcDownloadDelegate { - // 下载开始 - @objc - optional func beginDownloadLrc(url: String) - // 下载完成 - @objc - optional func downloadLrcFinished(url: String) - // 下载进度 - @objc - optional func downloadLrcProgress(url: String, progress: Double) - // 下载失败 - @objc - optional func downloadLrcError(url: String, error: Error?) - // 下载取消 - @objc - optional func downloadLrcCanceld(url: String) - // 开始解析歌词 - @objc - optional func beginParseLrc() - // 解析歌词结束 - @objc - optional func parseLrcFinished() -} -``` - -#### 歌唱评分回调 - -在 `AgoraKaraokeScoreDelegate` 中设置分数计算方法,实现歌唱评分实时显示。示例代码如下: - -```swift -weak var scoreDelegate: AgoraKaraokeScoreDelegate? -protocol AgoraKaraokeScoreDelegate { - @objc optional func agoraKaraokeScore(score: Double, cumulativeScore: Double, totalScore: Double) -} -``` - -| 参数 | 描述 | -| :---------------- | :--------------- | -| `score` | 当前行得分 | -| `cumulativeScore` | 当前累计得分 | -| `totalScore` | 当前歌曲总分数 | - -## 自定义歌词组件界面 - -Agora 歌词组件提供默认配置(参考示例代码),你也可以根据自身业务场景需求自定义歌词组件界面元素。 - -### 组件基础配置 - -```swift -// 评分模块配置 -public var scoreConfig: AgoraScoreItemConfigModel = .init() -// 歌词模块配置 -public var lrcConfig: AgoraLrcConfigModel = .init() -// 是否隐藏评分模块 -public var isHiddenScoreView: Bool = false -// 歌词背景图 -public var backgroundImageView: UIImageView? -// 评分模块和歌词模块之间的间距,默认为 0 -public var spacing: CGFloat = 0 -``` - -### 歌词视图配置 - -```swift -// 无歌词提示文案 -public var tipsString: String = "纯音乐,无歌词" -// 提示文字颜色 -public var tipsColor: UIColor = .orange -// 提示文字大小 -public var tipsFont: UIFont = .systemFont(ofSize: 17) -// 歌词进度分割线颜色 -public var separatorLineColor: UIColor = .lightGray -// 是否隐藏歌词进度分割线 -public var isHiddenSeparator: Bool = false -// 默认歌词背景色 -public var lrcNormalColor: UIColor = .gray -// 高亮歌词背景色 -public var lrcHighlightColor: UIColor = .white -// 实时绘制的歌词颜色 -public var lrcDrawingColor: UIColor = .orange -// 歌词文字大小 -public var lrcFontSize: UIFont = .systemFont(ofSize: 15) -// 歌词高亮文字大小 -public var lrcHighlightFontSize: UIFont = .systemFont(ofSize: 18) -// 歌词视图最大宽度 -public var maxWidth: CGFloat = UIScreen.main.bounds.width - 30 -// 歌词行间距 -public var lrcTopAndBottomMargin: CGFloat = 10 -// 是否隐藏等待开始圆点 -public var isHiddenWatitingView: Bool = false -// 等待开始圆点背景色 -public var waitingViewBgColor: UIColor? = .gray -// 等待开始圆点大小 默认: 10 -public var waitingViewSize: CGFloat = 10 -// 等待开始圆点底部间距 -public var waitingViewBottomMargin: CGFloat = 0 -// 是否可以拖动歌词,默认为 true。如果开启评分功能,则需要设置为 false。 -public var isDrag: Bool = true -// 底部蒙层颜色 -public var bottomMaskColors: [UIColor] = [UIColor(white: 0, alpha: 0.05), - UIColor(white: 0, alpha: 0.8)] -// 底部蒙层位置 -public var bottomMaskLocations: [NSNumber] = [0.7, 1.0] -// 底部蒙层高度,默认为歌词视图的高度 -public var bottomMaskHeight: CGFloat = 0 -// 是否隐藏底部蒙层 -public var isHiddenBottomMask: Bool = false -``` - -### 评分视图配置 - -```swift -// 评分视图高度 -public var scoreViewHeight: CGFloat = 100 -// 音准匹配圆点的起始位置 -public var innerMargin: CGFloat = 100 -// 音准线的高度 -public var lineHeight: CGFloat = 10 -// 音准线的宽度 -public var lineWidht: CGFloat = 120 -// 音准线颜色 -public var normalColor: UIColor = .gray -// 音准匹配后,音准线的颜色 -public var highlightColor: UIColor = .orange -// 垂直和上下分割线的颜色 -public var separatorLineColor: UIColor = .systemPink -// 是否隐藏垂直分割线 -public var isHiddenVerticalSeparatorLine: Bool = false -// 是否隐藏上下分割线 -public var isHiddenSeparatorLine: Bool = false -// 游标背景色 -public var cursorColor: UIColor = .systemPink -// 游标的宽 -public var cursorWidth: CGFloat = 20 -// 游标的高 -public var cursorHeight: CGFloat = 20 -// 是否隐藏粒子动画效果 -public var isHiddenEmitterView: Bool = false -// 使用图片创建粒子动画,如果设置为空,则默认使用 emitterColors 创建粒子动画 -public var emitterImages: [UIImage]? -// 使用颜色创建粒子动画 -public var emitterColors: [UIColor] = [.red] -// 尾部动画图片 -public var tailAnimateImage: UIImage? -// 尾部动画颜色 -public var tailAnimateColor: UIColor? = .yellow -// 每行歌词默认分数 -public var defaultScore: Double = 50 -// 每行歌词分制,默认为百分制 -public var lineCalcuScore: Double = 100 -// 每行歌词最低分,低于该值则不计入最后分数 -public var minCalcuScore: Double = 40 -``` - -## 相关信息 - -### 示例项目 - -Agora 在 GitHub 上提供歌词组件开源的[示例项目](https://github.com/AgoraIO-Community/LrcView-iOS)供你参考。 \ No newline at end of file diff --git "a/markdown/online-ktv/03 \350\277\233\351\230\266\345\212\237\350\203\275/03 \350\257\204\345\210\206\345\212\237\350\203\275 (Android).md" "b/markdown/online-ktv/03 \350\277\233\351\230\266\345\212\237\350\203\275/03 \350\257\204\345\210\206\345\212\237\350\203\275 (Android).md" deleted file mode 100644 index 9262248d421..00000000000 --- "a/markdown/online-ktv/03 \350\277\233\351\230\266\345\212\237\350\203\275/03 \350\257\204\345\210\206\345\212\237\350\203\275 (Android).md" +++ /dev/null @@ -1,79 +0,0 @@ -Agora 在线 K 歌房支持歌唱评分功能,即把演唱者和歌曲原唱的音调进行对比,并实时将匹配度以动画和分数的方式呈现在界面上。 - - - -## 前提条件 - -在实现评分功能前,请确保你已在项目中集成 Agora 音频 SDK v4.0.0 Beta,并实现在线 K 歌功能,详见如下教程: - -- [客户端实现(合唱功能 Android)](https://docs.agora.io/cn/online-ktv/chorus_client_android?platform=Android) -- [客户端实现(合唱功能 iOS)](https://docs.agora.io/cn/online-ktv/chorus_client_ios?platform=iOS) - -## 实现方法 - -1. 调用 `enableAudioVolumeIndication` 方法开启歌唱评分功能。示例代码如下: - -```java -// Java -getRtcEngine().enableAudioVolumeIndication(30, 10, true); -``` - -```swift -// Swift -rtc.enableAudioVolumeIndication(20, smooth: 3, reportVad: true) -``` - -2. 成功开启歌唱评分功能后,只要频道内有发流用户,SDK 会在加入频道后按设置的时间间隔触发音量信息提示回调。你需要设置该回调监听,以获取本地用户的人声音调(`voicePitch`)。 - -```java -// Java -@Override -public void onAudioVolumeIndication(AudioVolumeInfo[] speakers, int totalVolume) { - for(AudioVolumeInfo info : speakers){ - if(info.uid == 0 && info.voicePitch > 0){ - mMainThreadDispatch.onLocalPitch(info.voicePitch); - } - } -} -``` - -```swift -// Swift -func rtcEngine(_: AgoraRtcEngineKit, reportAudioVolumeIndicationOfSpeakers speakers: [AgoraRtcAudioVolumeInfo], totalVolume _: Int) { - voicePitchRelay.accept(speakers.map { $0.voicePitch }) -} -``` - -3. 将获取到的 `voicePitch` 传递到歌词组件,示例代码如下: - - 如何使用歌词组件实现评分效果,请参考[歌词组件使用教程 Android 端](https://docs.agora.io/cn/online-ktv/ktv_lrcview_android?platform=Android) 或 [歌词组件使用教程 iOS 端](https://docs.agora.io/cn/online-ktv/ktv_lrcview_android?platform=Android)。 - -```java - -// Java -@Override -public void onLocalPitch(double pitch) { - super.onLocalPitch(pitch); - mBinding.lrcControlView.getPitchView().updateLocalPitch(pitch); -} -``` - -```swift -// Swift -RoomManager.shared().subscribeVoicePitch().subscribe { result in - guard let value = result.element else { return } - self.lrcScoreView.setVoicePitch(value) -}.disposed(by: disposeBag) -``` - -## API 参考 - -### Android - -- [`enableAudioVolumeIndication`](https://docs.agora.io/cn/live-streaming-premium-4.x/API%20Reference/java_ng/API/class_irtcengine.html#api_enableaudiovolumeindication) -- [`onAudioVolumeIndication`](https://docs.agora.io/cn/live-streaming-premium-4.x/API%20Reference/java_ng/API/class_irtcengineeventhandler.html#callback_onaudiovolumeindication) - -### iOS - -- [`enableAudioVolumeIndication`](https://docs.agora.io/cn/live-streaming-premium-4.x/API%20Reference/ios_ng/API/class_irtcengine.html#api_enableaudiovolumeindication) -- [`reportAudioVolumeIndicationOfSpeakers`](https://docs.agora.io/cn/live-streaming-premium-4.x/API%20Reference/ios_ng/API/class_irtcengineeventhandler.html#callback_onaudiovolumeindication) \ No newline at end of file diff --git "a/markdown/online-ktv/03 \350\277\233\351\230\266\345\212\237\350\203\275/03 \350\257\204\345\210\206\345\212\237\350\203\275 (iOS).md" "b/markdown/online-ktv/03 \350\277\233\351\230\266\345\212\237\350\203\275/03 \350\257\204\345\210\206\345\212\237\350\203\275 (iOS).md" deleted file mode 100644 index 9262248d421..00000000000 --- "a/markdown/online-ktv/03 \350\277\233\351\230\266\345\212\237\350\203\275/03 \350\257\204\345\210\206\345\212\237\350\203\275 (iOS).md" +++ /dev/null @@ -1,79 +0,0 @@ -Agora 在线 K 歌房支持歌唱评分功能,即把演唱者和歌曲原唱的音调进行对比,并实时将匹配度以动画和分数的方式呈现在界面上。 - - - -## 前提条件 - -在实现评分功能前,请确保你已在项目中集成 Agora 音频 SDK v4.0.0 Beta,并实现在线 K 歌功能,详见如下教程: - -- [客户端实现(合唱功能 Android)](https://docs.agora.io/cn/online-ktv/chorus_client_android?platform=Android) -- [客户端实现(合唱功能 iOS)](https://docs.agora.io/cn/online-ktv/chorus_client_ios?platform=iOS) - -## 实现方法 - -1. 调用 `enableAudioVolumeIndication` 方法开启歌唱评分功能。示例代码如下: - -```java -// Java -getRtcEngine().enableAudioVolumeIndication(30, 10, true); -``` - -```swift -// Swift -rtc.enableAudioVolumeIndication(20, smooth: 3, reportVad: true) -``` - -2. 成功开启歌唱评分功能后,只要频道内有发流用户,SDK 会在加入频道后按设置的时间间隔触发音量信息提示回调。你需要设置该回调监听,以获取本地用户的人声音调(`voicePitch`)。 - -```java -// Java -@Override -public void onAudioVolumeIndication(AudioVolumeInfo[] speakers, int totalVolume) { - for(AudioVolumeInfo info : speakers){ - if(info.uid == 0 && info.voicePitch > 0){ - mMainThreadDispatch.onLocalPitch(info.voicePitch); - } - } -} -``` - -```swift -// Swift -func rtcEngine(_: AgoraRtcEngineKit, reportAudioVolumeIndicationOfSpeakers speakers: [AgoraRtcAudioVolumeInfo], totalVolume _: Int) { - voicePitchRelay.accept(speakers.map { $0.voicePitch }) -} -``` - -3. 将获取到的 `voicePitch` 传递到歌词组件,示例代码如下: - - 如何使用歌词组件实现评分效果,请参考[歌词组件使用教程 Android 端](https://docs.agora.io/cn/online-ktv/ktv_lrcview_android?platform=Android) 或 [歌词组件使用教程 iOS 端](https://docs.agora.io/cn/online-ktv/ktv_lrcview_android?platform=Android)。 - -```java - -// Java -@Override -public void onLocalPitch(double pitch) { - super.onLocalPitch(pitch); - mBinding.lrcControlView.getPitchView().updateLocalPitch(pitch); -} -``` - -```swift -// Swift -RoomManager.shared().subscribeVoicePitch().subscribe { result in - guard let value = result.element else { return } - self.lrcScoreView.setVoicePitch(value) -}.disposed(by: disposeBag) -``` - -## API 参考 - -### Android - -- [`enableAudioVolumeIndication`](https://docs.agora.io/cn/live-streaming-premium-4.x/API%20Reference/java_ng/API/class_irtcengine.html#api_enableaudiovolumeindication) -- [`onAudioVolumeIndication`](https://docs.agora.io/cn/live-streaming-premium-4.x/API%20Reference/java_ng/API/class_irtcengineeventhandler.html#callback_onaudiovolumeindication) - -### iOS - -- [`enableAudioVolumeIndication`](https://docs.agora.io/cn/live-streaming-premium-4.x/API%20Reference/ios_ng/API/class_irtcengine.html#api_enableaudiovolumeindication) -- [`reportAudioVolumeIndicationOfSpeakers`](https://docs.agora.io/cn/live-streaming-premium-4.x/API%20Reference/ios_ng/API/class_irtcengineeventhandler.html#callback_onaudiovolumeindication) \ No newline at end of file diff --git "a/markdown/online-ktv/04 \345\217\202\350\200\203\346\226\207\346\241\243/01 \351\237\263\344\271\220\346\222\255\346\224\276\345\231\250 API \345\217\202\350\200\203.md" "b/markdown/online-ktv/04 \345\217\202\350\200\203\346\226\207\346\241\243/01 \351\237\263\344\271\220\346\222\255\346\224\276\345\231\250 API \345\217\202\350\200\203.md" deleted file mode 100644 index a7589496d60..00000000000 --- "a/markdown/online-ktv/04 \345\217\202\350\200\203\346\226\207\346\241\243/01 \351\237\263\344\271\220\346\222\255\346\224\276\345\231\250 API \345\217\202\350\200\203.md" +++ /dev/null @@ -1,552 +0,0 @@ -## IAgoraMusicContentCenter - -`IAgoraMusicContentCenter` 接口类,该类实现音乐内容中心的主要功能。 - -### initialize - -初始化 `IAgoraMusicContentCenter` 。 - -```java -public abstract int initialize(AgoraMusicContentCenterConfiguration configuration); -``` - -**注意** - -在调用 `IAgoraMusicContentCenter` 类下的其他方法前,你需要先调用该方法初始化 `IAgoraMusicContentCenter`。 - -**参数** - -- `configuration`:`IAgoraMusicContentCenter` 的配置,详见 `AgoraMusicContentCenterConfiguration`。 - -**返回值** - -- 0: 方法调用成功。 -- < 0: 方法调用失败。 - -### createMusicPlayer - -创建音乐播放器。 - -```java -public abstract IAgoraMusicPlayer createMusicPlayer(); -``` - -如果你需要播放音乐内容中心的音乐资源,你需要先调用该方法来创建一个音乐播放器。 - -**返回值** - -- 方法调用成功:返回 `IAgoraMusicPlayer` 对象。 -- 方法调用失败:返回空指针。 - -### registerEventHandler - -注册音乐内容中心回调事件。 - -```java -public abstract int registerEventHandler(IMusicContentCenterEventHandler eventHandler); -``` - -**参数** - -- `eventHandler`:待注册的回调事件,详见`IMusicContentCenterEventHandler`。 - -**返回值** - -- 0: 方法调用成功。 -- < 0: 方法调用失败。 - -### unregisterEventHandler - -取消注册事件回调。 - -```java -public abstract int unregisterEventHandler(); -``` - -**返回值** - -- 0: 方法调用成功。 -- < 0: 方法调用失败。 - -### preload - -预加载音乐资源。 - -```java -public abstract int preload(long songCode, String jsonOption); -``` - -你可以调用该方法预先加载需要播放的音乐资源。成功调用该方法后,SDK 会触发 `onPreloadEvent` 回调报告预加载音乐资源的事件。 - -在调用该方法来预加载音乐资源之前,你需要调用 `getMusicCollectionByMusicChartId` 或 `searchMusic` 方法来获取你需要播放的音乐资源,并通过由此触发的 `onMusicCollectionResult` 回调获取音乐资源的编号(`songCode`)。 - -**参数** - -- `songCode`:输入参数;音乐资源的编号,用于标识一个音乐资源。 -- `jsonOption`:扩展 json 字段,默认为 `null`。 - -**返回值** - -- 0: 方法调用成功。 -- < 0: 方法调用失败。 - -### isPreloaded - -检测音乐资源是否已被预加载。 - -```java -public abstract int isPreloaded(long songCode); -``` - -该方法为同步调用。调用 `preload` 方法之后你可以调用该方法来检测音乐资源是否已被预加载。 - -**参数** - -- `songCode`:音乐资源的编号,用于标识一个音乐资源。 - -返回值 - -- 0: 该音乐资源已被预加载。 -- < 0: 方法调用失败。 - -### getMusicCharts - -获取曲库中全部的音乐榜单。 - -```java -public abstract String getMusicCharts(); -``` - -当你调用该方法后,SDK 会触发 `onMusicChartsResult` 回调报告音乐榜单的详细信息。 - -**返回值** - -- 一个 `requestId`,为本次请求的唯一标识。 - -### getMusicCollectionByMusicChartId[1/2] - -通过音乐榜单的 ID 获取指定榜单的音乐资源列表。 - -```java -public String getMusicCollectionByMusicChartId(int musicChartId, int page, int pageSize) { -return getMusicCollectionByMusicChartId(musicChartId, page, pageSize, null); -} -``` - -成功调用该方法后,SDK 会触发 `onMusicCollectionResult` 回调报告榜单中音乐资源列表的详细信息。 - -**参数** - -- `musicChartId`:音乐榜单的 ID,可以通过 `getMusicCharts` 方法获取。你也可以通过 RESTful API 来[获取曲库所有歌曲列表](https://docs.agora.io/cn/online-ktv/ktv_song_rest?platform=Android#a-namegeta获取曲库所有歌曲列表)或[增量歌曲列表](https://docs.agora.io/cn/online-ktv/ktv_song_rest?platform=Android#获取增量歌曲列表)。 - -- `page`:想要获取的音乐资源列表的目标页编号。 - -- `pageSize`:每页所展示的音乐资源的最大数量,最大值为 50。 - -**返回值** - -- 一个 `requestId`,为本次请求的唯一标识。 - -### getMusicCollectionByMusicChartId[2/2] - -通过音乐榜单的 ID 获取指定榜单的音乐资源列表。 - -```java -public abstract String getMusicCollectionByMusicChartId( int musicChartId, int page, int pageSize, String jsonOption); -``` - -你可以调用该方法获取指定榜单的音乐资源列表并自定义 json 参数。 - -成功调用该方法后,SDK 会触发 `onMusicCollectionResult` 回调报告榜单中音乐资源列表的详细信息。 - -**参数** - -- `musicChartId`:音乐榜单的 ID,可以通过 `getMusicCharts` 方法获取。你也可以通过 RESTful API 来[获取曲库所有歌曲列表](https://docs.agora.io/cn/online-ktv/ktv_song_rest?platform=Android#a-namegeta获取曲库所有歌曲列表)或[增量歌曲列表](https://docs.agora.io/cn/online-ktv/ktv_song_rest?platform=Android#获取增量歌曲列表)。 - -- `page`:想要获取的音乐资源列表的目标页编号。 - -- `pageSize`:每页所展示的音乐资源的最大数量,最大值为 50。 - -- `jsonOption`:扩展 json 字段,默认为 `null`。 - -**返回值** - -- 一个 `requestId`,为本次请求的唯一标识。 - -### searchMusic [1/2] - -搜索音乐资源。 - -```java -public String searchMusic(String keyword, int page, int pageSize) { -return searchMusic(keyword, page, pageSize, null); -} -``` - -成功调用该方法后,SDK 会触发 `onMusicCollectionResult` 回调,报告检索到的音乐资源列表。 - -**参数** - -- `keyword`:搜索关键词。 - -- `page`:想要获取的音乐资源列表的目标页编号。 - -- `pageSize`:每页所展示的音乐资源的最大数量,最大值为 50。 - -**返回值** - -- 一个 `requestId`,为本次请求的唯一标识。 - -### searchMusic [2/2] - -搜索音乐资源。 - -```java -public abstract String searchMusic(String keyword, int page, int pageSize, String jsonOption); -``` - -你可以调用该方法来通过关键词搜索音乐资源并自定义 json 参数。 - -成功调用该方法后,SDK 会触发 `onMusicCollectionResult` 回调,报告检索到的音乐资源列表。 - -**参数** - -- `keyword`:搜索关键词。 - -- `page`:想要获取的音乐资源列表的目标页编号。 - -- `pageSize`:每页所展示的音乐资源的最大数量。 - -- `jsonOption`:扩展 json 字段,默认为 `null`。 - -**返回值** - -- 一个 `requestId`,为本次请求的唯一标识。 - -### getLyric - -获取音乐资源的歌词下载地址。 - -```java -public abstract String getLyric(long songCode, int lyricType); -``` - -成功调用该方法后,SDK 会触发 `onLyricResult` 回调。 - -**参数** - -- `songCode`:输入参数;音乐资源的编号,用于标识一个音乐资源。 - -- `lyricType`:歌词类型: - - 0:xml 格式。 - - 1:lrc 格式。 - -**返回值** - -- 一个 `requestId`,为本次请求的唯一标识。 - -### release - -释放音乐内容中心所占用的所有资源。 - -```java -protected abstract void release(); -``` - -
    该方法需要在 RtcEnginedestroy 方法前调用。
    - -## IAgoraMusicPlayer - -`IAgoraMusicPlayer` 接口类,提供音乐播放器的相关方法。 - -### open - -打开音乐内容中心的音乐资源。 - -```java -int open(long songCode, long startPos); -``` - -在调用此方法之前,请确保需要播放的音乐资源已加载完成。你可以调用 `isPreloaded` 方法或通过 `onPreLoadEvent` 回调来检测音乐资源是否已被预加载。 - - -
    如果你需要播放音乐资源,请确保收到 onPlayerStateChanged 回调报告状态为 PLAYER_STATE_OPEN_COMPLETED(2) 再调用 play 来进行播放。
    - -**参数** - -- `songCode`:输入参数;音乐资源的编号,用于标识一个音乐资源。 - -- `startPos`:设置起始播放位置(毫秒),默认值为 0。 - -**返回值** - -- 0: 方法调用成功。 -- < 0: 方法调用失败。 - -### destroy - -销毁音乐播放器。 - -```java -int destroy(); -``` - -**返回值** - -- 0: 方法调用成功。 -< 0: 方法调用失败。 - - -## IAgoraMusicContentCenterEventHandler - -`IAgoraMusicContentCenterEventHandler` 接口类,用于 SDK 向客户端发送音乐内容中心事件通知。 - -### onPreLoadEvent - -报告预加载音乐资源的事件。 - -```java -void onPreLoadEvent(long songCode, int percent, int status, String msg, String lyricUrl); -``` - -当你调用 `preload` 方法后,SDK 会触发该回调。 - -**参数** - -- `songCode`:音乐资源的编号,用于标识一个音乐资源。 - -- `percent`:音乐资源当前的加载进度,取值范围为 [0,100]。 - -- `status`:当前音乐资源的加载状态: - - `0`:音乐资源加载完成。 - - `1`:音乐资源加载失败。 - - `2`:音乐资源正在加载中。 - -- `msg`:字符串类型的事件信息。 - -- `lyricUrl`:歌词下载地址。 - -### onMusicCollectionResult - -音乐资源列表回调。 - -```java -void onMusicCollectionResult( String requestId, int status, int page, int pageSize, int total, Music[] list); -``` - -当你调 `getMusicCollectionByMusicChartId` 方法来通过音乐榜单的 ID 获取指定榜单的音乐资源列表,SDK 会触发 `onMusicCollectionResult` 回调报告榜单中音乐资源列表的详细信息。 - -**参数** - -- `requestId`:请求 ID。本次请求的唯一标识。 - -- `status`:获取音乐资源列表的请求状态: - - `0`:请求成功。 - - `1`:请求失败。 - -- `page`:当前页面编号。 - -- `pageSize`:当前音乐资源列表的总页面数量。 - -- `total`:列表内音乐资源的总数量。 - -- `list`:当前页面列表中音乐资源的详细信息,详见 `Music`。 - -### onMusicChartsResult - -音乐榜单回调。 - -```java -void onMusicChartsResult(String requestId, int status, MusicChartInfo[] list); -``` - -当你调用 `getMusicCharts` 方法获取全部音乐榜单之后,SDK 会触发该回调。 - -**参数** - -- `requestId`:请求 ID。本次请求的唯一标识。 - -- `status`:获取音乐榜单的请求状态: - - `0`:请求成功。 - - `1`:请求失败。 - -- `list`:当前可播放的音乐榜单列表,详见 `MusicChartInfo`。 - -### onLyricResult - -歌词下载地址回调。 - -```java - void onLyricResult(String requestId, String lyricUrl); -``` - -当你调用 `getLyric` 获取指定歌曲的歌词下载地址后,SDK 会触发该回调。 - -**参数** - -- `requestId`:请求 ID。本次请求的唯一标识。 - -- `lyricUrl`:歌词的下载地址。 - -## Music - -音乐资源的详细信息。 - -```java -public class Music { - public long songCode; - public String name; - public String singer; - public String poster; - public String releaseTime; - public int type; - public int pitchType; - public int durationS; - public int[] lyricType; - public MvProperty[] mv; - public ClimaxSegment[] climaxSegments; -} -``` - -**songCode** - -音乐资源的编号,用于标识一个音乐资源。 - -**name** - -音乐资源名称。 - -**singer** - -歌手名。 - -**poster** - -音乐资源海报的地址。 - -**releaseTime** - -音乐资源发布的时间。 - -**type** - -音乐资源类型: - -- `1`:左声道伴奏,右声道原唱的单音轨纯音频音源。 -- `2`:只有伴唱的单音轨纯音频音源。 -- `3`:只有原唱的单音轨纯音频音源。 -- `4`:既有多音轨纯音频又有多音轨 MV 资源的音源。 -- `5`:只有多音轨 MV 资源的音源。 -- `6`:既有多音轨纯音频又有多音轨 MV 资源的音源(该音源受数字版权保护)。 - -**pitchTypes** - -歌曲是否支持演唱评分功能: - -- `1`:歌曲支持演唱评分功能。 -- `2`:歌曲不支持演唱评分功能。 - -**durationS** - -音乐资源总时长 (秒)。 - -**lyricTypes** - -支持的歌词类型: - -- `0`:xml 格式。 -- `1`:lrc 格式。 - -**mvProperties** - -需播放的 MV 的属性,详见 `MvProperty`。 - -**climaxSegments** - -音乐高潮片段列表,详见 `ClimaxSegment`。 - -## AgoraMusicContentCenterConfiguration - -音乐内容中心的配置。 - -```java -public class MusicContentCenterConfiguration { -public String appId; -public String rtmToken; -public long mccUid; -public IMusicContentCenterEventHandler eventHandler - } -``` - -**appId** - -已启用内容中心的项目的 App ID。 - -**rtmToken** - -使用音乐内容中心时,用于鉴权的 RTM Token,详见[使用 RTM Token 鉴权](https://docs.agora.io/cn/Real-time-Messaging/token_server_rtm?platform=AllPlatforms)。 - -**mccUid** - -使用音乐内容中心的用户 ID,该 ID 由用户自行设置。 - -**eventHandler** - -待接收的事件回调,详见 `IAgoraMusicContentCenterEventHandler`。 - -## MvProperty - -MV 的属性。 - -```java -public class MvProperty { -public String bandwidth; -public String resolution; -} -``` - -**bandwidth** - -该 MV 的视频带宽。 - -**resolution** - -该 MV 的分辨率。 - -## MusicChartInfo - -音乐榜单的详细信息。 - -```java -public class MusicChartInfo { -public String name; -public int type; -} -``` - -**name** - -榜单名。 - -**type** - -音乐榜单的 ID。 - -## ClimaxSegment - -音乐高潮片段设置。 - -```java -public class ClimaxSegment { -public int startTimeMs; -public int endTimeMs; -} -``` - -**startTimeMs** - -音乐高潮片段的开始时间点,单位毫秒。 - -**endTimeMs** - -音乐高潮片段的结束时间点,单位毫秒。 From c29a134610b9b679c06ab2b7b1f8c15a334a7f97 Mon Sep 17 00:00:00 2001 From: kelzr Date: Fri, 31 Mar 2023 14:57:36 +0800 Subject: [PATCH 13/20] [KTVI] fixed --- ...257\345\214\226 Kotlin API for Android.md" | 3 +- .../\351\233\206\346\210\220.md" | 7 +++- ...57\345\214\226 Objective-C API for iOS.md" | 41 ++++--------------- .../\351\233\206\346\210\220.md" | 7 +++- 4 files changed, 22 insertions(+), 36 deletions(-) diff --git "a/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/Android/K \346\255\214\346\210\277\345\234\272\346\231\257\345\214\226 Kotlin API for Android.md" "b/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/Android/K \346\255\214\346\210\277\345\234\272\346\231\257\345\214\226 Kotlin API for Android.md" index cf2aac70fe7..bcddaa58436 100644 --- "a/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/Android/K \346\255\214\346\210\277\345\234\272\346\231\257\345\214\226 Kotlin API for Android.md" +++ "b/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/Android/K \346\255\214\346\210\277\345\234\272\346\231\257\345\214\226 Kotlin API for Android.md" @@ -1,5 +1,4 @@ -本文提供在线 K 歌房场景定制化 Kotlin API。 - +本文提供在线 K 歌房场景定制化 Kotlin API。你可以在 GitHub 上查看源码 [KTVApi.kt](https://github.com/AgoraIO-Usecase/agora-ent-scenarios/blob/v2.1.1-ktv-Android/Android/scenes/ktv/src/main/java/io/agora/scene/ktv/live/KTVApi.kt) 和 [KTVApiImpl.kt](https://github.com/AgoraIO-Usecase/agora-ent-scenarios/blob/v2.1.1-ktv-Android/Android/scenes/ktv/src/main/java/io/agora/scene/ktv/live/KTVApiImpl.kt)。 ## 方法 diff --git "a/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/Android/\345\256\236\347\216\260\346\226\207\346\241\243/\351\233\206\346\210\220.md" "b/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/Android/\345\256\236\347\216\260\346\226\207\346\241\243/\351\233\206\346\210\220.md" index bd70596595d..4cb61a0faa8 100644 --- "a/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/Android/\345\256\236\347\216\260\346\226\207\346\241\243/\351\233\206\346\210\220.md" +++ "b/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/Android/\345\256\236\347\216\260\346\226\207\346\241\243/\351\233\206\346\210\220.md" @@ -4,7 +4,12 @@ 本文介绍如何使用 K 歌定制化场景 API 实现点歌、独唱、合唱等基础业务功能。 -
    开始前请确保你已参考项目配置集成所需 SDK。
    +## 前提条件 + +实现点歌、独唱、合唱前,请确保你已完成如下步骤: + +1. 参考项目配置集成所需 SDK。 +2. 在工程文件中引入 [KTVApi.kt](https://github.com/AgoraIO-Usecase/agora-ent-scenarios/blob/v2.1.1-ktv-Android/Android/scenes/ktv/src/main/java/io/agora/scene/ktv/live/KTVApi.kt) 和 [KTVApiImpl.kt](https://github.com/AgoraIO-Usecase/agora-ent-scenarios/blob/v2.1.1-ktv-Android/Android/scenes/ktv/src/main/java/io/agora/scene/ktv/live/KTVApiImpl.kt) 文件。 ## 点歌 diff --git "a/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/iOS/K \346\255\214\346\210\277\345\234\272\346\231\257\345\214\226 Objective-C API for iOS.md" "b/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/iOS/K \346\255\214\346\210\277\345\234\272\346\231\257\345\214\226 Objective-C API for iOS.md" index 3d4e1de426f..659774ebe29 100644 --- "a/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/iOS/K \346\255\214\346\210\277\345\234\272\346\231\257\345\214\226 Objective-C API for iOS.md" +++ "b/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/iOS/K \346\255\214\346\210\277\345\234\272\346\231\257\345\214\226 Objective-C API for iOS.md" @@ -1,8 +1,8 @@ -本文提供在线 K 歌房场景定制化 Objective-C API。 +本文提供在线 K 歌房场景定制化 Objective-C API。你可以在 GitHub 上查看源码 [KTVApi.h](https://github.com/AgoraIO-Usecase/agora-ent-scenarios/blob/v2.1.1-ktv-iOS/iOS/AgoraEntScenarios/Scenes/KTV/ViewController/KTV/KTVApi.h) 和 [KTVApi.m](https://github.com/AgoraIO-Usecase/agora-ent-scenarios/blob/v2.1.1-ktv-iOS/iOS/AgoraEntScenarios/Scenes/KTV/ViewController/KTV/KTVApi.m)。 ## 方法 -### initWithRtcEngine +### initWithRtcEngine:channel:musicCenter:player:dataStreamId:delegate: ```objective-c - (id)initWithRtcEngine:(AgoraRtcEngineKit *)engine @@ -11,7 +11,6 @@ player:(nonnull id)rtcMediaPlayer dataStreamId:(NSInteger)streamId delegate:(id)delegate; -) ``` 初始化 KTV API。 @@ -29,32 +28,10 @@ - `musicCenter`: 版权音乐内容中心实例。详见 [AgoraMusicContentCenter](https://docs.agora.io/cn/online-ktv/API%20Reference/ios_ng/API/rtc_interface_class.html#class_imusiccontentcenter)。 - `player`: 音乐播放器实例。详见 [AgoraMusicPlayerProtocol](https://docs.agora.io/cn/online-ktv/API%20Reference/ios_ng/API/rtc_interface_class.html#class_imusicplayer)。 - `dataStreamId`: 数据流(Data Stream)ID。 -- `delegate`: [KTVApiDelegate](#didchangedtostate)。 +- `delegate`: [KTVApiDelegate](#controllersongdidchangedtostatelocal)。 -### release - -```objective-c -fun release() -``` - -释放 KTV API 资源。 - -调用该方法可以清空 KTV API 模块内部变量和缓存数据,取消 `ktvApiEventHandler` 的事件监听,取消网络请求等。 - -#### 用法示例 - -```objective-c -// K 歌房 Activity 销毁时调用 ktvApiProtocol 释放,随后释放创建的实例 -@Override -protected void onDestroy() { - super.onDestroy(); - ktvApiProtocol.release() - // 释放 mRtcEngine、iAgoraMusicContentCenter、mPlayer、streamId -} -``` - -### loadSong +### loadSong:withConfig:withCallback: ```objective-c - (void)loadSong:(NSInteger)songCode @@ -73,7 +50,7 @@ withCallback:(void (^ _Nullable)(NSInteger songCode, NSString* lyricUrl, KTVSing - `block`: 歌词加载状态事件。 -### playSong +### playSong: ```objective-c - (void)playSong:(NSInteger)songCode @@ -112,7 +89,7 @@ withCallback:(void (^ _Nullable)(NSInteger songCode, NSString* lyricUrl, KTVSing 暂停播放歌曲。 -### seek +### seek: ```objective-c - (void)seek:(time: NSInteger); @@ -124,7 +101,7 @@ withCallback:(void (^ _Nullable)(NSInteger songCode, NSString* lyricUrl, KTVSing - `time`: 跳转的时间点。单位为毫秒。 -### selectTrackMode +### selectTrackMode: ```objective-c - (void)selectTrackMode:(KTVPlayerTrackMode)mode; @@ -138,7 +115,7 @@ withCallback:(void (^ _Nullable)(NSInteger songCode, NSString* lyricUrl, KTVSing - `mode`: 音轨的类型。详见 [KTVPlayerTrackMode](#ktvplayertrackmode)。 -### setKaraokeView +### setKaraokeView: ```objective-c @property(nonatomic, weak) KaraokeView* karaokeView; @@ -159,7 +136,7 @@ withCallback:(void (^ _Nullable)(NSInteger songCode, NSString* lyricUrl, KTVSing ## 回调 -### didChangedToState +### controller:song:didChangedToState:local: ```objective-c @protocol KTVApiDelegate diff --git "a/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/iOS/\345\256\236\347\216\260\346\226\207\346\241\243/\351\233\206\346\210\220.md" "b/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/iOS/\345\256\236\347\216\260\346\226\207\346\241\243/\351\233\206\346\210\220.md" index 3155368c358..419cf1046ed 100644 --- "a/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/iOS/\345\256\236\347\216\260\346\226\207\346\241\243/\351\233\206\346\210\220.md" +++ "b/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/iOS/\345\256\236\347\216\260\346\226\207\346\241\243/\351\233\206\346\210\220.md" @@ -4,7 +4,12 @@ 本文介绍如何使用 K 歌定制化场景 API 实现点歌、独唱、合唱等基础业务功能。 -
    开始前请确保你已参考项目配置集成所需 SDK。
    +## 前提条件 + +实现点歌、独唱、合唱前,请确保你已完成如下步骤: + +1. 参考项目配置集成所需 SDK。 +2. 在工程文件中引入 [KTVApi.h](https://github.com/AgoraIO-Usecase/agora-ent-scenarios/blob/v2.1.1-ktv-iOS/iOS/AgoraEntScenarios/Scenes/KTV/ViewController/KTV/KTVApi.h) 和 [KTVApi.m](https://github.com/AgoraIO-Usecase/agora-ent-scenarios/blob/v2.1.1-ktv-iOS/iOS/AgoraEntScenarios/Scenes/KTV/ViewController/KTV/KTVApi.m) 文件。 ## 点歌 From 42f95193bc01dc1d5921146682a0c3677c7f41d5 Mon Sep 17 00:00:00 2001 From: kelzr Date: Fri, 31 Mar 2023 15:10:07 +0800 Subject: [PATCH 14/20] [KTVI] fixed --- ...\234\272\346\231\257\345\214\226 Objective-C API for iOS.md" | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git "a/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/iOS/K \346\255\214\346\210\277\345\234\272\346\231\257\345\214\226 Objective-C API for iOS.md" "b/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/iOS/K \346\255\214\346\210\277\345\234\272\346\231\257\345\214\226 Objective-C API for iOS.md" index 659774ebe29..a46a022e826 100644 --- "a/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/iOS/K \346\255\214\346\210\277\345\234\272\346\231\257\345\214\226 Objective-C API for iOS.md" +++ "b/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/iOS/K \346\255\214\346\210\277\345\234\272\346\231\257\345\214\226 Objective-C API for iOS.md" @@ -23,7 +23,7 @@ #### 参数 -- `engine`: [AgoraRtcEngineKit](https://docs.agora.io/cn/online-ktv/API%20Reference/ios_ng/API/rtc_interface_class.html#class_irtcengine) +- `engine`: [AgoraRtcEngineKit](https://docs.agora.io/cn/online-ktv/API%20Reference/ios_ng/API/rtc_interface_class.html#class_irtcengine)。 - `channel`: 待加入的频道名。 - `musicCenter`: 版权音乐内容中心实例。详见 [AgoraMusicContentCenter](https://docs.agora.io/cn/online-ktv/API%20Reference/ios_ng/API/rtc_interface_class.html#class_imusiccontentcenter)。 - `player`: 音乐播放器实例。详见 [AgoraMusicPlayerProtocol](https://docs.agora.io/cn/online-ktv/API%20Reference/ios_ng/API/rtc_interface_class.html#class_imusicplayer)。 From 2815895984c57f49493730a887b40df96e024d56 Mon Sep 17 00:00:00 2001 From: kelzr Date: Fri, 31 Mar 2023 16:13:46 +0800 Subject: [PATCH 15/20] [KTVI] fixed --- .../\351\233\206\346\210\220.md" | 4 ++-- .../\351\233\206\346\210\220.md" | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git "a/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/Android/\345\256\236\347\216\260\346\226\207\346\241\243/\351\233\206\346\210\220.md" "b/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/Android/\345\256\236\347\216\260\346\226\207\346\241\243/\351\233\206\346\210\220.md" index 4cb61a0faa8..1267c908522 100644 --- "a/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/Android/\345\256\236\347\216\260\346\226\207\346\241\243/\351\233\206\346\210\220.md" +++ "b/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/Android/\345\256\236\347\216\260\346\226\207\346\241\243/\351\233\206\346\210\220.md" @@ -1,8 +1,8 @@ ## 概述 -为降低开发者集成难度,声网为 K 歌房场景提供了定制化场景 API。定制化场景 API 基于场景业务代码逻辑封装了声网音视频 SDK API,让你只需调用一个定制化 API,即可实现通过多个音视频 API 和复杂的代码逻辑才可实现的 K 歌业务功能,例如将合唱和伴唱端进行 NTP 时间同步。 +为降低开发者集成难度,声网为 K 歌房场景提供了定制化场景 API。定制化场景 API 基于场景业务代码逻辑封装了声网音视频 SDK API,让你只需调用一个定制化 API,即可实现通过多个音视频 API 和复杂的代码逻辑才可实现的 K 歌业务功能,例如将合唱和伴唱端进行 NTP 时间同步。声网在 GitHub 上提供 KTV 场景化 API 的源码文件 [KTVApi.kt](https://github.com/AgoraIO-Usecase/agora-ent-scenarios/blob/v2.1.1-ktv-Android/Android/scenes/ktv/src/main/java/io/agora/scene/ktv/live/KTVApi.kt) 和 [KTVApiImpl.kt](https://github.com/AgoraIO-Usecase/agora-ent-scenarios/blob/v2.1.1-ktv-Android/Android/scenes/ktv/src/main/java/io/agora/scene/ktv/live/KTVApiImpl.kt)。 -本文介绍如何使用 K 歌定制化场景 API 实现点歌、独唱、合唱等基础业务功能。 +本文介绍如何使用 KTV 场景化 API 实现点歌、独唱、合唱等基础业务功能。 ## 前提条件 diff --git "a/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/iOS/\345\256\236\347\216\260\346\226\207\346\241\243/\351\233\206\346\210\220.md" "b/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/iOS/\345\256\236\347\216\260\346\226\207\346\241\243/\351\233\206\346\210\220.md" index 419cf1046ed..42a4d3db68e 100644 --- "a/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/iOS/\345\256\236\347\216\260\346\226\207\346\241\243/\351\233\206\346\210\220.md" +++ "b/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/iOS/\345\256\236\347\216\260\346\226\207\346\241\243/\351\233\206\346\210\220.md" @@ -1,8 +1,8 @@ ## 概述 -为降低开发者集成难度,声网为 K 歌房场景提供了定制化场景 API。定制化场景 API 基于场景业务代码逻辑封装了声网音视频 SDK API,让你只需调用一个定制化 API,即可实现通过多个音视频 API 和复杂的代码逻辑才可实现的 K 歌业务功能,例如将合唱和伴唱端进行 NTP 时间同步。 +为降低开发者集成难度,声网为 K 歌房场景提供了定制化场景 API。定制化场景 API 基于场景业务代码逻辑封装了声网音视频 SDK API,让你只需调用一个定制化 API,即可实现通过多个音视频 API 和复杂的代码逻辑才可实现的 K 歌业务功能,例如将合唱和伴唱端进行 NTP 时间同步。声网在 GitHub 上提供 KTV 场景化 API 的源码文件 [KTVApi.h](https://github.com/AgoraIO-Usecase/agora-ent-scenarios/blob/v2.1.1-ktv-iOS/iOS/AgoraEntScenarios/Scenes/KTV/ViewController/KTV/KTVApi.h) 和 [KTVApi.m](https://github.com/AgoraIO-Usecase/agora-ent-scenarios/blob/v2.1.1-ktv-iOS/iOS/AgoraEntScenarios/Scenes/KTV/ViewController/KTV/KTVApi.m)。 -本文介绍如何使用 K 歌定制化场景 API 实现点歌、独唱、合唱等基础业务功能。 +本文介绍如何使用 KTV 场景化 API 实现点歌、独唱、合唱等基础业务功能。 ## 前提条件 From 0e24d52e7bb2e9c5e5d48bcee3590b5562148420 Mon Sep 17 00:00:00 2001 From: kelzr Date: Mon, 3 Apr 2023 17:02:13 +0800 Subject: [PATCH 16/20] [KTVI] update after review [round 1] --- ...\231\257\345\214\226 Kotlin API for Android.md" | 6 +++++- .../\351\233\206\346\210\220.md" | 14 +++++++------- ...231\257\345\214\226 Objective-C API for iOS.md" | 6 +++++- .../\351\233\206\346\210\220.md" | 10 +++++----- 4 files changed, 22 insertions(+), 14 deletions(-) diff --git "a/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/Android/K \346\255\214\346\210\277\345\234\272\346\231\257\345\214\226 Kotlin API for Android.md" "b/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/Android/K \346\255\214\346\210\277\345\234\272\346\231\257\345\214\226 Kotlin API for Android.md" index bcddaa58436..4a238ea7a80 100644 --- "a/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/Android/K \346\255\214\346\210\277\345\234\272\346\231\257\345\214\226 Kotlin API for Android.md" +++ "b/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/Android/K \346\255\214\346\210\277\345\234\272\346\231\257\345\214\226 Kotlin API for Android.md" @@ -73,7 +73,11 @@ fun loadSong( - `songCode`: 歌曲编号。 - `config`: K 歌配置。详见 [KTVSongConfiguration](#ktvsongconfiguration)。 -- `onLoaded`: 歌词加载状态事件。 +- `onLoaded`: 歌词加载状态事件,包含如下参数: + - `songCode`: 歌词编号。 + - `lyricUrl`: 歌词文件的 URL。 + - `role`: 当前用户角色,详见 [KTVSingRole](#ktvsingrole)。 + - `state`: 歌曲加载状态,详见 [KTVLoadSongState](#ktvloadsongstate)。 ### playSong diff --git "a/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/Android/\345\256\236\347\216\260\346\226\207\346\241\243/\351\233\206\346\210\220.md" "b/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/Android/\345\256\236\347\216\260\346\226\207\346\241\243/\351\233\206\346\210\220.md" index 1267c908522..5be0cc548d6 100644 --- "a/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/Android/\345\256\236\347\216\260\346\226\207\346\241\243/\351\233\206\346\210\220.md" +++ "b/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/Android/\345\256\236\347\216\260\346\226\207\346\241\243/\351\233\206\346\210\220.md" @@ -1,6 +1,6 @@ ## 概述 -为降低开发者集成难度,声网为 K 歌房场景提供了定制化场景 API。定制化场景 API 基于场景业务代码逻辑封装了声网音视频 SDK API,让你只需调用一个定制化 API,即可实现通过多个音视频 API 和复杂的代码逻辑才可实现的 K 歌业务功能,例如将合唱和伴唱端进行 NTP 时间同步。声网在 GitHub 上提供 KTV 场景化 API 的源码文件 [KTVApi.kt](https://github.com/AgoraIO-Usecase/agora-ent-scenarios/blob/v2.1.1-ktv-Android/Android/scenes/ktv/src/main/java/io/agora/scene/ktv/live/KTVApi.kt) 和 [KTVApiImpl.kt](https://github.com/AgoraIO-Usecase/agora-ent-scenarios/blob/v2.1.1-ktv-Android/Android/scenes/ktv/src/main/java/io/agora/scene/ktv/live/KTVApiImpl.kt)。 +为降低开发者集成难度,声网为 K 歌房场景提供了定制化场景 API。场景化 API 基于场景业务代码逻辑封装了声网音视频 SDK API,让你只需调用一个场景化 API,即可实现通过多个音视频 API 和复杂的代码逻辑才可实现的 K 歌业务功能,例如将合唱和伴唱端进行 NTP 时间同步。声网在 GitHub 上提供 KTV 场景化 API 的源码文件 [KTVApi.kt](https://github.com/AgoraIO-Usecase/agora-ent-scenarios/blob/v2.1.1-ktv-Android/Android/scenes/ktv/src/main/java/io/agora/scene/ktv/live/KTVApi.kt) 和 [KTVApiImpl.kt](https://github.com/AgoraIO-Usecase/agora-ent-scenarios/blob/v2.1.1-ktv-Android/Android/scenes/ktv/src/main/java/io/agora/scene/ktv/live/KTVApiImpl.kt)。 本文介绍如何使用 KTV 场景化 API 实现点歌、独唱、合唱等基础业务功能。 @@ -13,7 +13,7 @@ ## 点歌 -本节介绍如何实现点歌功能。用户需要在唱歌前进行点歌。点歌指用户通过浏览榜单或搜索关键词选定想唱的正版音乐,然后下载播放音乐。 +本节介绍如何实现点歌功能。点歌指用户通过浏览榜单或搜索关键词选定想唱的正版音乐,然后下载播放音乐。用户需要在唱歌前进行点歌。 ### 方案介绍 @@ -23,7 +23,7 @@ ### 1. 初始化 KTV API 模块 -实例化 `rtcEngine`、`musicCenter`、`mediaPlayer`、`streamId` 实例,并将它们通过 `initWithRtcEngine` 方法传入 KTV API 模块。调用 KTV API 模块的 API 前,请确保已调用 `initWithRtcEngine` 初始化 KTV API 实例。 +实例化 `rtcEngine`、`musicCenter`、`musicPlayer`、`streamId` 实例,并将它们通过 `initWithRtcEngine` 方法传入 KTV API 模块。调用 KTV API 模块的 API 前,请确保已调用 `initWithRtcEngine` 初始化 KTV API 实例。
    考虑到数据流的消息通道有频率限制,为了确保 KTV 模块和其他模块不会相互影响,声网建议你在不同模块中为数据流创建的 streamId 都不同。例如,如果你在其他模块中也使用 sendStreamMessage,请确保两个模块中创建的 streamId 不同。同时,每个用户在每个频道中最多只能创建 5 个数据流,请不要超出上限。
    @@ -52,10 +52,10 @@ iAgoraMusicContentCenter.initialize(contentCenterConfiguration) ``` -3. 调用 [`createMusicPlayer`](https://docs.agora.io/cn/online-ktv/API%20Reference/java_ng/API/toc_mediaplayer.html#api_irtcengine_createmediaplayer) 创建 Media Player。 +3. 调用 [`createMusicPlayer`](https://docs.agora.io/cn/online-ktv/API%20Reference/java_ng/API/toc_drm.html?platform=Android#api_imusiccontentcenter_createmusicplayer) 创建音乐播放器。 ```Kotlin - // 创建 media player + // 创建音乐播放器 val mPlayer = iAgoraMusicContentCenter.createMusicPlayer() ``` @@ -120,7 +120,7 @@ override fun onMusicChartsResult( ### 3. 加载歌曲 -调用 `loadSong` 加载歌曲。该方法中你需要参入编曲编号和 K 歌配置,例如当前的 K 歌唱的场景(合唱或伴唱)、用户角色、主唱伴唱的 UID。K 歌配置会决定 K 歌时歌曲的播放情况、各端用户的收发流情况等。歌曲加载结果会异步地通过 `onLoaded` 回调通知你。 +调用 `loadSong` 加载歌曲。该方法中你需要传入歌曲编号和 K 歌配置,例如当前的 K 歌场景(独唱或合唱)、用户角色、主唱伴唱的 UID。K 歌配置会决定 K 歌时歌曲的播放情况、各端用户的收发流情况等。歌曲加载结果会异步地通过 `onLoaded` 回调通知你。 ```Kotlin // isChorus: 歌曲是否合唱 @@ -549,7 +549,7 @@ ktvApiProtocol.stopSong() 通过 [`updateChannelMediaOptions`](https://docs.agora.io/cn/online-ktv/API%20Reference/java_ng/API/toc_core_method.html#api_irtcengine_updatechannelmediaoptions) 方法在听众加入频道后更新频道媒体选项,例如是否开启本地音频采集,是否发布本地音频流等。 -听众的用户角色为 AgoraClientRoleAudience,因此无法在频道内发布音频流。如果听众想上麦与主唱语聊,需要将用户角色修改为 AgoraClientRoleBroadcaster。修改角色后,SDK 默认发布该连麦听众的音频流,主唱和其他听众都能听到连麦听众的声音。 +听众的用户角色为 `CLIENT_ROLE_AUDIENCE`,因此无法在频道内发布音频流。如果听众想上麦与主唱语聊,需要将用户角色修改为 `CLIENT_ROLE_BROADCASTER`。修改角色后,SDK 默认发布该连麦听众的音频流,主唱和其他听众都能听到连麦听众的声音。 ```Kotlin // 针对需要上麦聊天的听众更新媒体选项 diff --git "a/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/iOS/K \346\255\214\346\210\277\345\234\272\346\231\257\345\214\226 Objective-C API for iOS.md" "b/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/iOS/K \346\255\214\346\210\277\345\234\272\346\231\257\345\214\226 Objective-C API for iOS.md" index a46a022e826..a8929ac3845 100644 --- "a/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/iOS/K \346\255\214\346\210\277\345\234\272\346\231\257\345\214\226 Objective-C API for iOS.md" +++ "b/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/iOS/K \346\255\214\346\210\277\345\234\272\346\231\257\345\214\226 Objective-C API for iOS.md" @@ -47,7 +47,11 @@ withCallback:(void (^ _Nullable)(NSInteger songCode, NSString* lyricUrl, KTVSing - `songCode`: 歌曲编号。 - `config`: K 歌配置。详见 [KTVSongConfiguration](#ktvsongconfiguration)。 -- `block`: 歌词加载状态事件。 +- `block`: 歌词加载状态事件,包含如下参数: + - `songCode`: 歌词编号。 + - `lyricUrl`: 歌词文件的 URL。 + - `role`: 当前用户角色,详见 [KTVSingRole](#ktvsingrole)。 + - `state`: 歌曲加载状态,详见 [KTVLoadSongState](#ktvloadsongstate)。 ### playSong: diff --git "a/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/iOS/\345\256\236\347\216\260\346\226\207\346\241\243/\351\233\206\346\210\220.md" "b/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/iOS/\345\256\236\347\216\260\346\226\207\346\241\243/\351\233\206\346\210\220.md" index 42a4d3db68e..74d5b24698e 100644 --- "a/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/iOS/\345\256\236\347\216\260\346\226\207\346\241\243/\351\233\206\346\210\220.md" +++ "b/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/iOS/\345\256\236\347\216\260\346\226\207\346\241\243/\351\233\206\346\210\220.md" @@ -13,7 +13,7 @@ ## 点歌 -本节介绍如何实现点歌功能。用户需要在唱歌前进行点歌。点歌指用户通过浏览榜单或搜索关键词选定想唱的正版音乐,然后下载播放音乐。 +本节介绍如何实现点歌功能。点歌指用户通过浏览榜单或搜索关键词选定想唱的正版音乐,然后下载播放音乐。用户需要在唱歌前进行点歌。 ### 方案介绍 @@ -23,7 +23,7 @@ ### 1. 初始化 KTV API 模块 -实例化 `rtcEngine`、`musicCenter`、`mediaPlayer`、`streamId` 实例,并将它们通过 `initWithRtcEngine` 方法传入 KTV API 模块。调用 KTV API 模块的 API 前,请确保已调用 `initWithRtcEngine` 初始化 KTV API 实例。 +实例化 `rtcEngine`、`musicCenter`、`musicPlayer`、`streamId` 实例,并将它们通过 `initWithRtcEngine` 方法传入 KTV API 模块。调用 KTV API 模块的 API 前,请确保已调用 `initWithRtcEngine` 初始化 KTV API 实例。
    考虑到数据流的消息通道有频率限制,为了确保 KTV 模块和其他模块不会相互影响,声网建议你在不同模块中为数据流创建的 streamId 都不同。例如,如果你在其他模块中也使用 sendStreamMessage,请确保两个模块中创建的 streamId 不同。同时,每个用户在每个频道中最多只能创建 5 个数据流,请不要超出上限。
    @@ -53,10 +53,10 @@ [self.AgoraMcc enableMainQueueDispatch:YES]; ``` -3. 调用 [`createMusicPlayerWithDelegate`](https://docs.agora.io/cn/online-ktv/API%20Reference/ios_ng/API/toc_drm.html#api_imusiccontentcenter_createmusicplayer) 创建 Media Player +3. 调用 [`createMusicPlayerWithDelegate`](https://docs.agora.io/cn/online-ktv/API%20Reference/ios_ng/API/toc_drm.html#api_imusiccontentcenter_createmusicplayer) 创建音乐播放器。 ```objective-c - // 创建 media player + // 创建音乐播放器 self.rtcMediaPlayer = [self.AgoraMcc createMusicPlayerWithDelegate:]; ``` @@ -134,7 +134,7 @@ ### 3. 加载歌曲 -调用 `loadSong` 加载歌曲。该方法中你需要参入编曲编号和 K 歌配置,例如当前的 K 歌唱的场景(合唱或伴唱)、用户角色、主唱伴唱的 UID。K 歌配置会决定 K 歌时歌曲的播放情况、各端用户的收发流情况等。歌曲加载结果会异步地通过 `withCallback` 回调通知你。 +调用 `loadSong` 加载歌曲。该方法中你需要传入歌曲编号和 K 歌配置,例如当前的 K 歌场景(独唱或合唱)、用户角色、主唱伴唱的 UID。K 歌配置会决定 K 歌时歌曲的播放情况、各端用户的收发流情况等。歌曲加载结果会异步地通过 `withCallback` 回调通知你。 ```objective-c From fd2fc8eb81e8b83db4f3c0bd67f23ca32e82e163 Mon Sep 17 00:00:00 2001 From: kelzr Date: Fri, 14 Apr 2023 15:32:46 +0800 Subject: [PATCH 17/20] [KTVI] update after review [round 2] --- ...257\345\214\226 Kotlin API for Android.md" | 8 +-- .../\351\233\206\346\210\220.md" | 19 ++++--- ...57\345\214\226 Objective-C API for iOS.md" | 20 ++++--- .../\351\233\206\346\210\220.md" | 56 +++++++++---------- 4 files changed, 52 insertions(+), 51 deletions(-) diff --git "a/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/Android/K \346\255\214\346\210\277\345\234\272\346\231\257\345\214\226 Kotlin API for Android.md" "b/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/Android/K \346\255\214\346\210\277\345\234\272\346\231\257\345\214\226 Kotlin API for Android.md" index 4a238ea7a80..bb19be8860e 100644 --- "a/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/Android/K \346\255\214\346\210\277\345\234\272\346\231\257\345\214\226 Kotlin API for Android.md" +++ "b/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/Android/K \346\255\214\346\210\277\345\234\272\346\231\257\345\214\226 Kotlin API for Android.md" @@ -1,4 +1,4 @@ -本文提供在线 K 歌房场景定制化 Kotlin API。你可以在 GitHub 上查看源码 [KTVApi.kt](https://github.com/AgoraIO-Usecase/agora-ent-scenarios/blob/v2.1.1-ktv-Android/Android/scenes/ktv/src/main/java/io/agora/scene/ktv/live/KTVApi.kt) 和 [KTVApiImpl.kt](https://github.com/AgoraIO-Usecase/agora-ent-scenarios/blob/v2.1.1-ktv-Android/Android/scenes/ktv/src/main/java/io/agora/scene/ktv/live/KTVApiImpl.kt)。 +本文提供在线 K 歌房场景定制化 Kotlin API。你可以在 GitHub 上查看源码文件 [KTVApi.kt](https://github.com/AgoraIO-Usecase/agora-ent-scenarios/blob/v2.1.1-ktv-Android/Android/scenes/ktv/src/main/java/io/agora/scene/ktv/live/KTVApi.kt) 和 [KTVApiImpl.kt](https://github.com/AgoraIO-Usecase/agora-ent-scenarios/blob/v2.1.1-ktv-Android/Android/scenes/ktv/src/main/java/io/agora/scene/ktv/live/KTVApiImpl.kt)。 ## 方法 @@ -74,7 +74,7 @@ fun loadSong( - `songCode`: 歌曲编号。 - `config`: K 歌配置。详见 [KTVSongConfiguration](#ktvsongconfiguration)。 - `onLoaded`: 歌词加载状态事件,包含如下参数: - - `songCode`: 歌词编号。 + - `songCode`: 歌曲编号。 - `lyricUrl`: 歌词文件的 URL。 - `role`: 当前用户角色,详见 [KTVSingRole](#ktvsingrole)。 - `state`: 歌曲加载状态,详见 [KTVLoadSongState](#ktvloadsongstate)。 @@ -88,7 +88,7 @@ fun playSong(songCode: Long) 播放歌曲。 -建议在调用 `loadSong` 函数成功回调 `KTVLoadSongState.KTVLoadSongStateOK` 后再调用 `playSong`。 +建议在调用 `loadSong` 函数并收到 `onLoaded` 回调的 `KTVLoadSongState.KTVLoadSongStateOK` 状态后再调用 `playSong`。 #### 参数 @@ -137,7 +137,7 @@ fun seek(time: Long) fun selectTrackMode(mode: KTVPlayerTrackMode) ``` -选择歌曲的音轨。 +选择播放的音轨。 歌曲的音轨包含原唱和伴奏。调用该方法可以选择播放的音轨。 diff --git "a/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/Android/\345\256\236\347\216\260\346\226\207\346\241\243/\351\233\206\346\210\220.md" "b/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/Android/\345\256\236\347\216\260\346\226\207\346\241\243/\351\233\206\346\210\220.md" index 5be0cc548d6..75d0977d579 100644 --- "a/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/Android/\345\256\236\347\216\260\346\226\207\346\241\243/\351\233\206\346\210\220.md" +++ "b/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/Android/\345\256\236\347\216\260\346\226\207\346\241\243/\351\233\206\346\210\220.md" @@ -1,6 +1,6 @@ ## 概述 -为降低开发者集成难度,声网为 K 歌房场景提供了定制化场景 API。场景化 API 基于场景业务代码逻辑封装了声网音视频 SDK API,让你只需调用一个场景化 API,即可实现通过多个音视频 API 和复杂的代码逻辑才可实现的 K 歌业务功能,例如将合唱和伴唱端进行 NTP 时间同步。声网在 GitHub 上提供 KTV 场景化 API 的源码文件 [KTVApi.kt](https://github.com/AgoraIO-Usecase/agora-ent-scenarios/blob/v2.1.1-ktv-Android/Android/scenes/ktv/src/main/java/io/agora/scene/ktv/live/KTVApi.kt) 和 [KTVApiImpl.kt](https://github.com/AgoraIO-Usecase/agora-ent-scenarios/blob/v2.1.1-ktv-Android/Android/scenes/ktv/src/main/java/io/agora/scene/ktv/live/KTVApiImpl.kt)。 +为降低开发者的集成难度,声网为 K 歌房场景提供了场景化 API。场景化 API 封装了声网音视频 SDK 的 API,并提供了 K 歌业务常见的功能,例如,对主唱和伴唱进行 NTP 时间同步。你只需要调用一个场景化 API 即可实现通过多个音视频 SDK 的 API 完成的复杂代码逻辑,从而更轻松实现 K 歌场景。声网在 GitHub 上提供 KTV 场景化 API 的源码文件 [KTVApi.kt](https://github.com/AgoraIO-Usecase/agora-ent-scenarios/blob/v2.1.1-ktv-Android/Android/scenes/ktv/src/main/java/io/agora/scene/ktv/live/KTVApi.kt) 和 [KTVApiImpl.kt](https://github.com/AgoraIO-Usecase/agora-ent-scenarios/blob/v2.1.1-ktv-Android/Android/scenes/ktv/src/main/java/io/agora/scene/ktv/live/KTVApiImpl.kt)。 本文介绍如何使用 KTV 场景化 API 实现点歌、独唱、合唱等基础业务功能。 @@ -25,7 +25,6 @@ 实例化 `rtcEngine`、`musicCenter`、`musicPlayer`、`streamId` 实例,并将它们通过 `initWithRtcEngine` 方法传入 KTV API 模块。调用 KTV API 模块的 API 前,请确保已调用 `initWithRtcEngine` 初始化 KTV API 实例。 -
    考虑到数据流的消息通道有频率限制,为了确保 KTV 模块和其他模块不会相互影响,声网建议你在不同模块中为数据流创建的 streamId 都不同。例如,如果你在其他模块中也使用 sendStreamMessage,请确保两个模块中创建的 streamId 不同。同时,每个用户在每个频道中最多只能创建 5 个数据流,请不要超出上限。
    1. 调用 [`create`](https://docs.agora.io/cn/online-ktv/API%20Reference/java_ng/API/toc_core_method.html#api_irtcengine_initialize) 初始化 `RtcEngine`。 @@ -69,6 +68,8 @@ val streamId = mRtcEngine.createDataStream(cfg) ``` +
    考虑到数据流的消息通道有频率限制,为了确保 KTV 模块和其他模块不会相互影响,声网建议你在不同模块中为数据流创建的 streamId 都不同。例如,如果你在其他模块中也使用 sendStreamMessage 发送数据流,请确保两个模块中数据流的 streamId 不同。此外,每个用户在每个频道中最多只能创建 5 个数据流,请不要超出上限。
    + 5. 调用 `initWithRtcEngine` 初始化 KTV API 实例。 ```Kotlin @@ -327,7 +328,7 @@ ktvApiProtocol.stopSong() 听众的用户角色为 AgoraClientRoleAudience,因此无法在频道内发布音频流。如果听众想上麦与主唱语聊,需要将用户角色修改为 AgoraClientRoleBroadcaster。修改角色后,SDK 默认发布该连麦听众的音频流,主唱和其他听众都能听到连麦听众的声音。 ```Kotlin -// 针对需要上麦聊天的听众更新媒体选项 +// 对需要上麦聊天的听众更新媒体选项 val channelMediaOption = ChannelMediaOptions() // 发布本地麦克风流 channelMediaOption.publishMicrophoneTrack = true @@ -339,7 +340,7 @@ channelMediaOption.clientRoleType = CLIENT_ROLE_BROADCASTER mRtcEngine.updateChannelMediaOptions(channelMediaOption) -// 针对未上麦的听众更新媒体选项 +// 对未上麦的听众更新媒体选项 val channelMediaOption = ChannelMediaOptions() // 不发布本地麦克风流 channelMediaOption.publishMicrophoneTrack = false @@ -361,7 +362,7 @@ mRtcEngine.updateChannelMediaOptions(channelMediaOption) - 主唱:加入频道,加载并播放歌曲,发布麦克风采集的音频流。KTV API 模块内部控制音乐播放器播放音乐,发布音乐到远端,将音乐播放进度同步到远端,让歌词组件进入歌词滚动状态等逻辑。 - 伴唱:加入频道,加载并播放歌曲,发布麦克风采集的音频流。KTV API 模块内部控制音乐播放器播放音乐,同步主唱的音乐播放进度,让歌词组件进入歌词滚动状态等逻辑。 -- 听众:加入频道,加载歌曲。KTV API 模块内部控制听众订阅主唱的人声和音乐的音频合流,同步主唱的音乐播放进度,让歌词组件进入歌词滚动状态等逻辑。如果普通观众需要上麦聊天,可以更新媒体选项。 +- 听众:加入频道,加载歌曲。KTV API 模块内部控制听众订阅主唱的人声和音乐的音频合流,订阅伴唱的人声,同步主唱的音乐播放进度,让歌词组件进入歌词滚动状态等逻辑。如果普通观众需要上麦聊天,可以更新媒体选项。 ![](https://web-cdn.agora.io/docs-files/1678784685024) @@ -524,7 +525,7 @@ mRtcEngine.joinChannel( 调用 `loadSong` 加载歌曲。加载结果会异步地通过 `onLoaded` 回调通知你。收到 `onLoaded(KTVLoadSongStateOK)` 回调状态后,调用 `playSong`。 -听众加入频道后,默认订阅主唱发布的音频合流,即主唱人声和音乐混合的音频流。听众只需调用 `playSong` 进入歌曲播放状态即可。 +听众加入频道后,默认订阅主唱人声和音乐混合的音频流,默认订阅伴唱人声。听众只需调用 `playSong` 进入歌曲播放状态即可。 ```Kotlin ktvApiProtocol.loadSong( @@ -549,10 +550,10 @@ ktvApiProtocol.stopSong() 通过 [`updateChannelMediaOptions`](https://docs.agora.io/cn/online-ktv/API%20Reference/java_ng/API/toc_core_method.html#api_irtcengine_updatechannelmediaoptions) 方法在听众加入频道后更新频道媒体选项,例如是否开启本地音频采集,是否发布本地音频流等。 -听众的用户角色为 `CLIENT_ROLE_AUDIENCE`,因此无法在频道内发布音频流。如果听众想上麦与主唱语聊,需要将用户角色修改为 `CLIENT_ROLE_BROADCASTER`。修改角色后,SDK 默认发布该连麦听众的音频流,主唱和其他听众都能听到连麦听众的声音。 +听众的用户角色为 `CLIENT_ROLE_AUDIENCE`,因此无法在频道内发布音频流。如果听众想上麦与主唱/伴唱语聊,需要将用户角色修改为 `CLIENT_ROLE_BROADCASTER`。修改角色后,SDK 默认发布该连麦听众的音频流,主唱、伴唱、其他听众都能听到连麦听众的声音。 ```Kotlin -// 针对需要上麦聊天的听众更新媒体选项 +// 对需要上麦聊天的听众更新媒体选项 val channelMediaOption = ChannelMediaOptions() // 发布本地麦克风流 channelMediaOption.publishMicrophoneTrack = true @@ -564,7 +565,7 @@ channelMediaOption.clientRoleType = CLIENT_ROLE_BROADCASTER mRtcEngine.updateChannelMediaOptions(channelMediaOption) -// 针对未上麦的听众更新媒体选项 +// 对未上麦的听众更新媒体选项 val channelMediaOption = ChannelMediaOptions() // 不发布本地麦克风流 channelMediaOption.publishMicrophoneTrack = false diff --git "a/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/iOS/K \346\255\214\346\210\277\345\234\272\346\231\257\345\214\226 Objective-C API for iOS.md" "b/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/iOS/K \346\255\214\346\210\277\345\234\272\346\231\257\345\214\226 Objective-C API for iOS.md" index a8929ac3845..2204013aace 100644 --- "a/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/iOS/K \346\255\214\346\210\277\345\234\272\346\231\257\345\214\226 Objective-C API for iOS.md" +++ "b/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/iOS/K \346\255\214\346\210\277\345\234\272\346\231\257\345\214\226 Objective-C API for iOS.md" @@ -1,4 +1,4 @@ -本文提供在线 K 歌房场景定制化 Objective-C API。你可以在 GitHub 上查看源码 [KTVApi.h](https://github.com/AgoraIO-Usecase/agora-ent-scenarios/blob/v2.1.1-ktv-iOS/iOS/AgoraEntScenarios/Scenes/KTV/ViewController/KTV/KTVApi.h) 和 [KTVApi.m](https://github.com/AgoraIO-Usecase/agora-ent-scenarios/blob/v2.1.1-ktv-iOS/iOS/AgoraEntScenarios/Scenes/KTV/ViewController/KTV/KTVApi.m)。 +本文提供在线 K 歌房场景定制化 Objective-C API。你可以在 GitHub 上查看源码文件 [KTVApi.h](https://github.com/AgoraIO-Usecase/agora-ent-scenarios/blob/v2.1.1-ktv-iOS/iOS/AgoraEntScenarios/Scenes/KTV/ViewController/KTV/KTVApi.h) 和 [KTVApi.m](https://github.com/AgoraIO-Usecase/agora-ent-scenarios/blob/v2.1.1-ktv-iOS/iOS/AgoraEntScenarios/Scenes/KTV/ViewController/KTV/KTVApi.m)。 ## 方法 @@ -13,13 +13,13 @@ delegate:(id)delegate; ``` -初始化 KTV API。 +初始化 KTVApi 模块。 调用该方法可以初始化 KTV API 模块内部变量和缓存数据,并注册相应的回调监听。 #### 注意事项 -调用其他 KTV API 之前,你需要先调用本方法初始化。 +调用其他 KTV API 之前,你需要先调用本方法完成初始化。 #### 参数 @@ -30,6 +30,10 @@ - `dataStreamId`: 数据流(Data Stream)ID。 - `delegate`: [KTVApiDelegate](#controllersongdidchangedtostatelocal)。 +#### 返回值 + +KTVApi 实例。 + ### loadSong:withConfig:withCallback: @@ -41,14 +45,14 @@ withCallback:(void (^ _Nullable)(NSInteger songCode, NSString* lyricUrl, KTVSing 加载歌曲。 -传入歌曲编号和 K 歌配置,调用 `loadSong` 加载歌曲。加载结果会异步地通过 `onLoaded` 回调通知你。 +传入歌曲编号和 K 歌配置,调用 `loadSong` 加载歌曲。加载结果会异步地通过 `block` 回调通知你。 #### 参数 - `songCode`: 歌曲编号。 - `config`: K 歌配置。详见 [KTVSongConfiguration](#ktvsongconfiguration)。 - `block`: 歌词加载状态事件,包含如下参数: - - `songCode`: 歌词编号。 + - `songCode`: 歌曲编号。 - `lyricUrl`: 歌词文件的 URL。 - `role`: 当前用户角色,详见 [KTVSingRole](#ktvsingrole)。 - `state`: 歌曲加载状态,详见 [KTVLoadSongState](#ktvloadsongstate)。 @@ -62,7 +66,7 @@ withCallback:(void (^ _Nullable)(NSInteger songCode, NSString* lyricUrl, KTVSing 播放歌曲。 -建议在调用 `loadSong` 函数成功回调 `KTVLoadSongStateOK` 状态后再调用 `playSong`。 +建议在调用 `loadSong` 函数并收到 `block` 回调的 `KTVLoadSongStateOK` 状态后再调用 `playSong`。 #### 参数 @@ -103,7 +107,7 @@ withCallback:(void (^ _Nullable)(NSInteger songCode, NSString* lyricUrl, KTVSing #### 参数 -- `time`: 跳转的时间点。单位为毫秒。 +- `time`: 跳转的时间点。单位为毫秒。取值不得超过歌曲总时长。 ### selectTrackMode: @@ -111,7 +115,7 @@ withCallback:(void (^ _Nullable)(NSInteger songCode, NSString* lyricUrl, KTVSing - (void)selectTrackMode:(KTVPlayerTrackMode)mode; ``` -选择歌曲的音轨。 +选择播放的音轨。 歌曲的音轨包含原唱和伴奏。调用该方法可以选择播放的音轨。 diff --git "a/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/iOS/\345\256\236\347\216\260\346\226\207\346\241\243/\351\233\206\346\210\220.md" "b/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/iOS/\345\256\236\347\216\260\346\226\207\346\241\243/\351\233\206\346\210\220.md" index 74d5b24698e..df0efd5bd70 100644 --- "a/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/iOS/\345\256\236\347\216\260\346\226\207\346\241\243/\351\233\206\346\210\220.md" +++ "b/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/iOS/\345\256\236\347\216\260\346\226\207\346\241\243/\351\233\206\346\210\220.md" @@ -1,6 +1,6 @@ ## 概述 -为降低开发者集成难度,声网为 K 歌房场景提供了定制化场景 API。定制化场景 API 基于场景业务代码逻辑封装了声网音视频 SDK API,让你只需调用一个定制化 API,即可实现通过多个音视频 API 和复杂的代码逻辑才可实现的 K 歌业务功能,例如将合唱和伴唱端进行 NTP 时间同步。声网在 GitHub 上提供 KTV 场景化 API 的源码文件 [KTVApi.h](https://github.com/AgoraIO-Usecase/agora-ent-scenarios/blob/v2.1.1-ktv-iOS/iOS/AgoraEntScenarios/Scenes/KTV/ViewController/KTV/KTVApi.h) 和 [KTVApi.m](https://github.com/AgoraIO-Usecase/agora-ent-scenarios/blob/v2.1.1-ktv-iOS/iOS/AgoraEntScenarios/Scenes/KTV/ViewController/KTV/KTVApi.m)。 +为降低开发者的集成难度,声网为 K 歌房场景提供了场景化 API。场景化 API 封装了声网音视频 SDK 的 API,并提供了 K 歌业务常见的功能,例如,对主唱和伴唱进行 NTP 时间同步。你只需要调用一个场景化 API 即可实现通过多个音视频 SDK 的 API 完成的复杂代码逻辑,从而更轻松实现 K 歌场景。声网在 GitHub 上提供 KTV 场景化 API 的源码文件 [KTVApi.h](https://github.com/AgoraIO-Usecase/agora-ent-scenarios/blob/v2.1.1-ktv-iOS/iOS/AgoraEntScenarios/Scenes/KTV/ViewController/KTV/KTVApi.h) 和 [KTVApi.m](https://github.com/AgoraIO-Usecase/agora-ent-scenarios/blob/v2.1.1-ktv-iOS/iOS/AgoraEntScenarios/Scenes/KTV/ViewController/KTV/KTVApi.m)。 本文介绍如何使用 KTV 场景化 API 实现点歌、独唱、合唱等基础业务功能。 @@ -25,8 +25,6 @@ 实例化 `rtcEngine`、`musicCenter`、`musicPlayer`、`streamId` 实例,并将它们通过 `initWithRtcEngine` 方法传入 KTV API 模块。调用 KTV API 模块的 API 前,请确保已调用 `initWithRtcEngine` 初始化 KTV API 实例。 -
    考虑到数据流的消息通道有频率限制,为了确保 KTV 模块和其他模块不会相互影响,声网建议你在不同模块中为数据流创建的 streamId 都不同。例如,如果你在其他模块中也使用 sendStreamMessage,请确保两个模块中创建的 streamId 不同。同时,每个用户在每个频道中最多只能创建 5 个数据流,请不要超出上限。
    - 1. 调用 `sharedEngineWithAppId` 初始化 `AgoraRtcEngineKit`。 ```objective-c @@ -49,7 +47,8 @@ contentCenterConfiguration.token = ""; // self.AgoraMcc 是定义的 AgoraMusicContentCenter 的全局变量 self.AgoraMcc = [AgoraMusicContentCenter sharedContentCenterWithConfig:contentCenterConfiguration]; - [self.AgoraMcc registerEventDelegate:]; + // 注册音乐内容中心回调 + [self.AgoraMcc registerEventDelegate:]; [self.AgoraMcc enableMainQueueDispatch:YES]; ``` @@ -72,6 +71,8 @@ config:config]; ``` +
    考虑到数据流的消息通道有频率限制,为了确保 KTV 模块和其他模块不会相互影响,声网建议你在不同模块中为数据流创建的 streamId 都不同。例如,如果你在其他模块中也使用 sendStreamMessage 发送数据流,请确保两个模块中数据流的 streamId 不同。此外,每个用户在每个频道中最多只能创建 5 个数据流,请不要超出上限。
    + 5. 调用 `initWithRtcEngine` 初始化 KTV API 实例 ```objective-c @@ -134,12 +135,12 @@ ### 3. 加载歌曲 -调用 `loadSong` 加载歌曲。该方法中你需要传入歌曲编号和 K 歌配置,例如当前的 K 歌场景(独唱或合唱)、用户角色、主唱伴唱的 UID。K 歌配置会决定 K 歌时歌曲的播放情况、各端用户的收发流情况等。歌曲加载结果会异步地通过 `withCallback` 回调通知你。 +调用 `loadSong` 加载歌曲。该方法中你需要传入歌曲编号和 K 歌配置,例如当前的 K 歌场景(独唱或合唱)、用户角色、主唱伴唱的 UID。K 歌配置会决定 K 歌时歌曲的播放情况、各端用户的收发流情况等。歌曲加载结果会异步地通过 `block` 回调通知你。 ```objective-c // KTVSongType: 歌唱类型(合唱或独唱) -// KTVSingRole: 用户角色 +// KTVSingRole: K 歌用户角色 // songCode: 歌曲的编号 // mainSingerUid: 主唱 UID // coSingerUid: 伴唱 UID。如果是独唱场景,不存在伴唱角色,那么传入 0 即可。 @@ -157,7 +158,7 @@ config.coSingerUid = ; // 歌曲加载成功的操作 ... } else if(state == KTVLoadSongStateNoLyricUrl) { - // 歌曲加载失败,重试三次 + // 歌曲加载失败的操作 ... } }]; @@ -189,7 +190,7 @@ config.coSingerUid = ; 歌曲播放时,音乐播放器会通过 `didChangedToState` 回调向业务层通知歌曲播放状态改变。收到 `didChangedToState(AgoraMediaPlayerStatePlaying)` 回调后,你可以使用 `seek`、`pause`、`resume`、`selectAudioTrack` 等方法控制播放器。 -
    KTV API 模块内部会自动处理播放器同步,因此你也可以通过 `didChangedToState` 回调获取远端播放器的状态。
    +
    KTV API 模块内部会自动处理播放器同步,因此你也可以通过 didChangedToState 回调获取远端播放器的状态。
    ```objective-c // 跳转到指定时间播放歌曲 @@ -232,7 +233,6 @@ config.coSingerUid = ; // 加入频道 [self.RTCKit joinChannelByToken: channelId: - info:nil uid: // 媒体选项详见第 5 步操作 mediaOptions:mediaOption @@ -242,7 +242,7 @@ config.coSingerUid = ; #### 2. 播放歌曲 -调用 `loadSong` 加载歌曲。歌曲加载结果会异步地通过 `withCallback` 回调通知你。收到 `KTVLoadSongStateOK` 回调状态后,调用 `playSong` 开始播放歌曲。 +调用 `loadSong` 加载歌曲。歌曲加载结果会异步地通过 `block` 回调通知你。收到 `KTVLoadSongStateOK` 回调状态后,调用 `playSong` 开始播放歌曲。 ```objective-c KTVSongConfiguration *config = [[KTVSongConfiguration alloc] init]; @@ -258,7 +258,7 @@ config.coSingerUid = 0; // 歌曲加载成功,则播放歌曲 [self.ktvApi playSong:songCode]; } else if(state == KTVLoadSongStateNoLyricUrl) { - // 歌曲加载失败,重试三次 + // 歌曲加载失败,则重试三次 ... } }]; @@ -306,7 +306,6 @@ options.clientRoleType = AgoraClientRoleBroadcaster; // 加入频道 [self.RTCKit joinChannelByToken: channelId: - info:nil uid: // 媒体选项详见第 4 步操作 mediaOptions:mediaOption @@ -315,7 +314,7 @@ options.clientRoleType = AgoraClientRoleBroadcaster; #### 2. 加载歌曲 -调用 `loadSong` 加载歌曲。加载结果会异步地通过 `withCallback` 回调通知你。收到 `withCallback(KTVLoadSongStateOK)` 回调状态后,调用 `playSong`。 +调用 `loadSong` 加载歌曲。加载结果会异步地通过 `block` 回调通知你。收到 `KTVLoadSongStateOK` 回调状态后,调用 `playSong`。 听众加入频道后,默认订阅主唱发布的音频合流,即主唱人声和音乐混合的音频流。听众只需调用 `playSong` 进入歌曲播放状态即可。 @@ -333,7 +332,7 @@ config.coSingerUid = 0; // 歌曲加载成功,则播放歌曲 [self.ktvApi playSong:songCode]; } else if(state == KTVLoadSongStateNoLyricUrl) { - // 歌曲加载失败,重试三次 + // 歌曲加载失败,则重试三次 ... } }]; @@ -354,7 +353,7 @@ config.coSingerUid = 0; 听众的用户角色为 `AgoraClientRoleAudience`,因此无法在频道内发布音频流。如果听众想上麦与主唱语聊,需要将用户角色修改为 `AgoraClientRoleBroadcaster`。修改角色后,SDK 默认发布该连麦听众的音频流,主唱和其他听众都能听到连麦听众的声音。 ```objective-c -// 针对需要上麦聊天的听众更新媒体选项 +// 对需要上麦聊天的听众更新媒体选项 AgoraRtcChannelMediaOptions* options = [AgoraRtcChannelMediaOptions new]; // 发布本地麦克风流 options.publishMicrophoneTrack = true @@ -366,7 +365,7 @@ options.clientRoleType = AgoraClientRoleBroadcaster [self.RTCKit updateChannelWithMediaOptions:options]; -// 针对未上麦的听众更新媒体选项 +// 对未上麦的听众更新媒体选项 AgoraRtcChannelMediaOptions* options = [AgoraRtcChannelMediaOptions new]; // 不发布本地麦克风流 options.publishMicrophoneTrack = false @@ -388,7 +387,7 @@ options.clientRoleType = AgoraClientRoleAudience - 主唱:加入频道,加载并播放歌曲,发布麦克风采集的音频流。KTV API 模块内部控制音乐播放器播放音乐,发布音乐到远端,将音乐播放进度同步到远端,让歌词组件进入歌词滚动状态等逻辑。 - 伴唱:加入频道,加载并播放歌曲,发布麦克风采集的音频流。KTV API 模块内部控制音乐播放器播放音乐,同步主唱的音乐播放进度,让歌词组件进入歌词滚动状态等逻辑。 -- 听众:加入频道,加载歌曲。KTV API 模块内部控制听众订阅主唱的人声和音乐的音频合流,同步主唱的音乐播放进度,让歌词组件进入歌词滚动状态等逻辑。如果普通观众需要上麦聊天,可以更新媒体选项。 +- 听众:加入频道,加载歌曲。KTV API 模块内部控制听众订阅主唱的人声和音乐的音频合流,订阅伴唱的人声,同步主唱的音乐播放进度,让歌词组件进入歌词滚动状态等逻辑。如果普通观众需要上麦聊天,可以更新媒体选项。 ![](https://web-cdn.agora.io/docs-files/1678784685024) @@ -400,7 +399,7 @@ options.clientRoleType = AgoraClientRoleAudience #### 1. 设置合唱私有参数 -实时合唱场景对低延时和音质的要求很高。开启合唱前,你需要在初始化 AgoraRtcEngineKit 的方法里设置如下私有参数。 +实时合唱场景对低延时和音质的要求很高。开启合唱前,你需要在初始化 `AgoraRtcEngineKit` 的方法里设置如下私有参数。 ```objective-c [self.RTCKit setParameters:@"{\"rtc.ntp_delay_drop_threshold\":1000}"]; @@ -417,7 +416,6 @@ options.clientRoleType = AgoraClientRoleAudience // 加入频道 [self.RTCKit joinChannelByToken: channelId: - info:nil uid: // 媒体选项详见第 5 步操作 mediaOptions:mediaOption @@ -426,7 +424,7 @@ options.clientRoleType = AgoraClientRoleAudience #### 3. 开始合唱 -调用 `loadSong` 加载歌曲。歌曲加载结果会异步地通过 `withCallback` 回调通知你。收到 `KTVLoadSongStateOK` 回调状态后,调用 `playSong` 开始播放歌曲,开始合唱。 +调用 `loadSong` 加载歌曲。歌曲加载结果会异步地通过 `block` 回调通知你。收到 `KTVLoadSongStateOK` 回调状态后,调用 `playSong` 开始播放歌曲,开始合唱。 ```objective-c KTVSongConfiguration *config = [[KTVSongConfiguration alloc] init]; @@ -442,7 +440,7 @@ config.coSingerUid = 0; // 歌曲加载成功,则播放歌曲 [self.ktvApi playSong:songCode]; } else if(state == KTVLoadSongStateNoLyricUrl) { - // 歌曲加载失败,重试三次 + // 歌曲加载失败,则重试三次 ... } }]; @@ -493,7 +491,6 @@ options.clientRoleType = AgoraClientRoleBroadcaster; // 加入频道 [self.RTCKit joinChannelByToken: channelId: - info:nil uid: // 媒体选项详见第 5 步操作 mediaOptions:mediaOption @@ -502,7 +499,7 @@ options.clientRoleType = AgoraClientRoleBroadcaster; #### 3. 开始合唱 -调用 `loadSong` 加载歌曲。歌曲加载结果会异步地通过 `withCallback` 回调通知你。收到 `KTVLoadSongStateOK` 回调状态后,调用 `playSong` 开始播放歌曲,开始合唱。 +调用 `loadSong` 加载歌曲。歌曲加载结果会异步地通过 `block` 回调通知你。收到 `KTVLoadSongStateOK` 回调状态后,调用 `playSong` 开始播放歌曲,开始合唱。 ```objective-c KTVSongConfiguration *config = [[KTVSongConfiguration alloc] init]; @@ -518,7 +515,7 @@ config.coSingerUid = ; // 歌曲加载成功,则播放歌曲 [self.ktvApi playSong:songCode]; } else if(state == KTVLoadSongStateNoLyricUrl) { - // 歌曲加载失败,重试三次 + // 歌曲加载失败,则重试三次 ... } }]; @@ -558,7 +555,6 @@ options.clientRoleType = AgoraClientRoleBroadcaster; // 加入频道 [self.RTCKit joinChannelByToken: channelId: - info:nil uid: // 媒体选项详见第 4 步操作 mediaOptions:mediaOption @@ -567,9 +563,9 @@ options.clientRoleType = AgoraClientRoleBroadcaster; #### 2. 加载歌曲 -调用 `loadSong` 加载歌曲。加载结果会异步地通过 `withCallback` 回调通知你。收到 `withCallback(KTVLoadSongStateOK)` 回调状态后,调用 `playSong`。 +调用 `loadSong` 加载歌曲。加载结果会异步地通过 `block` 回调通知你。收到 `KTVLoadSongStateOK` 回调状态后,调用 `playSong`。 -听众加入频道后,默认订阅主唱发布的音频合流,即主唱人声和音乐混合的音频流。听众只需调用 `playSong` 进入歌曲播放状态即可。 +听众加入频道后,默认订阅主唱人声和音乐混合的音频流,默认订阅伴唱人声。听众只需调用 `playSong` 进入歌曲播放状态即可。 ```objective-c KTVSongConfiguration *config = [[KTVSongConfiguration alloc] init]; @@ -600,10 +596,10 @@ config.coSingerUid = 0; 通过 [`updateChannelWithMediaOptions`](https://docs.agora.io/cn/online-ktv/API%20Reference/ios_ng/API/toc_core_method.html#api_irtcengine_updatechannelmediaoptions) 方法在听众加入频道后更新频道媒体选项,例如是否开启本地音频采集,是否发布本地音频流等。 -听众的用户角色为 `AgoraClientRoleAudience`,因此无法在频道内发布音频流。如果听众想上麦与主唱语聊,需要将用户角色修改为 `AgoraClientRoleBroadcaster`。修改角色后,SDK 默认发布该连麦听众的音频流,主唱和其他听众都能听到连麦听众的声音。 +听众的用户角色为 `AgoraClientRoleAudience`,因此无法在频道内发布音频流。如果听众想上麦与主唱/伴唱语聊,需要将用户角色修改为 `AgoraClientRoleBroadcaster`。修改角色后,SDK 默认发布该连麦听众的音频流,主唱、伴唱、其他听众都能听到连麦听众的声音。 ```objective-c -// 针对需要上麦聊天的听众更新媒体选项 +// 对需要上麦聊天的听众更新媒体选项 AgoraRtcChannelMediaOptions* options = [AgoraRtcChannelMediaOptions new]; // 发布本地麦克风流 options.publishMicrophoneTrack = true @@ -615,7 +611,7 @@ options.clientRoleType = AgoraClientRoleBroadcaster [self.RTCKit updateChannelWithMediaOptions:options]; -// 针对未上麦的听众更新媒体选项 +// 对未上麦的听众更新媒体选项 AgoraRtcChannelMediaOptions* options = [AgoraRtcChannelMediaOptions new]; // 不发布本地麦克风流 options.publishMicrophoneTrack = false From 418ca8174aff2d087a38f02821500c2e275eb057 Mon Sep 17 00:00:00 2001 From: kelzr Date: Fri, 14 Apr 2023 18:26:47 +0800 Subject: [PATCH 18/20] [KTVI] update after review [round 2] --- .../\347\202\271\346\255\214.wsd" | 4 ++-- .../\347\202\271\346\255\214.wsd" | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git "a/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/Android/\345\256\236\347\216\260\346\226\207\346\241\243/\347\202\271\346\255\214.wsd" "b/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/Android/\345\256\236\347\216\260\346\226\207\346\241\243/\347\202\271\346\255\214.wsd" index 16bebe8dc71..4c35e523ee8 100644 --- "a/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/Android/\345\256\236\347\216\260\346\226\207\346\241\243/\347\202\271\346\255\214.wsd" +++ "b/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/Android/\345\256\236\347\216\260\346\226\207\346\241\243/\347\202\271\346\255\214.wsd" @@ -7,10 +7,10 @@ participant "声网 SDK" as b == 初始化 KTV API 模块== a -> b: initWithRtcEngine == 获取歌曲列表(方式一:用关键词)== -a -> b: searchSong +a -> b: searchMusic b -->> a: onMusicCollectionResult == 获取歌曲列表(方式二:用音乐榜单)== -a -> b: searchSongWithRankingChartId +a -> b: getMusicCollectionByMusicChartId b -->> a: onMusicChartsResult == 加载歌曲 == a -> b: loadSong diff --git "a/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/iOS/\345\256\236\347\216\260\346\226\207\346\241\243/\347\202\271\346\255\214.wsd" "b/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/iOS/\345\256\236\347\216\260\346\226\207\346\241\243/\347\202\271\346\255\214.wsd" index 8b4cbe4155b..5b1b2f63bbe 100644 --- "a/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/iOS/\345\256\236\347\216\260\346\226\207\346\241\243/\347\202\271\346\255\214.wsd" +++ "b/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/iOS/\345\256\236\347\216\260\346\226\207\346\241\243/\347\202\271\346\255\214.wsd" @@ -7,10 +7,10 @@ participant "声网 SDK" as b == 初始化 KTV API 模块== a -> b: initWithRtcEngine == 获取歌曲列表(方式一:用关键词)== -a -> b: loadSearchDataWithKeyWord +a -> b: searchMusicWithKeyWord b -->> a: onMusicCollectionResult == 获取歌曲列表(方式二:用音乐榜单)== -a -> b: loadDataWithIndex +a -> b: getMusicCollectionWithMusicChartId b -->> a: onMusicChartsResult == 加载歌曲 == a -> b: loadSong From eebcf607915de7cde3f14ea9e9c4a678c483ae21 Mon Sep 17 00:00:00 2001 From: kelzr Date: Mon, 8 May 2023 16:13:58 +0800 Subject: [PATCH 19/20] [KTVI] update after review [round 3] --- .../\351\233\206\346\210\220.md" | 2 +- .../\351\233\206\346\210\220.md" | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git "a/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/Android/\345\256\236\347\216\260\346\226\207\346\241\243/\351\233\206\346\210\220.md" "b/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/Android/\345\256\236\347\216\260\346\226\207\346\241\243/\351\233\206\346\210\220.md" index 75d0977d579..fdcb762a123 100644 --- "a/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/Android/\345\256\236\347\216\260\346\226\207\346\241\243/\351\233\206\346\210\220.md" +++ "b/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/Android/\345\256\236\347\216\260\346\226\207\346\241\243/\351\233\206\346\210\220.md" @@ -19,7 +19,7 @@ 下图展示点歌的 API 调用时序图: -![](https://web-cdn.agora.io/docs-files/1678788543385) +![](https://web-cdn.agora.io/docs-files/1683360019651) ### 1. 初始化 KTV API 模块 diff --git "a/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/iOS/\345\256\236\347\216\260\346\226\207\346\241\243/\351\233\206\346\210\220.md" "b/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/iOS/\345\256\236\347\216\260\346\226\207\346\241\243/\351\233\206\346\210\220.md" index df0efd5bd70..05dddde15b5 100644 --- "a/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/iOS/\345\256\236\347\216\260\346\226\207\346\241\243/\351\233\206\346\210\220.md" +++ "b/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/iOS/\345\256\236\347\216\260\346\226\207\346\241\243/\351\233\206\346\210\220.md" @@ -19,7 +19,7 @@ 下图展示点歌的 API 调用时序图: -![](https://web-cdn.agora.io/docs-files/1678788669061) +![](https://web-cdn.agora.io/docs-files/1683360046603) ### 1. 初始化 KTV API 模块 From 907b9a9bf3fde5edcfb01129b12fb9a199b0f0df Mon Sep 17 00:00:00 2001 From: kelzr Date: Thu, 18 May 2023 17:23:31 +0800 Subject: [PATCH 20/20] [KTVI] update after review [round 3] --- ...46\231\257\345\214\226 Kotlin API for Android.md" | 12 ++++++++++-- ...6\231\257\345\214\226 Objective-C API for iOS.md" | 12 ++++++++++-- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git "a/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/Android/K \346\255\214\346\210\277\345\234\272\346\231\257\345\214\226 Kotlin API for Android.md" "b/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/Android/K \346\255\214\346\210\277\345\234\272\346\231\257\345\214\226 Kotlin API for Android.md" index bb19be8860e..b8ab3bac3fa 100644 --- "a/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/Android/K \346\255\214\346\210\277\345\234\272\346\231\257\345\214\226 Kotlin API for Android.md" +++ "b/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/Android/K \346\255\214\346\210\277\345\234\272\346\231\257\345\214\226 Kotlin API for Android.md" @@ -1,5 +1,7 @@ 本文提供在线 K 歌房场景定制化 Kotlin API。你可以在 GitHub 上查看源码文件 [KTVApi.kt](https://github.com/AgoraIO-Usecase/agora-ent-scenarios/blob/v2.1.1-ktv-Android/Android/scenes/ktv/src/main/java/io/agora/scene/ktv/live/KTVApi.kt) 和 [KTVApiImpl.kt](https://github.com/AgoraIO-Usecase/agora-ent-scenarios/blob/v2.1.1-ktv-Android/Android/scenes/ktv/src/main/java/io/agora/scene/ktv/live/KTVApiImpl.kt)。 +
    本文适用于场景化 API v2.1.1。
    + ## 方法 ### initWithRtcEngine @@ -177,8 +179,14 @@ interface KTVApiEventHandler { - `state`: 播放器的当前状态。详见 [MediaPlayerState](https://docs.agora.io/cn/online-ktv/API%20Reference/java_ng/API/enum_mediaplayerstate.html?platform=Android)。 - `error`: 播放器的错误码。详见 [MediaPlayerError](https://docs.agora.io/cn/online-ktv/API%20Reference/java_ng/API/enum_mediaplayererror.html?platform=Android)。 - `isLocal`: 是否为本地事件: - - `true`: 是本地事件。 - - `false`: 不是本地事件。 + - `true`: 代表是本地播放器的状态改变。可用于主唱和伴唱监听本地播放器状态。 + - `false`: 是远端播放器的状态改变。可用于伴唱和听众知晓主唱的播放器状态,从而方便后续进行多端播放同步。 + +举例来说,在合唱场景下,主唱、伴唱、听众收到的 `onPlayerStateChanged` 回调有如下区别: + +- 主唱:收到一个 `isLocal` 为 `true` 的回调,报告主唱播放器的状态改变。 +- 伴唱:收到一个 `isLocal` 为 `true` 的回调,报告伴唱播放器的状态改变;同时,还收到一个 `isLocal` 为 `false` 的回调,报告主唱播放器的状态改变。 +- 听众:收到一个 `isLocal` 为 `false` 的回调报告主唱端播放器的状态改变。 ## Enum class diff --git "a/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/iOS/K \346\255\214\346\210\277\345\234\272\346\231\257\345\214\226 Objective-C API for iOS.md" "b/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/iOS/K \346\255\214\346\210\277\345\234\272\346\231\257\345\214\226 Objective-C API for iOS.md" index 2204013aace..fff69a99eb2 100644 --- "a/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/iOS/K \346\255\214\346\210\277\345\234\272\346\231\257\345\214\226 Objective-C API for iOS.md" +++ "b/markdown/online-ktv/\346\224\271\350\277\233/\345\234\272\346\231\257\345\214\226\346\226\271\346\241\210/iOS/K \346\255\214\346\210\277\345\234\272\346\231\257\345\214\226 Objective-C API for iOS.md" @@ -1,5 +1,7 @@ 本文提供在线 K 歌房场景定制化 Objective-C API。你可以在 GitHub 上查看源码文件 [KTVApi.h](https://github.com/AgoraIO-Usecase/agora-ent-scenarios/blob/v2.1.1-ktv-iOS/iOS/AgoraEntScenarios/Scenes/KTV/ViewController/KTV/KTVApi.h) 和 [KTVApi.m](https://github.com/AgoraIO-Usecase/agora-ent-scenarios/blob/v2.1.1-ktv-iOS/iOS/AgoraEntScenarios/Scenes/KTV/ViewController/KTV/KTVApi.m)。 +
    本文适用于场景化 API v2.1.1。
    + ## 方法 ### initWithRtcEngine:channel:musicCenter:player:dataStreamId:delegate: @@ -159,8 +161,14 @@ withCallback:(void (^ _Nullable)(NSInteger songCode, NSString* lyricUrl, KTVSing - `songCode`: 歌曲编号。 - `state`: 播放器的当前状态。详见 [AgoraMediaPlayerState](https://docs.agora.io/cn/online-ktv/API%20Reference/ios_ng/API/enum_mediaplayerstate.html?platform=iOS)。 - `local`: 是否为本地事件: - - `YES`: 是本地事件。 - - `NO`: 不是本地事件。 + - `YES`: 代表是本地播放器的状态改变。可用于主唱和伴唱监听本地播放器状态。 + - `NO`: 是远端播放器的状态改变。可用于伴唱和听众知晓主唱的播放器状态,从而方便后续进行多端播放同步。 + +举例来说,在合唱场景下,主唱、伴唱、听众收到的 `didChangedToState` 回调有如下区别: + +- 主唱:收到一个 `local` 为 `YES` 的回调,报告主唱播放器的状态改变。 +- 伴唱:收到一个 `local` 为 `YES` 的回调,报告伴唱播放器的状态改变;同时,还收到一个 `local` 为 `NO` 的回调,报告主唱播放器的状态改变。 +- 听众:收到一个 `local` 为 `NO` 的回调报告主唱端播放器的状态改变。 ## Enum