}
func setupRecord(){
+ // 通话录音声道数。1:单声道 ;2:⽴体声;默认单声道
+ StarburstConfig.shared().callRecordChannel = 1
+
let config = StarburstAsrConfig()
config.isTranslate = isTranslate
config.fromLanguage = fromLanguage
public var ttsFormat = "mp3"
/// 识别回调
public var asrCallback:((String)->Void)?
+ /// 对方识别回调
+ public var asrOtherCallback:((String)->Void)?
/// 状态码回调
public var codeCallback:((Int)->Void)?
/// 录音文件回调
public var filePathCallBack:((String?)->Void)?
- /// 设置录音文件路径
- public var filePath:String?
+ /// opus回调
+ public var opusCallBack:((Data)->Void)?
var asrList : [(String,Int)] = []
var asrText = ""
-
+ var asrOtherList : [(String,Int)] = []
+ var asrOtherText = ""
init() {
setupRecord()
}
func setupRecord(){
+ // 通话录音声道数。1:单声道 ;2:⽴体声;默认单声道
+ StarburstConfig.shared().callRecordChannel = 2
+
let config = StarburstAsrConfig()
config.isTranslate = isTranslate
config.fromLanguage = fromLanguage
config.isTTs = isTTs
config.ttsFormat = ttsFormat
- StarburstSdk.shared().setupDeviceRecord(with: filePath, asrConfig: config) { config in
+ StarburstSdk.shared().setupDeviceRecord(with: nil, asrConfig: config) { config in
DCLogger.debug("录音类型 \(config!.type == 0x00 ? "实时录音" : "通话录音")")
} recognizeText: { [weak self] model in
guard let self = self else {return}
guard let model = model else {return}
-
+ DCLogger.debug(model.channel,"asr =",model.textResult)
if let text = model.textResult,!text.isEmpty{
- DCLogger.debug("asr = \(text)")
- let msg = asrList.firstIndex (where: { (str,idx) in
- return idx == model.definiteIndex;
- })
- if (msg == nil) {
- asrList.append((text,model.definiteIndex))
- } else {
- asrList[msg!] = (text,model.definiteIndex)
- }
- asrText = asrList.compactMap({ (str,idx) in
- return str
- }).joined(separator: "\n")
- asrCallback?(asrText)
+ if model.channel == 1 {
+ //左耳
+ let msg = asrList.firstIndex (where: { (str,idx) in
+ return idx == model.definiteIndex;
+ })
+ if (msg == nil) {
+ asrList.append((text,model.definiteIndex))
+ } else {
+ asrList[msg!] = (text,model.definiteIndex)
+ }
+ asrText = asrList.compactMap({ (str,idx) in
+ return str
+ }).joined(separator: "\n")
+
+ asrCallback?(asrText)
+ }
+ else if model.channel == 2{
+ //右耳
+ let msg = asrOtherList.firstIndex (where: { (str,idx) in
+ return idx == model.definiteIndex;
+ })
+ if (msg == nil) {
+ asrOtherList.append((text,model.definiteIndex))
+ } else {
+ asrOtherList[msg!] = (text,model.definiteIndex)
+ }
+ asrOtherText = asrOtherList.compactMap({ (str,idx) in
+ return str
+ }).joined(separator: "\n")
+
+ asrCallback?(asrOtherText)
+ }
+
}
} stateChange: { [weak self] code in
DCLogger.debug("状态改变",code)
DCLogger.debug("文件录音保存路径 \(filePath ?? "")")
self?.filePathCallBack?(filePath)
}
+
+ //通话录音声道数。1:单声道 ;2:⽴体声;默认单声道
+ StarburstConfig.shared().callRecordChannel = 2
+ //收到sdk传过来的opus音频数据,传到OpusManager解码。解码过程是异步
+ StarburstSdk.shared().outputOpusData {[weak self] (config, opusData) in
+ DispatchQueue.global().async {
+ self?.opusCallBack?(opusData!)
+ }
+ }
+ }
+
+ public func inputPcm(data:Data){
+ StarburstSdk.shared().inputPcmData(data)
}
/// 0x00实时录音,0x0c通话录音
- public func start(type:Int,recordPath:String?){
+ public func start(){
asrList = []
asrText = ""
-
- filePath = recordPath
- DCLogger.debug("设置地址 \(filePath ?? "")")
+
+
+ asrOtherList = []
+ asrOtherText = ""
setupRecord()
let config = StarburstBleAsrConfig()
- config.type = type
+ config.type = 0x0c
StarburstSdk.shared().startDeviceRecord(config)
}
var cmd: UInt8 = 0x0C
var data:UInt8 = 0x06
var index:UInt8 = UInt8(index)
- var value:UInt8 = UInt8(value)
+ let int8Value: Int8 = Int8(value)
+ let valueData = withUnsafeBytes(of: int8Value) { Data($0) }
+
var total = Data()
total.append(&head, count: 1)
total.append(&cmd, count: 1)
total.append(&data, count: 1)
total.append(&index, count: 1)
- total.append(&value, count: 1)
+ total.append(valueData)
total.addCrc()
let sender = BleSender()
let type = UInt8(result.data[2].int)
DCLogger.debug(type)
if type == 5 {
+ let data = result.data[3...12]
+ let int8Array = data.map { byte in
+ return Data([byte]).toInt8()
+ }
+ print(int8Array)
+ let list = int8Array.map{Int($0)}
+ finished((type,list),true)
+
+ }
+ else{
+ finished((type,[]),true)
}
- finished((type,[]),true)
}
else{
finished(nil,false)
DCBleManager.shared.send(sender)
}
}
-
+extension Data {
+ func toInt8() -> Int8 {
+ var int8Result: Int8 = 0
+ self.withUnsafeBytes {
+ int8Result = $0.load(as: Int8.self)
+ }
+ return int8Result
+ }
+}
extension DCDevice{
//0x01(打开AI对话)
StarburstConfig.shared().switch(to: .ble)
StarburstConfig.shared().appId = "prjzVnOEpKL1N"
- StarburstConfig.shared().isDebug = false
+ StarburstConfig.shared().isDebug = true
//MARK: 配置缓存路径
StarburstConfig.shared().sdkFolder = NSTemporaryDirectory()
// StarburstConfig.shared().sdkFolder = NSHomeDirectory() + "/Documents/DeepChordsSdk"
StarburstConfig.shared().enableEarbudsSwitchSync = true
//耳机仓表情下发开关
StarburstConfig.shared().enableEmotionSyncToDevice = false
- // 通话录音声道数。1:单声道 ;2:⽴体声;默认单声道
- StarburstConfig.shared().callRecordChannel = 2
//添加调试信息
// StarburstConfig.shared().debugInfo = [
// "x-use-ppe":"1",
}
}
else{
-// DCDevice.shared.setCustomEQ(index: UInt8(index), value: value) {result in
-// }
+ DCDevice.shared.setCustomEQ(index: UInt8(index), value: value) {result in
+ }
}
}
}
}
else{
// if JoySdkManager.shared.callRecord.supportStereo{
-// App.shared.goToPage(DcStereoCallVC())
+ App.shared.goToPage(DcStereoCallVC())
// }
// else{
- App.shared.goToPage(DcCallingRecordVC())
+// App.shared.goToPage(DcCallingRecordVC())
// }
}
}
--- /dev/null
+//
+// DcStereo.swift
+// ShenYin
+//
+// Created by liangshi on 2025/12/31.
+//
+
+import UIKit
+import DeepChordsSdk
+
+class DcStereo:NSObject{
+ static let shared = DcStereo()
+
+ var recordFinish:((String)->Void)?
+ var stereoNeedReloadData:((String,Int)->Void)?
+ var selfText = ""
+
+ var otherText = ""
+ /// 状态码回调
+ public var codeCallback:((Int)->Void)?
+ public var callRecordNotOnCall:(()->Void)?
+
+
+ override init() {
+ super.init()
+ setupSdk()
+ }
+ func setupSdk(){
+ DCCallRecord.shared.asrCallback = { [weak self] text in
+ guard let self = self else{return}
+ selfText = text
+ stereoNeedReloadData?(text,1)
+ }
+ DCCallRecord.shared.asrOtherCallback = { [weak self] text in
+ guard let self = self else{return}
+ otherText = text
+ stereoNeedReloadData?(text,2)
+ }
+ DCCallRecord.shared.codeCallback = { [weak self] code in
+ guard let self = self else{return}
+ codeCallback?(code)
+ }
+ DCCallRecord.shared.opusCallBack = { data in
+ OpusManager.shared().write(data)
+ }
+ DCCallRecord.shared.filePathCallBack = { [weak self] path in
+ guard let self = self else{return}
+ if selfText.isEmpty || otherText.isEmpty {
+ return
+ }
+ guard let path = path,path.isNotEmpty else{
+ return
+ }
+ let path1 = OpusTool.opusFile(toWAVFile: path)
+ guard let wavPath = DCFolder.recordFileResave(srcPath: path1) else{return}
+ recordFinish?(wavPath)
+ }
+
+ //设置解码参数
+ OpusManager.shared().setupSampleRate(16000, frameRate: 80)
+ //opus转成pcm,发送给sdk
+ OpusManager.shared().pcmDataCallback = { data in
+ DCCallRecord.shared.inputPcm(data: data)
+ }
+ }
+ func setRecordLanage(_ language:CCAiLanguage){
+ DCBleRecord.shared.fromLanguage = language.value
+ }
+ func recordStart(){
+ selfText = ""
+ otherText = ""
+ setupSdk()
+ if !CCCall.shared.isInCall {
+ callRecordNotOnCall?()
+ return
+ }
+ Logger.debug("开启通话录音")
+ DCCallRecord.shared.start()
+ }
+ func recordStop(){
+ DCBleRecord.shared.stop()
+ }
+}
Logger.debug("开启通话录音")
DCBleRecord.shared.start(type: 0x00, recordPath: nil )
}
- func recordResume(){
- // JoyGemRecordManager.shared().resume()
- }
- func recordPause(){
- // JoyGemRecordManager.shared().pause()
- }
func recordStop(){
DCBleRecord.shared.stop()
}
wavPath = nil
setupSdk()
recordPath = DcXcly.getFilePath()
- DCBleRecord.shared.start(type: 0x0c, recordPath: recordPath )
+ DCBleRecord.shared.start(type: 0x00, recordPath: recordPath )
}
func recordResume(){
wavPath = nil
pausText = recordText
- DCBleRecord.shared.start(type: 0x0c, recordPath: recordPath )
+ DCBleRecord.shared.start(type: 0x00, recordPath: recordPath )
}
func recordPause(){
DCBleRecord.shared.stop()
let path = cacheDirectory.appendingPathComponent(name).path
return path
}
+// static func getFilePath()->String?{
+// // 获取缓存目录路径
+// let cacheDirectory = DCFolder.record
+// // 构建压缩文件路径
+// let name = "\(Int(Date().timeIntervalSince1970*1000)).opus"
+// let path = cacheDirectory + name
+// return path
+// }
}
extension DcChatVC:ChatRecordBarDelegate{
func recordBarCancel(_ toolBar: ChatRecordBar) {
-// JoyGemChatManager.shared().stopSingleChat()
DcChatManager.shared.stopChat()
if let item = DcChatManager.shared.dataList.last,item.content.isEmpty{
DcChatManager.shared.dataList.removeLast()
}
func recordBarStart(_ toolBar: ChatRecordBar) {
-// JoyGemChatManager.shared().startSingleChat()
DcChatManager.shared.startChat()
}
func recordBarStop(_ toolBar: ChatRecordBar) {
-// JoyGemChatManager.shared().stopSingleChat()
DcChatManager.shared.stopChat()
}
}
CCAiManager.preparePlayBlueTooth()
}
override func navleftViewTapped() {
- if toolBar.recordBtn.isSelected {
- self.sdk.stop()
- // CustomAlert.showWith(title: "提示".l10n, messgage: "当前正在录音,确定放弃当前录音吗".l10n, cancelCallback: {}) {
- // self.sdk.stop()
- //// PcmSound.sharedInstance().stopAudioUnit()
- //// PcmSound.sharedInstance().cleanup()
- // super.navleftViewTapped()
- // }
- }
- else{
- super.navleftViewTapped()
- }
+ self.sdk.stop()
+ super.navleftViewTapped()
+// if toolBar.recordBtn.isSelected {
+// self.sdk.stop()
+// // CustomAlert.showWith(title: "提示".l10n, messgage: "当前正在录音,确定放弃当前录音吗".l10n, cancelCallback: {}) {
+// // self.sdk.stop()
+// //// PcmSound.sharedInstance().stopAudioUnit()
+// //// PcmSound.sharedInstance().cleanup()
+// // super.navleftViewTapped()
+// // }
+// }
+// else{
+// super.navleftViewTapped()
+// }
}
override func navRightViewTapped() {
let tip = RecordTipsView(type: .chinese2english)
DCBleRecord.shared.stop()
}
}
-
-//extension DcDialogTranslateVC : JoyGemSIManagerDelegate {
-//
-//
-// func siManagerStopped(_ manager: JoyGemSIManager, reason code: JoyGemRecordStopCode) {
-// if let model = dataList.last,model.send.isEmpty{
-// dataList.removeLast()
-// refreshData()
-// }
-// }
-//
-// func siManager(_ manager: JoyGemSIManager, didUpdateRaw model: JoyGemAsrModel) {
-// let msg = tmpList.firstIndex (where: { (str,idx) in
-// return idx == model.index;
-// })
-// if (msg == nil) {
-// tmpList.append((model.text,model.index))
-// } else {
-// tmpList[msg!] = (model.text,model.index)
-// }
-//
-// ///监听到一句话结束,自动结束当前录音
-// if (model.definite == true) {
-// toolBar.stopRecord(.phone)
-// stopPhoneRecord()
-// }
-//
-// guard let model = dataList.last else {
-// return
-// }
-//
-// model.send = tmpList.compactMap({ (str,idx) in
-// return str
-// }).joined(separator: "\n")
-// refreshData()
-// }
-//
-// func siManager(_ manager: JoyGemSIManager, didUpdateTranslate model: JoyGemAsrModel) {
-// let msg = tmpOtherList.firstIndex (where: { (str,idx) in
-// return idx == model.index;
-// })
-// if (msg == nil) {
-// tmpOtherList.append((model.text,model.index))
-// } else {
-// tmpOtherList[msg!] = (model.text,model.index)
-// }
-//
-// guard let model = dataList.last else {
-// return
-// }
-//
-// model.response = tmpOtherList.compactMap({ (str,idx) in
-// return str
-// }).joined(separator: "\n")
-// refreshData()
-// }
-//
-// func siManager(_ manager: JoyGemSIManager, saveFile path: String) {
-//
-// }
-//
-//
-// func siManager(_ manager: JoyGemSIManager, stateChange code: JoyGemStateCode) {
-//
-// }
-//
-// func siManagerStopped(_ manager: JoyGemSIManager) {
-//
-// }
-// func siManager(_ manager: JoyGemSIManager, didUpdateAudioData data: Data, sampleRate: UInt) {
-// PcmSound.sharedInstance().feed(data)
-// }
-// func siManager(_ manager: JoyGemSIManager, onError code: JoyGemErrorCode) {
-//
-// }
-//}
-
-//extension DcDialogTranslateVC : JoyGemSIDeviceManagerDelegate {
-// func siDeviceManagerStopped(_ manager: JoyGemSIDeviceManager, reason code: JoyGemRecordStopCode) {
-// if let model = dataList.last,model.send.isEmpty{
-// dataList.removeLast()
-// refreshData()
-// }
-// }
-//
-// func siDeviceManager(_ manager: JoyGemSIDeviceManager, didUpdateRaw model: JoyGemAsrModel) {
-// Logger.debug("text: \(model.text),index: \(model.index)")
-// let msg = tmpList.firstIndex (where: { (str,idx) in
-// return idx == model.index;
-// })
-// if (msg == nil) {
-// tmpList.append((model.text,model.index))
-// } else {
-// tmpList[msg!] = (model.text,model.index)
-// }
-//
-// ///监听到一句话结束,自动结束当前录音
-// if (model.definite == true) {
-// toolBar.stopRecord(.earphone)
-// stopEarRecord()
-// }
-//
-// guard let model = dataList.last else {
-// return
-// }
-//
-// model.send = tmpList.compactMap({ (str,idx) in
-// return str
-// }).joined(separator: "\n")
-// refreshData()
-// }
-//
-// func siDeviceManager(_ manager: JoyGemSIDeviceManager, didUpdateTranslate model: JoyGemAsrModel) {
-// let msg = tmpOtherList.firstIndex (where: { (str,idx) in
-// return idx == model.index;
-// })
-// if (msg == nil) {
-// tmpOtherList.append((model.text,model.index))
-// } else {
-// tmpOtherList[msg!] = (model.text,model.index)
-// }
-//
-// guard let model = dataList.last else {
-// return
-// }
-//
-// model.response = tmpOtherList.compactMap({ (str,idx) in
-// return str
-// }).joined(separator: "\n")
-// refreshData()
-// }
-// func siDeviceManager(_ manager: JoyGemSIDeviceManager, didUpdateAudioData data: Data, sampleRate: UInt) {
-// PcmSound.sharedInstance().feed(data)
-// }
-// func siDeviceManager(_ manager: JoyGemSIDeviceManager, update mode: JoyGemRecordMode) {
-//
-// }
-//
-// func siDeviceManager(_ manager: JoyGemSIDeviceManager, saveFile path: String) {
-// Logger.debug("path: \(path)")
-// }
-//
-// func siDeviceManager(_ manager: JoyGemSIDeviceManager, stateChange code: JoyGemStateCode) {
-// Logger.debug("code: \(code)")
-// }
-//
-// func siDeviceManager(_ manager: JoyGemSIDeviceManager, onError code: JoyGemErrorCode) {
-//
-// }
-//}
-
extension DcDialogTranslateVC : PcmSoundDelegate {
func remaining(_ frames: UInt) {
Logger.debug("[frame:\(frames)]");
}
override func setupData() {
if bgEnter {
- selfTextView.text = JoySdkManager.shared.callRecord.selfText
- otherTextView.text = JoySdkManager.shared.callRecord.otherText
+ selfTextView.text = DcStereo.shared.selfText
+ otherTextView.text = DcStereo.shared.otherText
}
}
func setupSdk(){
-// JoySdkManager.shared.callRecord.recordFinish = {[weak self] path in
-// guard let self = self else { return }
-// if !needSave {
-// return
-// }
-// let v = PopTextField()
-// v.show()
-// v.cancelTap = {
-// App.shared.popPage()
-// }
-// v.okTap = { [weak self] text in
-// guard let self = self else{return}
-// gotoDetail(fileName: text, wavPath: path, language: toolBar.leftItem.language)
-// }
-// }
-// JoySdkManager.shared.callRecord.stereoNeedReloadData = {[weak self] (text,index) in
-// guard let self = self else { return }
-// if (index == 1) {
-// selfTextView.text = text
-// selfTextView.scrollToBottom()
-// } else if (index == 2) {
-// otherTextView.text = text
-// otherTextView.scrollToBottom()
-// }
-// }
-// JoySdkManager.shared.callRecord.callRecordAutoFinish = {[weak self] in
+ DcStereo.shared.recordFinish = {[weak self] path in
+ guard let self = self else { return }
+ if !needSave {
+ return
+ }
+ let v = PopTextField()
+ v.show()
+ v.cancelTap = {
+ App.shared.popPage()
+ }
+ v.okTap = { [weak self] text in
+ guard let self = self else{return}
+ gotoDetail(fileName: text, wavPath: path, language: toolBar.leftItem.language)
+ }
+ }
+ DcStereo.shared.stereoNeedReloadData = {[weak self] (text,index) in
+ guard let self = self else { return }
+ if (index == 1) {
+ selfTextView.text = text
+ selfTextView.scrollToBottom()
+ } else if (index == 2) {
+ otherTextView.text = text
+ otherTextView.scrollToBottom()
+ }
+ }
+// DcStereo.shared.callRecordAutoFinish = {[weak self] in
// guard let self = self else { return }
// stopAction()
// }
-// JoySdkManager.shared.callRecord.callRecordNotOnCall = {[weak self] in
-// guard let self = self else { return }
-// YLprogressHUD.showAutoHiden("未处于通话状态".l10n)
-// rhythView.reset()
-// toolBar.recordBtn.isSelected = false
-// toolBar.leftItem.alpha = 1
-// }
-// if autoStart{
-// startRecord()
-// }
+ DcStereo.shared.callRecordNotOnCall = {[weak self] in
+ guard let self = self else { return }
+ YLprogressHUD.showAutoHiden("未处于通话状态".l10n)
+ rhythView.reset()
+ toolBar.recordBtn.isSelected = false
+ toolBar.leftItem.alpha = 1
+ }
+ if autoStart{
+ startRecord()
+ }
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
CustomAlert.showWith(title: "提示".l10n, messgage: "当前正在录音,确定放弃当前录音吗?".l10n, cancelCallback: {}) {[unowned self] in
self.rhythView.stopTimer()
self.needSave = false
- JoySdkManager.shared.callRecord.callRecordStop()
+ DcStereo.shared.recordStop()
self.navigationController?.popViewController(animated: true)
}
}
extension DcStereoCallVC:FieldRecordBarDelegate{
func fieldRecordBar(_ bar: FieldRecordBar, languageChange language: CCAiLanguage) {
- JoySdkManager.shared.callRecord.setCallRecoordLanage(language)
+ DcStereo.shared.setRecordLanage(language)
AiLanguageCache.save(type: .thly, lan: language)
}
func fieldRecordBar(_ bar: FieldRecordBar, stateChange state: RecordSate) {
if state == .start {
- // selfMessageList.removeAll()
- // otherMessageList.removeAll()
selfTextView.text = nil
otherTextView.text = nil
- JoySdkManager.shared.callRecord.callRecordStart()
rhythView.startTimer()
+ DcStereo.shared.recordStart()
}
else if state == .stop{
stopAction()
}
func stopAction(){
rhythView.stopTimer()
- JoySdkManager.shared.callRecord.callRecordStop()
+ DcStereo.shared.recordStop()
toolBar.startAnimating()
}
func gotoDetail( fileName:String,wavPath:String,language:CCAiLanguage){
CCAiManager.preparePlayBlueTooth()
}
override func navleftViewTapped() {
- if toolBar.recordBtn.isSelected {
- self.sdk.stop()
-// CustomAlert.showWith(title: "提示".l10n, messgage: "当前正在录音,确定放弃当前录音吗".l10n, cancelCallback: {}) {
-// self.sdk.stop()
-//// PcmSound.sharedInstance().stopAudioUnit()
-//// PcmSound.sharedInstance().cleanup()
-// super.navleftViewTapped()
-// }
- }
- else{
- super.navleftViewTapped()
- }
+ self.sdk.stop()
+ super.navleftViewTapped()
+// if toolBar.recordBtn.isSelected {
+// self.sdk.stop()
+//// CustomAlert.showWith(title: "提示".l10n, messgage: "当前正在录音,确定放弃当前录音吗".l10n, cancelCallback: {}) {
+//// self.sdk.stop()
+////// PcmSound.sharedInstance().stopAudioUnit()
+////// PcmSound.sharedInstance().cleanup()
+//// super.navleftViewTapped()
+//// }
+// }
+// else{
+// super.navleftViewTapped()
+// }
}
override func navRightViewTapped() {
let tip = RecordTipsView(type: .chinese2english)
#import <zlib.h>
#import "JLBleAssistManager.h"
#import "OpusTool.h"
+#import "OpusManager.h"
}
}
override func setupData() {
-// JoyGemChatManager.shared().getSpeakerList {[weak self] speakers in
-// guard let self = self else{return}
-// self.list = speakers
-// self.tableView.reloadData()
-// }
+
}
}