输入关键词搜索,支持 AI 答疑

实现独唱

更新时间: 2024/11/26 15:44:05

独唱是指麦位上的用户独自演唱,房间内的观众在线观看。

功能原理

独唱的原理图如下图所示。

独唱的原理图.png

NERTC SDK 将主唱的伴奏和干声传给 NERTC 服务器, NERTC 服务器将音频传给观众端。

主唱实现方案

  1. 主唱调用applyOnSeat申请上麦,成为连麦主播。

    fun requestSeat(callback: NECallback<AnyObject>) {
         roomContext?.seatController.submitSeatRequest { code, msg, _ in
                if code == 0 {
                    NEKaraokeLog.successLog(kitTag, desc: "Successfully apply on seat.")
                } else {
                    NEKaraokeLog.errorLog(kitTag, desc: "Failed to apply on seat. Code: \(code). Msg: \(msg ?? "")")
                }
                callback?(code, msg, nil)
            }
    }
    
  2. 麦位上的用户可以点歌,具体实现逻辑需要业务自行实现。

  3. 歌词展示与同步,具体实现逻辑需要业务自行实现。

  4. 主唱以主流方式发送伴奏,将伴奏和人声混流后的音频流推送到远端。

     roomContext?.rtcController.setParameters(["key_audio_external_audio_mix": true])
     roomContext?.rtcController.setParameters(["engine.audio.ktv.chrous": true])
    
  5. 主唱播放音乐

    func playOrginalAndAccompany(_ roomContext: NERoomContext,
                                 _ orginalPath: String,
                                 _ accompanyPath: String,
                                 _ volume: Int,
                                 timestamp: Int64 = 0,
                                 type: NERoomAudioStreamType = .main,
                                 sendEnable:Bool = true) -> Int {
        roomContext.rtcController.stopAllEffects()
        // 原唱
        let oOption = NERoomCreateAudioEffectOption()
        oOption.startTimeStamp = timestamp
        oOption.path = orginalPath
        oOption.playbackVolume = 0
        oOption.sendVolume = 0
        oOption.sendEnabled = sendEnable
        oOption.sendWithAudioType = type
        var code = roomContext.rtcController.playEffect(effectId: originalId, option: oOption)
        if code == 0 {
            // 伴奏
            let aOption = NERoomCreateAudioEffectOption()
            aOption.startTimeStamp = timestamp
            aOption.path = accompanyPath
            aOption.playbackVolume = volume
            aOption.sendVolume = volume
            aOption.sendEnabled = sendEnable
            aOption.sendWithAudioType = type
            code = roomContext.rtcController.playEffect(effectId: accompanyId, option: aOption)
        }
        return code
    }
    
  6. 歌词同步。

    主唱收到伴奏播放进度回调,并将自己的伴奏进度通过 SEI 发送出去。

                self.playTimer = Timer.init(timeInterval: 0.1, repeats: true, block: { timer in
                    let position = self.roomContext?.rtcController.getEffectCurrentPosition(effectId: self.isOriginal ? self.originalId : self.accompanyId)
                    // 上报当前播放进度
                    if self.callback != nil && position != nil {
                        self.callback!.onSongPlayPosition(position!)
                    }
                    // 发送SEI
                    if let data = SEI(position ?? 0).toData() {
                        self.roomContext?.rtcController.sendSEIMsg(data)
                    }
                })
                if self.playTimer != nil {
                    RunLoop.current.add(self.playTimer!, forMode: .common)
                    self.playTimer?.fire()
                }
    
  7. 主唱 leaveSeat 接口申请下麦。

    self.roomContext!.seatController.leaveSeat { code, msg, _ in
        if code == 0 {
            NEKaraokeLog.successLog(kitTag, desc: "Successfully leaveseat.")
        } else {
            NEKaraokeLog.errorLog(kitTag, desc: "Failed to leave seat. Code: \(code). Msg: \(msg ?? "")")
        }
        callback?(code, msg, nil)
    }
    

观众端实现方案

  1. 观众端调用setAudioProfile接口,设置音频 profile 类型为 HighQualityStereo,设置 scenarioMUSIC
  2. 观众端同步本地歌词进度。
    1. 观众端监听 onRtcReciveSEIMessage 的 SEI 回调信息。
    2. 观众收到主唱发送的 SEI 消息,根据拿到的时间戳,解析歌词、展示时间戳对应时间段的歌词数据。
    /// 接收到SEI消息
    func onRtcReciveSEIMessage(_ userUuid: String, message: Data) {
        // String转字典
        guard let jsonObjc = NEKaraokeDecoder.decode(message) else { return }
        guard let musicPosition = jsonObjc[SEI_KEY] as? UInt64  else { return }
        // 上报当前主唱的演唱进度
        self.callback?.onSongPlayPosition(musicPosition)
    }
    
此文档是否对你有帮助?
有帮助
去反馈
  • 功能原理
  • 主唱实现方案
  • 观众端实现方案