From: liangzhenying <867174124@qq.com> Date: Thu, 18 Dec 2025 10:15:19 +0000 (+0800) Subject: 骨传导耳机AI修改 X-Git-Tag: 1.0.2~32 X-Git-Url: http://git.snicker.com.cn/?a=commitdiff_plain;h=1a5ddeafcd377721d50a31c0b9ed455b8c5495c1;p=mirrors%2FShenYin.git 骨传导耳机AI修改 --- diff --git a/DeepChordsSdk_iOS/DeepChordsSdk/Example/DeepChordsSdk/DCBleManager.swift b/DeepChordsSdk_iOS/DeepChordsSdk/Example/DeepChordsSdk/DCBleManager.swift index 4cd4c3d..e02c3e5 100644 --- a/DeepChordsSdk_iOS/DeepChordsSdk/Example/DeepChordsSdk/DCBleManager.swift +++ b/DeepChordsSdk_iOS/DeepChordsSdk/Example/DeepChordsSdk/DCBleManager.swift @@ -70,20 +70,56 @@ public extension DCBleManager{ func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) { DCLogger.debug("收 ",characteristic.uuid,characteristic.value?.joined() ?? "") guard let data = characteristic.value else { return } - if data.count < 4 { - return + if data.count > 4 && data[0] == 0xBA { + if let sender = BleSender.answer(data) { + sender.completion?(.response.code(.success).value(data)) + sender.deinitTimer() + sender.remove() + return + } + else if data[1] == 0x0D{ + let subData = data.subdata(in: 2..Data{ + if data.count > 4 && data[0] == 0xBA { + return data.subdata(in: 2..Void)? + /// 识别回调 + public var translateCallback:((String)->Void)? + /// 状态码回调 + public var codeCallback:((Int)->Void)? + /// 录音文件回调 + public var filePathCallBack:((String?)->Void)? - public func setup(textCallback:@escaping (String)->Void){ - let asr = StarburstAsrConfig() -// asr.isTranslate = true; -// asr.fromLanguage = "zh-CN" -// asr.toLanguage = "en-US" -// asr.isTTs = true -// asr.ttsFormat = "mp3" - StarburstSdk.shared().setupDeviceRecord(with: nil, asrConfig: asr) { config in + var asrList : [(String,Int)] = [] + var asrText = "" + + var translateList : [(String,Int)] = [] + var translateText = "" + + func setupRecord(){ + let config = StarburstAsrConfig() + config.isTranslate = isTranslate + config.fromLanguage = fromLanguage + config.toLanguage = toLanguage + config.isTTs = isTTs + config.ttsFormat = ttsFormat + + 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} if let text = model.textResult{ - DCLogger.debug("识别 = \(text)") - let msg = recordList.firstIndex (where: { (str,idx) in + DCLogger.debug("asr = \(text)") + let msg = asrList.firstIndex (where: { (str,idx) in return idx == model.definiteIndex; }) if (msg == nil) { - recordList.append((model.textResult,model.definiteIndex)) + 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 let translate = model.translateResult,let text = translate.text{ + DCLogger.debug("translate = \(text)") + let msg = translateList.firstIndex (where: { (str,idx) in + return idx == translate.definiteIndex; + }) + if (msg == nil) { + translateList.append((text,Int(translate.definiteIndex))) } else { - recordList[msg!] = (model.textResult,model.definiteIndex) + translateList[msg!] = (text,Int(translate.definiteIndex)) } - recordText = recordList.compactMap({ (str,idx) in + translateText = translateList.compactMap({ (str,idx) in return str }).joined(separator: "\n") - textCallback(recordText) + translateCallback?(translateText) } } stateChange: { [weak self] code in - guard let self = self else {return} - DCLogger.debug("状态改变 \(code)") - } finished: { filePath in - if let path = filePath,path.count > 0 { - // 文件转存到自己想要保存的位置 - DCLogger.debug("文件录音保存路径 \(path)") - } - else{ - DCLogger.debug("文件录音保存失败") - } + DCLogger.debug("状态改变",code) + self?.codeCallback?(code.rawValue) + } finished: {[weak self] filePath in + DCLogger.debug("文件录音保存路径 \(filePath ?? "")") + self?.filePathCallBack?(filePath) } } - public func startAction(){ + public func start(){ + asrList = [] + asrText = "" + + translateList = [] + translateText = "" + + setupRecord() let config = StarburstBleAsrConfig() config.type = 0x0c/// 0x00实时录音,0x0c通话录音 //开始录音 StarburstSdk.shared().startDeviceRecord(config) } - public func stopAction(){ + public func stop(){ StarburstSdk.shared().stopDeviceRecord() } } diff --git a/DeepChordsSdk_iOS/DeepChordsSdk/Example/DeepChordsSdk/DCChat.swift b/DeepChordsSdk_iOS/DeepChordsSdk/Example/DeepChordsSdk/DCChat.swift index 2d1d4cb..adf6117 100644 --- a/DeepChordsSdk_iOS/DeepChordsSdk/Example/DeepChordsSdk/DCChat.swift +++ b/DeepChordsSdk_iOS/DeepChordsSdk/Example/DeepChordsSdk/DCChat.swift @@ -7,106 +7,108 @@ import StarburstSdk -/// Ai回应模型 -public class ChatCellModel:NSObject,NSCoding { - public var type: Int = 0//1发,0收 - public var content: String = "" - public var id:Int = 0 - - required public override init() { } - - public func encode(with aCoder: NSCoder) { - aCoder.encode("\(type)", forKey: "type") - aCoder.encode(content, forKey: "content") - aCoder.encode("\(id)", forKey: "id") - } - - required public init?(coder aDecoder: NSCoder) { - type = Int(aDecoder.decodeObject(forKey: "type") as? String ?? "0") ?? 0 - content = aDecoder.decodeObject(forKey: "content") as? String ?? "" - id = Int(aDecoder.decodeObject(forKey: "id") as? String ?? "0") ?? 0 - } +public class DCRole{ + /// 音色id + public var sid:String = "" + /// 名称 + public var name:String = "" + /// 类型 + public var type:String = "" + /// 角色 + public var role:String = "" } - public class DCChat{ public static let shared = DCChat() - var speakerList = [StarbusrtSpeaker]() - - - var dataList:[ChatCellModel] = [] - public var chatStateChange:((Int)->Void)? - public var chatNeedReloadData:(()->Void)? + var speakerList = [DCRole]() public var dialogId = 0 + /// 识别回调 + public var asrCallback:((String,Int)->Void)? + /// 回复回调 + public var responseCallback:((String,Int)->Void)? + /// 对话语音回调 + public var dataCallback:((Int,Data?,Bool)->Void)? + /// 状态码回调 + public var codeCallback:((Int)->Void)? + /// 结束对话回调 + public var finishCallback:((String?)->Void)? - public func getVcSpeekerList(finished:@escaping (Any,Bool)->Void){ + /// 智能体 + public var role:DCRole? + /// 是否进⾏Tts,默认false + public var tts:Bool = true + /// 默认pcm。⽀持pcm / ogg_opus / mp3 + public var ttsAudioFormat:String = "mp3" + /// 语⾔编码,默认为普通话(zh-CN) + public var language:String = "" +} +extension DCChat{ + public func getVcSpeekerList(finished:@escaping ([DCRole]?,Bool)->Void){ StarburstSdk.shared().getVcSpeekerList { [weak self] (list, success) in guard let self = self else { return } - finished(list,success) + if success,let list = list { + var temp = [DCRole]() + for item in list { + var role = DCRole() + role.sid = item.sid + role.name = item.name + role.type = item.type + role.role = item.role + temp.append(role) + } + finished(temp,success) + } + else{ + finished(nil,success) + } + } } - public func setup(asrCallback:@escaping (String)->Void,responseCallback:@escaping (String)->Void,dataCallback:@escaping (Int,Data?,Bool)->Void,codeCallback:@escaping (Int)->Void){ + public func initChat(){ let config = StarburstVoiceChatConfig() - config.tts = true - config.ttsAudioFormat = "mp3" - //以下配置从音色列表获取 - if speakerList.count > 0 { + config.tts = tts + config.ttsAudioFormat = ttsAudioFormat + if let speaker = role { // config.role = "spongebob" // config.ttsSpeakerType = "default" // config.ttsSpeakerId = "BV063_streaming" - config.role = speakerList[0].role - config.ttsSpeakerType = speakerList[0].type - config.ttsSpeakerId = speakerList[0].sid + config.role = speaker.role + config.ttsSpeakerType = speaker.type + config.ttsSpeakerId = speaker.sid } StarburstSdk.shared().setupVoiceChat(with: config) { dialogId in - print("开始对话 = \(dialogId)") + DCLogger.debug("开始对话 = \(dialogId)") } textCallback: {[weak self] model in guard let self = self else { return } guard let textModel = model else { return } self.dialogId = textModel.dialogId - + DCLogger.debug("[recv] = \(textModel.dialogId),\(textModel.conversationFinish),\(textModel.sequence),\(textModel.textResult),\(textModel.responseText)") if let text = textModel.textResult{ - asrCallback(text) + //DCLogger.debug("[send] = \(textModel.sequence),\(text)") + asrCallback?(text,self.dialogId) } else if let text = textModel.responseText{ - responseCallback(text) + //DCLogger.debug("[recv] = \(textModel.sequence),\(text)") + responseCallback?(text,self.dialogId) } -// Logger.debug(text,index) -// guard let text = text else{ -// return -// } - -// if dataList.last?.id == -1 { -// dataList.removeLast() -// } -// if let model = dataList.last,model.id == textModel.definiteIndex,model.type == 1,let text = textModel.textResult { -// model.content = text -// } -// else{ -// let model = ChatCellModel() -// model.type = 1 -// model.content = textModel.textResult ?? "" -// model.id = textModel.definiteIndex -// dataList.append(model) -// } -// chatNeedReloadData?() - } responseVoice: { [weak self] (dialogId,data, finish) in guard let self = self else { return } self.dialogId = dialogId - dataCallback(dialogId,data,finish) - } stateChange: {code in + dataCallback?(dialogId,data,finish) + } stateChange: {[weak self] code in DCLogger.debug("状态改变 \(code)") - codeCallback(code.rawValue) - } finished: { filePath in + self?.codeCallback?(code.rawValue) + } finished: {[weak self] filePath in DCLogger.debug("结束对话") + self?.finishCallback?(filePath) } } + public func startChat(){ - + initChat() } public func stopChat(){ StarburstSdk.shared().stopVoiceChat(self.dialogId) diff --git a/DeepChordsSdk_iOS/DeepChordsSdk/Example/DeepChordsSdk/DCDevice.swift b/DeepChordsSdk_iOS/DeepChordsSdk/Example/DeepChordsSdk/DCDevice.swift index 74bbf2d..4300bea 100644 --- a/DeepChordsSdk_iOS/DeepChordsSdk/Example/DeepChordsSdk/DCDevice.swift +++ b/DeepChordsSdk_iOS/DeepChordsSdk/Example/DeepChordsSdk/DCDevice.swift @@ -10,12 +10,28 @@ import Foundation public class DCDevice{ public static let shared = DCDevice() + func receiveNotice(_ data:Data){ + if data[1] == 0x10,data[2] == 0x06{ + let index = UInt16(data[3...4].int) + let pro = UInt16(data[5...6].int) + let total = UInt16(data[7...8].int) + DCLogger.debug("播放进度index = \(index),total = \(total),pro = \(pro)") + } + } +} + extension DCDevice{ public func getBatery(finished:@escaping ((Int,Int)?)->Void){ - var data = Data.dataWithHexString("BA0900") - data.addCrc() - + var head: UInt8 = 0xBA + var cmd: UInt8 = 0x09 + var data:UInt8 = 00 + var total = Data() + total.append(&head, count: 1) + total.append(&cmd, count: 1) + total.append(&data, count: 1) + total.addCrc() + let sender = BleSender() - sender.data = data + sender.data = total sender.completion = { result in DCLogger.debug("\(result.description)") if result.isSuccess{ @@ -31,17 +47,23 @@ public class DCDevice{ DCBleManager.shared.send(sender) } public func getSoftVersion(finished:@escaping (String?)->Void){ - var data = Data.dataWithHexString("BA0A00") - data.addCrc() + var head: UInt8 = 0xBA + var cmd: UInt8 = 0x0A + var data:UInt8 = 00 + var total = Data() + total.append(&head, count: 1) + total.append(&cmd, count: 1) + total.append(&data, count: 1) + total.addCrc() let sender = BleSender() - sender.data = data + sender.data = total sender.completion = { result in DCLogger.debug("\(result.description)") if result.isSuccess{ let data = result.data[2...6] let text = String(data: data, encoding:.utf8) - DCLogger.debug(text) + DCLogger.debug(text ?? "") finished((text)) } else{ @@ -51,17 +73,23 @@ public class DCDevice{ DCBleManager.shared.send(sender) } public func getHardVersion(finished:@escaping (String?)->Void){ - var data = Data.dataWithHexString("BA0A01") - data.addCrc() + var head: UInt8 = 0xBA + var cmd: UInt8 = 0x0A + var data:UInt8 = 01 + var total = Data() + total.append(&head, count: 1) + total.append(&cmd, count: 1) + total.append(&data, count: 1) + total.addCrc() let sender = BleSender() - sender.data = data + sender.data = total sender.completion = { result in DCLogger.debug("\(result.description)") if result.isSuccess{ let data = result.data[2...6] let text = String(data: data, encoding:.utf8) - DCLogger.debug(text) + DCLogger.debug(text ?? "") finished((text)) } else{ @@ -70,4 +98,330 @@ public class DCDevice{ } DCBleManager.shared.send(sender) } + + /// state 0关闭,1打开 + public func findDevice(state:UInt8,finished:@escaping (Bool)->Void){ + var head: UInt8 = 0xBA + var cmd: UInt8 = 0x0B + var data:UInt8 = state + var total = Data() + total.append(&head, count: 1) + total.append(&cmd, count: 1) + total.append(&data, count: 1) + total.addCrc() + + let sender = BleSender() + sender.data = total + sender.completion = { result in + DCLogger.debug("\(result.description)") + if result.isSuccess{ + let value = result.data[2].int + DCLogger.debug(value) + finished(true) + } + else{ + finished(false) + } + } + DCBleManager.shared.send(sender) + } + + /// type, 0x00(正常模式) + // 0x01(流行) + // 0x02(经典) + // 0x03(爵士) + // 0x04(摇滚) + // 0x05(自定义) + public func setEQ(type:UInt8,finished:@escaping (Bool)->Void){ + var head: UInt8 = 0xBA + var cmd: UInt8 = 0x0C + var data:UInt8 = type + var total = Data() + total.append(&head, count: 1) + total.append(&cmd, count: 1) + total.append(&data, count: 1) + total.addCrc() + + let sender = BleSender() + sender.data = total + sender.completion = { result in + DCLogger.debug("\(result.description)") + if result.isSuccess{ + let value = result.data[2].int + DCLogger.debug(value) + finished(value == 0) + } + else{ + finished(false) + } + } + DCBleManager.shared.send(sender) + } + /// vaue,只有自定义才有,索引 0-9(对应10段EQ调节)(-12~12) + public func setCustomEQ(index:UInt8,value:Int,finished:@escaping (Bool)->Void){ + var head: UInt8 = 0xBA + var cmd: UInt8 = 0x0C + var data:UInt8 = 0x06 + var index:UInt8 = UInt8(index) + var value:UInt8 = UInt8(value) + 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.addCrc() + + let sender = BleSender() + sender.data = total + sender.completion = { result in + DCLogger.debug("\(result.description)") + if result.isSuccess{ + let value = result.data[2].int + DCLogger.debug(value) + finished(value == 0) + } + else{ + finished(false) + } + } + DCBleManager.shared.send(sender) + } + public func getEQ(finished:@escaping ((type:UInt8,value:[Int])?,Bool)->Void){ + var head: UInt8 = 0xBA + var cmd: UInt8 = 0x0E + var data:UInt8 = 0x00 + var total = Data() + total.append(&head, count: 1) + total.append(&cmd, count: 1) + total.append(&data, count: 1) + total.addCrc() + + let sender = BleSender() + sender.data = total + sender.completion = { result in + DCLogger.debug("\(result.description)") + if result.isSuccess{ + let type = UInt8(result.data[2].int) + DCLogger.debug(type) + if type == 5 { + + } + finished((type,[]),true) + } + else{ + finished(nil,false) + } + } + DCBleManager.shared.send(sender) + } +} + +extension DCDevice{ + public func getMusicList(size:UInt8,index:UInt16,finished:@escaping ([DCMusic]?,Bool)->Void){ + var head: UInt8 = 0xBA + var cmd: UInt8 = 0x10 + var data:UInt8 = 0x01 + var pageSize = size + var total = Data() + total.append(&head, count: 1) + total.append(&cmd, count: 1) + total.append(&data, count: 1) + total.append(&pageSize, count: 1) + total.appendInt16(index) + total.addCrc() + + let sender = BleSender() + sender.data = total + sender.completion = { result in + DCLogger.debug("\(result.description)") + if result.isSuccess{ +// let data = DCBleManager.contentData(result.data) + let data = result.data.subdata(in: 3..Void){ + var head: UInt8 = 0xBA + var cmd: UInt8 = 0x10 + var data:UInt8 = 0x02 + var mode:UInt8 = mode + var total = Data() + total.append(&head, count: 1) + total.append(&cmd, count: 1) + total.append(&data, count: 1) + total.append(&mode, count: 1) + total.addCrc() + + let sender = BleSender() + sender.data = total + sender.completion = { result in + DCLogger.debug("\(result.description)") + if result.isSuccess{ + let value = result.data[2].int + DCLogger.debug(value) + finished(value == 0) + } + else{ + finished(false) + } + } + DCBleManager.shared.send(sender) + } + public func getMusicPlayMode(finished:@escaping (UInt8?,Bool)->Void){ + var head: UInt8 = 0xBA + var cmd: UInt8 = 0x10 + var data:UInt8 = 0x03 + var content:UInt8 = 0x00 + var total = Data() + total.append(&head, count: 1) + total.append(&cmd, count: 1) + total.append(&data, count: 1) + total.append(&content, count: 1) + total.addCrc() + + let sender = BleSender() + sender.data = total + sender.completion = { result in + DCLogger.debug("\(result.description)") + if result.isSuccess{ + let value = UInt8(result.data[3].int) + DCLogger.debug(value) + finished(value,true) + } + else{ + finished(nil,false) + } + } + DCBleManager.shared.send(sender) + } + + public func musicPlay(index:UInt16?,finished:@escaping (UInt16?,Bool)->Void){ + if let index = index { + var head: UInt8 = 0xBA + var cmd: UInt8 = 0x10 + var data:UInt8 = 0x05 + + var total = Data() + total.append(&head, count: 1) + total.append(&cmd, count: 1) + total.append(&data, count: 1) + total.appendInt16(index) + total.addCrc() + + let sender = BleSender() + sender.data = total + sender.completion = { result in + DCLogger.debug("\(result.description)") + if result.isSuccess{ + let value = UInt16(result.data[3...4].int) + DCLogger.debug(value) + finished(value,true) + } + else{ + finished(nil,false) + } + } + DCBleManager.shared.send(sender) + } + else{ + var head: UInt8 = 0xBA + var cmd: UInt8 = 0x10 + var data:UInt8 = 0x04 + var content:UInt8 = 0x00 + + var total = Data() + total.append(&head, count: 1) + total.append(&cmd, count: 1) + total.append(&data, count: 1) + total.append(&content, count: 1) + total.addCrc() + + let sender = BleSender() + sender.data = total + sender.completion = { result in + DCLogger.debug("\(result.description)") + if result.isSuccess{ + let value = UInt16(result.data[3...4].int) + DCLogger.debug(value) + finished(value,true) + } + else{ + finished(nil,false) + } + } + DCBleManager.shared.send(sender) + } + } + public func musicPause(finished:@escaping (UInt16?,Bool)->Void){ + var head: UInt8 = 0xBA + var cmd: UInt8 = 0x10 + var data:UInt8 = 0x04 + var content:UInt8 = 0x01 + var total = Data() + total.append(&head, count: 1) + total.append(&cmd, count: 1) + total.append(&data, count: 1) + total.append(&content, count: 1) + total.addCrc() + + let sender = BleSender() + sender.data = total + sender.completion = { result in + DCLogger.debug("\(result.description)") + if result.isSuccess{ + let value = UInt16(result.data[3...4].int) + DCLogger.debug(value) + finished(value,true) + } + else{ + finished(nil,false) + } + } + DCBleManager.shared.send(sender) + } + public func musicDelete(list:[UInt16],finished:@escaping (Bool)->Void){ + var head: UInt8 = 0xBA + var cmd: UInt8 = 0x10 + var data:UInt8 = 0x07 + var content:UInt8 = UInt8(list.count) + var total = Data() + total.append(&head, count: 1) + total.append(&cmd, count: 1) + total.append(&data, count: 1) + total.append(&content, count: 1) + for item in list { + total.appendInt16(item) + } + total.addCrc() + + let sender = BleSender() + sender.data = total + sender.completion = { result in + DCLogger.debug("\(result.description)") + if result.isSuccess{ + let value = UInt16(result.data[3].int) + DCLogger.debug(value) + finished(value == 0) + } + else{ + finished(false) + } + } + DCBleManager.shared.send(sender) + } + } diff --git a/DeepChordsSdk_iOS/DeepChordsSdk/Example/DeepChordsSdk/DCMusic.swift b/DeepChordsSdk_iOS/DeepChordsSdk/Example/DeepChordsSdk/DCMusic.swift new file mode 100644 index 0000000..e6d8925 --- /dev/null +++ b/DeepChordsSdk_iOS/DeepChordsSdk/Example/DeepChordsSdk/DCMusic.swift @@ -0,0 +1,17 @@ +// +// DCMusic.swift +// Example +// +// Created by liangshi on 2025/12/17. +// + +public class DCMusic:Codable{ + /// id + public var id = "" + /// 名字 + public var name = "" + /// 大小 + public var size = "" + /// 时长 + public var dur = 0 +} diff --git a/DeepChordsSdk_iOS/DeepChordsSdk/Example/DeepChordsSdk/DCRecord.swift b/DeepChordsSdk_iOS/DeepChordsSdk/Example/DeepChordsSdk/DCRecord.swift index 2fc6c5f..b931197 100644 --- a/DeepChordsSdk_iOS/DeepChordsSdk/Example/DeepChordsSdk/DCRecord.swift +++ b/DeepChordsSdk_iOS/DeepChordsSdk/Example/DeepChordsSdk/DCRecord.swift @@ -8,72 +8,95 @@ import StarburstSdk public class DCRecord { public static let shared = DCRecord() - var recordList : [(String,Int)] = [] - var recordText = "" + /// 语⾔编码,默认为普通话(zh-CN),auto_lang自动识别 + public var fromLanguage:String = "" + /// ⽬标语⾔编码,默认为英语(en-US),auto_lang自动识别 + public var toLanguage:String = "" + /// 是否翻译,默认false。如果为true翻译源语言和目标语言不能是同一种语言 + public var isTranslate = false + /// 是否进⾏Tts,默认false + public var isTTs = false + /// 默认pcm ⽀持pcm / ogg_opus / mp3 + public var ttsFormat = "mp3" + /// 识别回调 + public var asrCallback:((String)->Void)? + /// 识别回调 + public var translateCallback:((String)->Void)? + /// 状态码回调 + public var codeCallback:((Int)->Void)? + /// 打断 + public var interruptionCallBack:(()->Void)? + /// 录音文件回调 + public var filePathCallBack:((String?)->Void)? - public func start(fromLanguage:String,toLanguage:String,textCallback:@escaping (String)->Void){ + var asrList : [(String,Int)] = [] + var asrText = "" + + var translateList : [(String,Int)] = [] + var translateText = "" + + public func start(){ let config = StarburstAsrConfig() -// config.isTranslate = true; -// config.fromLanguage = fromLanguage -// config.toLanguage = toLanguage -// config.isTTs = true -// config.ttsFormat = "mp3" + config.isTranslate = isTranslate + config.fromLanguage = fromLanguage + config.toLanguage = toLanguage + config.isTTs = isTTs + config.ttsFormat = ttsFormat + asrList = [] + asrText = "" + + translateList = [] + translateText = "" StarburstSdk.shared().startRecordAsr(config) {voiceData in - //print("录音的音频流 = \(voiceData!)") + //DCLogger.debug("录音的音频流 = \(voiceData!)") } recognizeText: {[weak self] model in guard let self = self else {return} guard let model = model else {return} if let text = model.textResult{ - DCLogger.debug("识别 = \(text)") - let msg = recordList.firstIndex (where: { (str,idx) in + DCLogger.debug("asr = \(text)") + let msg = asrList.firstIndex (where: { (str,idx) in return idx == model.definiteIndex; }) if (msg == nil) { - recordList.append((model.textResult,model.definiteIndex)) + asrList.append((text,model.definiteIndex)) } else { - recordList[msg!] = (model.textResult,model.definiteIndex) + asrList[msg!] = (text,model.definiteIndex) } - recordText = recordList.compactMap({ (str,idx) in + asrText = asrList.compactMap({ (str,idx) in return str }).joined(separator: "\n") - textCallback(recordText) + asrCallback?(asrText) + } + if let translate = model.translateResult,let text = translate.text{ + DCLogger.debug("translate = \(text)") + let msg = translateList.firstIndex (where: { (str,idx) in + return idx == translate.definiteIndex; + }) + if (msg == nil) { + translateList.append((text,Int(translate.definiteIndex))) + } else { + translateList[msg!] = (text,Int(translate.definiteIndex)) + } + translateText = translateList.compactMap({ (str,idx) in + return str + }).joined(separator: "\n") + + translateCallback?(translateText) } - -// guard let self = self else {return} -// self.resetTimer() -// guard let model = model else {return} -// -// if let translate = model.translateResult,let text = translate.text,translate.definite == true { -// print("确定 text = \(text)") -// self.addText(text) -// } -// if let ttsData = model.ttsResult?.streamPiece { -// //收到的tts -// if self.ttsData == nil{ -// self.ttsData = Data() -// } -// if let data = Data(base64Encoded: ttsData){ -// self.ttsData?.append(data) -// } -// } } stateChange: {[weak self] code in DCLogger.debug("状态改变",code) -// guard let self = self else {return} -// if code == .unAuth{ -// self.view.makeToast("未鉴权") -// self.recordBtn.isSelected = false -// } + self?.codeCallback?(code.rawValue) } interruption: { [weak self] in -// self?.stopAction() - + self?.interruptionCallBack?() } } public func stop(){ - StarburstSdk.shared().stopRecordAsr {filePath in - DCLogger.debug("文件录音保存路径 \(filePath)") + StarburstSdk.shared().stopRecordAsr {[weak self] filePath in + DCLogger.debug("文件录音保存路径 \(filePath ?? "")") + self?.filePathCallBack?(filePath) } } diff --git a/DeepChordsSdk_iOS/DeepChordsSdk/Example/DeepChordsSdk/Data.swift b/DeepChordsSdk_iOS/DeepChordsSdk/Example/DeepChordsSdk/Data.swift index e3a160c..d59dcb4 100644 --- a/DeepChordsSdk_iOS/DeepChordsSdk/Example/DeepChordsSdk/Data.swift +++ b/DeepChordsSdk_iOS/DeepChordsSdk/Example/DeepChordsSdk/Data.swift @@ -45,8 +45,8 @@ extension Data { } mutating func addCrc(){ - var len = UInt8(self.count - 2) - self.append(&len, count: 1) + var len = UInt16(self.count - 2) + self.appendInt16(len) var crc = Data.crc8(self) self.append(&crc, count: 1) } @@ -56,6 +56,14 @@ extension Data { // var crc = chargestore_crc8(list, len: list.count) // self.append(&crc, count: 1) // } + + mutating func appendInt16(_ value:UInt16){ + var byte1 = UInt8(value & 0xFF) + var byte2 = UInt8((value >> 8) & 0xFF) + self.append(&byte1, count: 1) + self.append(&byte2, count: 1) + + } } @@ -80,9 +88,18 @@ func chargestore_crc8(_ ptr: UnsafePointer, len: Int) -> UInt8 { return crc } -//// 调用示例 -//let data: [UInt8] = [0x01, 0x02, 0x03] -//let crcResult = data.withUnsafeBufferPointer { buffer in -// chargestore_crc8(buffer.baseAddress!, len: buffer.count) -//} -//print("CRC8 结果: \(crcResult)") +class JSON { + /// JSON decode + static func decode(_ decodable: T.Type, from data: Data) -> T? { + do { + return try JSONDecoder().decode(decodable.self, from: data) + } catch { } + return nil + } + + /// JSON decode + static func decode(_ decodable: T.Type, from jsonString: String) -> T? { + guard let data = jsonString.data(using: .utf8) else { return nil } + return decode(decodable, from: data) + } +} diff --git a/DeepChordsSdk_iOS/DeepChordsSdk/Example/DeepChordsSdk/DeepChordsAiSdk.swift b/DeepChordsSdk_iOS/DeepChordsSdk/Example/DeepChordsSdk/DeepChordsAiSdk.swift index eb5c089..8ef5819 100644 --- a/DeepChordsSdk_iOS/DeepChordsSdk/Example/DeepChordsSdk/DeepChordsAiSdk.swift +++ b/DeepChordsSdk_iOS/DeepChordsSdk/Example/DeepChordsSdk/DeepChordsAiSdk.swift @@ -29,7 +29,7 @@ public class DeepChordsAiSdk{ //debug模式会打印日志 - StarburstConfig.shared().isDebug = true + StarburstConfig.shared().isDebug = false // sdk缓存路径 StarburstConfig.shared().sdkFolder = NSHomeDirectory() + "/Documents/DeepChordsSdk" // 日志路径 diff --git a/DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/VC/BaseVC.swift b/DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/VC/BaseVC.swift index e37c695..3b4e8c0 100644 --- a/DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/VC/BaseVC.swift +++ b/DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/VC/BaseVC.swift @@ -29,7 +29,7 @@ class BaseVC: UIViewController { sdklogBtn.backgroundColor = .blue sdklogBtn.layer.cornerRadius = 30 sdklogBtn.layer.masksToBounds = true - sdklogBtn.setTitle("log sdk", for: .normal) + sdklogBtn.setTitle("sdk log", for: .normal) sdklogBtn.addTarget(self, action: #selector(sdkAction), for: .touchUpInside) self.view.addSubview(sdklogBtn) } diff --git a/DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/VC/BleSdkBleChatVC.swift b/DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/VC/BleSdkBleChatVC.swift deleted file mode 100644 index 6aadc88..0000000 --- a/DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/VC/BleSdkBleChatVC.swift +++ /dev/null @@ -1,76 +0,0 @@ -// -// Untitled.swift -// Example -// -// Created by liangshi on 2025/12/15. -// - -import UIKit -import DeepChordsSdk -import AVFoundation -class BleSdkBleChatVC: BaseVC { - var recordBtn = UIButton(type: .custom) - private var textView = UITextView() - - - deinit { - stopAction() - } - - override func viewDidLoad() { - super.viewDidLoad() - setupViews() - setupSDk() - } - - func setupViews() { - self.title = "耳机对话" - - textView.isEditable = false - self.view.addSubview(textView) - textView.snp.makeConstraints { make in - make.edges.equalToSuperview() - } - - recordBtn.backgroundColor = .blue - recordBtn.setTitle("stop", for: .normal) - recordBtn.setTitle("stop", for: .selected) - recordBtn.addTarget(self, action: #selector(recordAction), for: .touchUpInside) - recordBtn.frame = CGRectMake(0, 0, 80, 40) - self.navigationItem.rightBarButtonItems = [UIBarButtonItem(customView: recordBtn)] - } - func setupSDk(){ - ChatManager.shared.asrCallBack = {[weak self] text in - guard let self = self else{return} - let text = "[sebd]" + text + "\n\n" - self.textView.text = self.textView.text + text - let range = NSRange( - location: self.textView.text.count - 1, length: 1) - self.textView.scrollRangeToVisible(range) - } - ChatManager.shared.reponseCallBack = {[weak self] text in - guard let self = self else{return} - let text = "[recieve]" + text + "\n\n" - self.textView.text = self.textView.text + text - let range = NSRange( - location: self.textView.text.count - 1, length: 1) - self.textView.scrollRangeToVisible(range) - } - } - @objc func recordAction() { -// self.recordBtn.isSelected = !self.recordBtn.isSelected -// if self.recordBtn.isSelected { -// DCChat.shared.startChat() -// } -// else{ - stopAction() -// } - } - func stopAction() { - DCChat.shared.stopChat() - } - - - -} - diff --git a/DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/VC/BleSdkEarPhonesAsrVC.swift b/DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/VC/BleSdkEarPhonesAsrVC.swift deleted file mode 100644 index d371c46..0000000 --- a/DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/VC/BleSdkEarPhonesAsrVC.swift +++ /dev/null @@ -1,84 +0,0 @@ -// -// Untitled.swift -// Example -// -// Created by liangshi on 2025/12/9. -// - -import UIKit -import DeepChordsSdk -class BleSdkEarPhonesAsrVC: BaseVC { - var textView = UITextView() - var recordBtn = UIButton(type: .custom) - var preText = "" - - override func viewDidLoad() { - super.viewDidLoad() - setupViews() - setupSdk() - } - - override func viewWillDisappear(_ animated: Bool) { - super.viewWillDisappear(animated) - stopAction() - } - - func setupViews(){ - title = "耳机录音" - view.backgroundColor = .white - - recordBtn.backgroundColor = .blue - recordBtn.setTitle("start", for: .normal) - recordBtn.setTitle("stop", for: .selected) - recordBtn.addTarget(self, action: #selector(recordAction), for: .touchUpInside) - recordBtn.frame = CGRectMake(0, 0, 80, 40) - self.navigationItem.rightBarButtonItem = UIBarButtonItem(customView: recordBtn) - - textView.isEditable = false - view.addSubview(textView) - textView.frame = CGRectMake(0, 100, view.bounds.size.width, view.bounds.size.height - 100) - } - func setupSdk(){ - //设置回调 - DCBleRecord.shared.setup {[weak self] text in - guard let self = self else{return} - - self.textView.text = text - let range = NSMakeRange(textView.text.count - 1, 1) - self.textView.scrollRangeToVisible(range) - } - } - - @objc func recordAction() { - self.recordBtn.isSelected = !self.recordBtn.isSelected - if self.recordBtn.isSelected { - startAction() - } - else{ - stopAction() - } - } -} -extension BleSdkEarPhonesAsrVC{ - func startAction(){ - DCBleRecord.shared.startAction() - - } - func stopAction(){ - DCBleRecord.shared.stopAction() - } -} - -extension BleSdkEarPhonesAsrVC{ - func addText(_ text:String){ - self.textView.text += text - let range = NSMakeRange(textView.text.count - 1, 1) - self.textView.scrollRangeToVisible(range) - } - func removePreText(){ - if self.textView.text.count - self.preText.count > 0{ - self.textView.text = String(self.textView.text.prefix(self.textView.text.count - self.preText.count)) - self.preText = "" - } - } -} diff --git a/DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/VC/BleSdkPhoneAsrVC.swift b/DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/VC/BleSdkPhoneAsrVC.swift deleted file mode 100644 index 41cbaac..0000000 --- a/DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/VC/BleSdkPhoneAsrVC.swift +++ /dev/null @@ -1,173 +0,0 @@ -// -// Ble.swift -// Example -// -// Created by liangshi on 2025/12/9. -// - -import UIKit -import DeepChordsSdk - -class BleSdkPhoneAsrVC: BaseVC { - var textView = UITextView() - var recordBtn = UIButton(type: .custom) - var preText = "" - var ttsData:Data? - var timer: Timer? - - var fromLanguage = "zh-CN" - var toLanguage = "en-US" - var fromItem:UIBarButtonItem! - var toItem:UIBarButtonItem! - - override func viewDidLoad() { - super.viewDidLoad() - title = "手机录音" - view.backgroundColor = .white - -// fromItem = UIBarButtonItem(title: "源语言", style: .done, target: self, action: #selector(fromLanguageAction)) -// toItem = UIBarButtonItem(title: "目标语言", style: .done, target: self, action: #selector(toLanguageAction)) -// self.navigationItem.rightBarButtonItems = [ -// fromItem,toItem -// ]; -// - recordBtn.backgroundColor = .blue - recordBtn.setTitle("开始录音", for: .normal) - recordBtn.setTitle("停止录音", for: .selected) - recordBtn.addTarget(self, action: #selector(recordAction), for: .touchUpInside) - view.addSubview(recordBtn) - recordBtn.snp.makeConstraints { make in - make.width.centerX.bottom.equalToSuperview() - make.height.equalTo(80) - } - - textView.isEditable = false - view.addSubview(textView) - textView.snp.makeConstraints { make in - make.width.centerX.equalToSuperview() - make.top.equalToSuperview().inset(120) - make.bottom.equalTo(recordBtn.snp.top) - } - } - - - - override func viewWillDisappear(_ animated: Bool) { - super.viewWillDisappear(animated) - stopAction() - } - - func setupSdk(){ - - } - - @objc func fromLanguageAction(){ - let vc = SelectLanuageVC() - vc.didSelectedCallback = { (name,value) in - self.fromLanguage = value - self.fromItem.title = name - } - navigationController?.pushViewController(vc, animated: true) - } - @objc func toLanguageAction(){ - let vc = SelectLanuageVC() - vc.didSelectedCallback = { (name,value) in - self.toLanguage = value - self.toItem.title = name - } - navigationController?.pushViewController(vc, animated: true) - } - - @objc func recordAction() { - self.recordBtn.isSelected = !self.recordBtn.isSelected - if self.recordBtn.isSelected { - startAction() - } - else{ - stopAction() - } - } -} - -extension BleSdkPhoneAsrVC{ - func startAction(){ - DCRecord.shared.start(fromLanguage: fromLanguage, toLanguage: toLanguage, textCallback: { [weak self] text in - guard let self = self else{return} - - self.textView.text = text - let range = NSMakeRange(textView.text.count - 1, 1) - self.textView.scrollRangeToVisible(range) - }) - } - - func stopAction(){ -// StarburstSdk.shared().stopRecordAsr {filePath in -// if let path = filePath,path.count > 0 { -// // 文件转存到自己想要保存的位置 -// print("文件录音保存路径 \(path)") -// } -// else{ -// print("文件录音保存失败") -// } -// } -// self.invalidateTimer() - DCRecord.shared.stop() - } - - func resetTimer() { - self.invalidateTimer() - self.timer = Timer.scheduledTimer(timeInterval: 3, target: self, selector: #selector(timerFired), userInfo: nil, repeats: false) - } - - func invalidateTimer() { - self.timer?.invalidate() - self.timer = nil - } - - @objc func timerFired() { - if let data = self.ttsData { - self.saveToFile(data: data) - self.ttsData = nil - } - } -} - -extension BleSdkPhoneAsrVC{ - func addText(_ text:String){ - self.textView.text += text - let range = NSMakeRange(textView.text.count - 1, 1) - self.textView.scrollRangeToVisible(range) - } - func saveToFile(data: Data) { - let format = DateFormatter() - format.dateFormat = "yyyyMMddHHmmss" - let time = format.string(from: Date()) - -// let filePath = NSHomeDirectory() + "/Documents/\(time).mp3" -// let url = URL(fileURLWithPath: filePath) -// do { -// try data.write(to: url) -// print("\(filePath)") -// PlayerManager.shared.play(filePath: filePath,dialoIg: 0) { (success,dialoIg) in -// print("播放结束 = \(success)") -// } -// // saveFile(withFilePath: filePath) -// } catch let error { -// print("\(error)") -// } - } - - func saveFile(withFilePath filePath: String) { - let fUrl = URL(fileURLWithPath: filePath) - let vc = UIDocumentPickerViewController(url: fUrl, in:.exportToService) - vc.delegate = self - vc.modalPresentationStyle = .overFullScreen - self.present(vc, animated: true, completion: nil) - } -} - -extension BleSdkPhoneAsrVC:UIDocumentPickerDelegate{ - func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) { - controller.dismiss(animated: true, completion: nil) - } -} diff --git a/DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/VC/ChatManager.swift b/DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/VC/ChatManager.swift deleted file mode 100644 index 0359daf..0000000 --- a/DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/VC/ChatManager.swift +++ /dev/null @@ -1,112 +0,0 @@ -// -// ChatManager.swift -// Example -// -// Created by liangshi on 2025/12/15. -// -import AVFoundation -import DeepChordsSdk - -class ChatManager:NSObject{ - static let shared = ChatManager() - var isTtsPlaying = false - - private var audioData: Data? - - var asrCallBack:((String)->Void)? - var reponseCallBack:((String)->Void)? - - func setupSdk(){ - - PcmSound.sharedInstance().setup(16000, withchannels: 1) - let options: AVAudioSession.CategoryOptions = [.allowBluetooth] - do { - try AVAudioSession.sharedInstance().setCategory(.playback, options: [options]) - try AVAudioSession.sharedInstance().setActive(true) - } catch { - DCLogger.debug(error) - } - - setupChat() - } - - func setupChat(){ - DCChat.shared.getVcSpeekerList { list, success in - - } - DCChat.shared.setup { text in - self.asrCallBack?(text) - } responseCallback: { text in - self.reponseCallBack?(text) - } dataCallback: {[weak self] dialogId, data, finish in - guard let self = self else { return } -// PcmSound.sharedInstance().delegate = self -// if (!isTtsPlaying) { -// isTtsPlaying = true -// DCLogger.debug("开始播放") -// DCChat.shared.startPlayTtsAudio() -// } -// DCLogger.debug("收到音频数据", data?.count ?? 0) -// PcmSound.sharedInstance().feed(data) - - if finish { - self.saveToFile(data: self.audioData!) - self.audioData = nil - } else { - if self.audioData == nil { - self.audioData = Data() - } - self.audioData?.append(data!) - } - }codeCallback: { code in -// if code == 20001 { -// if (self.isTtsPlaying) { -// self.isTtsPlaying = false -// PcmSound.sharedInstance().cleanup() -// PcmSound.sharedInstance().setup(16000, withchannels: 1) -// -// var options: AVAudioSession.CategoryOptions = [.allowBluetooth] -// do { -// try AVAudioSession.sharedInstance().setCategory(.playback, options: [options]) -// try AVAudioSession.sharedInstance().setActive(true) -// } catch { -// DCLogger.debug(error) -// } -// } -// } - } - } - - func saveToFile(data: Data) { - let filePath = NSHomeDirectory() + "/Documents/1.mp3" - let url = URL(fileURLWithPath: filePath) - do { - try data.write(to: url) - print("\(filePath)") - - var options: AVAudioSession.CategoryOptions = [.allowBluetooth] - do { - try AVAudioSession.sharedInstance().setCategory(.playback, options: [options]) - try AVAudioSession.sharedInstance().setActive(true) - } catch { - DCLogger.debug(error) - } - //这个要传开始播放的tts的dialogid - DCChat.shared.startPlayTtsAudio() - PlayerManager.shared.play(filePath: filePath,dialoIg:0) { (success,dialoIg) in - print("播放结束 = \(success)") - DCChat.shared.stopPlayTtsAudio() - } - } catch let error { - print("\(error)") - } - } -} -extension ChatManager : PcmSoundDelegate { - func remaining(_ frames: UInt) { - DCLogger.debug("[frame:\(frames)]"); - if (frames == 0) { - DCChat.shared.stopPlayTtsAudio() - } - } -} diff --git a/DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/VC/ChatVC.swift b/DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/VC/ChatVC.swift new file mode 100644 index 0000000..8e663c6 --- /dev/null +++ b/DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/VC/ChatVC.swift @@ -0,0 +1,183 @@ +// +// Untitled.swift +// Example +// +// Created by liangshi on 2025/12/15. +// + +public var kScreenWid: CGFloat { return UIScreen.main.bounds.size.width } + +public var kScreenHei: CGFloat { return UIScreen.main.bounds.size.height } + +import UIKit +import DeepChordsSdk +import AVFoundation +class ChatVC: BaseVC { + var recordBtn = UIButton(type: .custom) + let tableView = UITableView(frame: CGRectZero, style: .plain) + + + deinit { + stopAction() + } + + override func viewDidLoad() { + super.viewDidLoad() + setupViews() + setupSDk() + } + override func viewWillDisappear(_ animated: Bool) { + super.viewWillDisappear(animated) + stopAction() + } + func setupViews() { + self.title = "耳机对话" + self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: "智能体", style: .done, target: self, action: #selector(roleAction)) + + tableView.backgroundColor = .clear + tableView.delegate = self + tableView.dataSource = self + tableView.estimatedRowHeight = 60 + tableView.separatorStyle = .none + tableView.register(ChatCell.self, forCellReuseIdentifier: ChatCell.reuseID) + self.view.addSubview(tableView) + tableView.snp.makeConstraints { make in + make.left.right.top.equalToSuperview() + make.bottom.equalToSuperview().inset(100) + } + + recordBtn.backgroundColor = .blue + recordBtn.setTitle("开始录音", for: .normal) + recordBtn.setTitle("停止录音", for: .selected) + recordBtn.addTarget(self, action: #selector(recordAction), for: .touchUpInside) + view.addSubview(recordBtn) + recordBtn.snp.makeConstraints { make in + make.width.centerX.bottom.equalToSuperview() + make.height.equalTo(80) + } + } + func setupSDk(){ + ChatManager.shared.chatNeedReloadData = {[weak self] in + guard let self = self else{return} + tableView.reloadData() + } + } + func reloadData(){ + + } + @objc func recordAction() { +// self.recordBtn.isSelected = !self.recordBtn.isSelected +// if self.recordBtn.isSelected { +// DCChat.shared.startChat() +// } +// else{ + stopAction() +// } + } + func stopAction() { + DCChat.shared.stopChat() + } + @objc func roleAction(){ + let vc = RoleVC() + vc.didSelectedCallback = { role in + ChatManager.shared.role = role + } + navigationController?.pushViewController(vc, animated: true) + } +} + +extension ChatVC:UITableViewDelegate,UITableViewDataSource { + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return ChatManager.shared.dataList.count + } + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cell = tableView.dequeueReusableCell(withIdentifier: ChatCell.reuseID, for: indexPath) as? ChatCell + cell!.data = ChatManager.shared.dataList[indexPath.row] + return cell! + } + func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + + } +} +class ChatCell:UITableViewCell{ + let bgView = UIView() + let originalLabel = UILabel() + + public static var reuseID: String { + return NSStringFromClass(Self.self).components(separatedBy: ".").last! + } + + override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { + super.init(style: style, reuseIdentifier: reuseIdentifier) + setupViews() + setupLayout() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func setupViews() { + contentView.backgroundColor = .clear + + bgView.do { v in +// v.yc.setCornerRadius(12) + v.backgroundColor = .white + } + originalLabel.do { v in + v.font = .boldSystemFont(ofSize: 14) + v.textColor = .darkGray + v.numberOfLines = 0 + v.textAlignment = .left + } + + contentView.addSubview(bgView) + bgView.addSubview(originalLabel) + } + + func setupLayout() { + bgView.snp.makeConstraints { make in + make.top.bottom.equalToSuperview().inset(10) + make.width.lessThanOrEqualTo(kScreenWid - 36) + make.left.equalToSuperview().inset(18) + } + + originalLabel.snp.makeConstraints { make in + make.top.bottom.equalToSuperview().inset(14) + make.left.right.equalToSuperview().inset(12) + make.width.lessThanOrEqualTo(kScreenWid - 60) + make.width.greaterThanOrEqualTo(10) + make.height.greaterThanOrEqualTo(19) + } + } + + var data:ChatCellModel!{ + didSet{ + if data.content.isEmpty{ + originalLabel.text = "" + } + else{ + originalLabel.text = data.content + } + if data.type == 1 { + bgView.backgroundColor = .blue + originalLabel.textColor = .white + + bgView.snp.remakeConstraints { make in + make.top.bottom.equalToSuperview().inset(10) + make.right.equalToSuperview().inset(20) + } + } + else{ + bgView.backgroundColor = .lightGray + originalLabel.textColor = .darkGray + + bgView.snp.remakeConstraints { make in + make.top.bottom.equalToSuperview().inset(10) + make.left.equalToSuperview().inset(20) + } + } + } + } +} diff --git a/DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/VC/Demo.h b/DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/VC/Demo.h deleted file mode 100644 index 00760aa..0000000 --- a/DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/VC/Demo.h +++ /dev/null @@ -1,16 +0,0 @@ -// -// Demo.h -// Example -// -// Created by liangshi on 2025/12/15. -// - -#import - -NS_ASSUME_NONNULL_BEGIN - -@interface Demo : NSObject - -@end - -NS_ASSUME_NONNULL_END diff --git a/DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/VC/Demo.m b/DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/VC/Demo.m deleted file mode 100644 index 9d53b65..0000000 --- a/DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/VC/Demo.m +++ /dev/null @@ -1,12 +0,0 @@ -// -// Demo.m -// Example -// -// Created by liangshi on 2025/12/15. -// - -#import "Demo.h" - -@implementation Demo - -@end diff --git a/DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/VC/DeviceListVC.swift b/DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/VC/DeviceListVC.swift index ed0a221..c01add6 100644 --- a/DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/VC/DeviceListVC.swift +++ b/DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/VC/DeviceListVC.swift @@ -70,7 +70,7 @@ extension DeviceListVC:UITableViewDelegate,UITableViewDataSource { func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) - cell.textLabel?.text = list[indexPath.row].name + cell.textLabel?.text = list[indexPath.row].name! + list[indexPath.row].identifier.uuidString return cell } diff --git a/DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/VC/EarPhoneRecordVC.swift b/DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/VC/EarPhoneRecordVC.swift new file mode 100644 index 0000000..7a344e2 --- /dev/null +++ b/DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/VC/EarPhoneRecordVC.swift @@ -0,0 +1,130 @@ +// +// Untitled.swift +// Example +// +// Created by liangshi on 2025/12/9. +// + +import UIKit +import DeepChordsSdk + +class EarPhoneRecordVC: BaseVC { + var fromTextView = UITextView() + var toTextView = UITextView() + var recordBtn = UIButton(type: .custom) + + var fromLanguage = "zh-CN" + var toLanguage = "en-US" + var fromItem:UIBarButtonItem! + var toItem:UIBarButtonItem! + + override func viewDidLoad() { + super.viewDidLoad() + setupView() + setupSdk() + } + + override func viewWillDisappear(_ animated: Bool) { + super.viewWillDisappear(animated) + stopAction() + } + func setupView(){ + title = "手机录音" + view.backgroundColor = .white + + fromItem = UIBarButtonItem(title: "源语言", style: .done, target: self, action: #selector(fromLanguageAction)) + toItem = UIBarButtonItem(title: "目标语言", style: .done, target: self, action: #selector(toLanguageAction)) + self.navigationItem.rightBarButtonItems = [ + fromItem,toItem + ]; + + recordBtn.backgroundColor = .blue + recordBtn.setTitle("开始录音", for: .normal) + recordBtn.setTitle("停止录音", for: .selected) + recordBtn.addTarget(self, action: #selector(recordAction), for: .touchUpInside) + view.addSubview(recordBtn) + recordBtn.snp.makeConstraints { make in + make.width.centerX.bottom.equalToSuperview() + make.height.equalTo(80) + } + + fromTextView.isEditable = false + fromTextView.layer.borderColor = UIColor.black.cgColor + fromTextView.layer.borderWidth = 1 + view.addSubview(fromTextView) + fromTextView.snp.makeConstraints { make in + make.width.centerX.equalToSuperview() + make.top.equalToSuperview().inset(100) + make.height.equalTo((kScreenHei - 200)/2) + } + + toTextView.isEditable = false + toTextView.layer.borderColor = UIColor.black.cgColor + toTextView.layer.borderWidth = 1 + view.addSubview(toTextView) + toTextView.snp.makeConstraints { make in + make.width.centerX.equalToSuperview() + make.top.equalTo(fromTextView.snp.bottom).offset(20) + make.bottom.equalTo(recordBtn.snp.top) + } + } + func setupSdk(){ + DCBleRecord.shared.asrCallback = { [weak self] text in + guard let self = self else{return} + self.fromTextView.text = text + let range = NSMakeRange(fromTextView.text.count - 1, 1) + self.fromTextView.scrollRangeToVisible(range) + } + DCBleRecord.shared.translateCallback = { [weak self] text in + guard let self = self else{return} + self.toTextView.text = text + let range = NSMakeRange(toTextView.text.count - 1, 1) + self.toTextView.scrollRangeToVisible(range) + } + DCBleRecord.shared.filePathCallBack = { [weak self] path in + guard let self = self else{return} + } + } + + @objc func fromLanguageAction(){ + let vc = LanuageVC() + vc.didSelectedCallback = { (name,value) in + self.fromLanguage = value + self.fromItem.title = name + + } + navigationController?.pushViewController(vc, animated: true) + } + @objc func toLanguageAction(){ + let vc = LanuageVC() + vc.didSelectedCallback = { (name,value) in + self.toLanguage = value + self.toItem.title = name + + } + navigationController?.pushViewController(vc, animated: true) + } + + @objc func recordAction() { + self.recordBtn.isSelected = !self.recordBtn.isSelected + if self.recordBtn.isSelected { + startAction() + } + else{ + stopAction() + } + } +} + +extension EarPhoneRecordVC{ + func startAction(){ + DCBleRecord.shared.fromLanguage = fromLanguage + DCBleRecord.shared.toLanguage = toLanguage + DCBleRecord.shared.isTranslate = true + DCBleRecord.shared.start() + } + + func stopAction(){ + DCBleRecord.shared.stop() + } +} diff --git a/DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/VC/EqVC.swift b/DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/VC/EqVC.swift new file mode 100644 index 0000000..46de61d --- /dev/null +++ b/DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/VC/EqVC.swift @@ -0,0 +1,128 @@ +// +// EqVC.swift +// Example +// +// Created by liangshi on 2025/12/17. +// + +import UIKit +import SnapKit +import DeepChordsSdk +import Toast_Swift +class EqVC: BaseVC{ + let list:[(name:String,action:String)] = [ + ("获取eq","getEQAction"), + ("正常","normalAction"), + ("流行","popAction"), + ("经典","classicAction"), + ("爵士","jazzAction"), + ("摇滚","rockAction"), + ("自定义","customAction"), + + ] + + override func viewDidLoad() { + super.viewDidLoad() + // Do any additional setup after loading the view. + + setupViews() + } + + func setupViews(){ + self.title = "eq" + let tableView = UITableView(frame: CGRectMake(0, 80, self.view.bounds.size.width, self.view.bounds.size.height), style: .plain) + tableView.delegate = self + tableView.dataSource = self + tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell") + view.addSubview(tableView) + } +} + +extension EqVC:UITableViewDelegate,UITableViewDataSource { + func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { + return 60 + } + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return list.count + } + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) + cell.textLabel?.text = list[indexPath.row].name + + return cell + } + func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + let data = list[indexPath.row] + let action = Selector(data.action) + self.perform(action) + } +} + +extension EqVC { + @objc func getEQAction(){ + DCDevice.shared.getEQ { (arg0,result) in + } + } + @objc func normalAction(){ + view.makeToastActivity(.center) + DCDevice.shared.setEQ(type: 0x00) {[weak self] result in + self?.view.hideToastActivity() + } + } + @objc func popAction(){ + view.makeToastActivity(.center) + DCDevice.shared.setEQ(type: 0x01) {[weak self] result in + self?.view.hideToastActivity() + } + } + @objc func classicAction(){ + view.makeToastActivity(.center) + DCDevice.shared.setEQ(type: 0x02) {[weak self] result in + self?.view.hideToastActivity() + } + } + @objc func jazzAction(){ + view.makeToastActivity(.center) + DCDevice.shared.setEQ(type: 0x03) {[weak self] result in + self?.view.hideToastActivity() + } + } + @objc func rockAction(){ + view.makeToastActivity(.center) + DCDevice.shared.setEQ(type: 0x04) {[weak self] result in + self?.view.hideToastActivity() + } + } + @objc func customAction(){ + let list = [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + ] + view.makeToastActivity(.center) + + let dispatchGroup = DispatchGroup() + for (index,item) in list.enumerated() { + dispatchGroup.enter() + DCDevice.shared.setCustomEQ(index: UInt8(index), value: item) {result in + dispatchGroup.leave() + } + } + + dispatchGroup.notify(queue:.main) { + self.view.hideToastActivity() + DCDevice.shared.setEQ(type: 0x05) {[weak self] result in + self?.view.hideToastActivity() + } + } + + } +} diff --git a/DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/VC/HomeVC.swift b/DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/VC/HomeVC.swift index e4ac6bc..0787bdc 100644 --- a/DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/VC/HomeVC.swift +++ b/DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/VC/HomeVC.swift @@ -12,9 +12,13 @@ import DeepChordsSdk import Toast_Swift class HomeVC: BaseVC{ let list:[(name:String,action:String)] = [ + ("开始查找耳机","startFindAction"), + ("停止查找耳机","stopFindAction"), ("获取电量","batteryAction"), ("获取软件版本","softVersionAction"), ("获取硬件版本","hardVersionAction"), + ("EQ","eqAction"), + ("音乐","musicAction"), ("现场录音","recordAction"), ("耳机录音","bleAsrAction"), ("对话","chatAction"), @@ -30,11 +34,10 @@ class HomeVC: BaseVC{ DispatchQueue.main.asyncAfter(deadline:.now() + 2) { ChatManager.shared.setupSdk() } - } func setupViews(){ - self.title = "设备鉴权+设备采集音频" + self.title = "DeepChords SDK" // self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: "扫描", style: .done, target: self, action: #selector(scanAction)) let tableView = UITableView(frame: CGRectMake(0, 80, self.view.bounds.size.width, self.view.bounds.size.height), style: .plain) @@ -67,39 +70,52 @@ extension HomeVC:UITableViewDelegate,UITableViewDataSource { } extension HomeVC{ + + @objc func startFindAction(){ + view.makeToastActivity(.center) + DCDevice.shared.findDevice(state: 1) {[weak self] result in + self?.view.hideToastActivity() + } + } + @objc func stopFindAction(){ + view.makeToastActivity(.center) + DCDevice.shared.findDevice(state: 0) {[weak self] result in + self?.view.hideToastActivity() + } + } @objc func batteryAction(){ view.makeToastActivity(.center) DCDevice.shared.getBatery {[weak self] result in self?.view.hideToastActivity() - if let (left,right) = result{ - } } } @objc func softVersionAction(){ view.makeToastActivity(.center) DCDevice.shared.getSoftVersion {[weak self] result in self?.view.hideToastActivity() - if let version = result{ - } } } @objc func hardVersionAction(){ view.makeToastActivity(.center) DCDevice.shared.getHardVersion {[weak self] result in self?.view.hideToastActivity() - if let version = result{ - } } } + @objc func eqAction(){ + self.navigationController?.pushViewController(EqVC(), animated: true) + } + @objc func musicAction(){ + self.navigationController?.pushViewController(MusicVC(), animated: true) + } @objc func recordAction(){ - self.navigationController?.pushViewController(BleSdkPhoneAsrVC(), animated: true) + self.navigationController?.pushViewController(PhoneRecordVC(), animated: true) } @objc func bleAsrAction(){ - navigationController?.pushViewController(BleSdkEarPhonesAsrVC(), animated: true) + navigationController?.pushViewController(EarPhoneRecordVC(), animated: true) } @objc func chatAction(){ - navigationController?.pushViewController(BleSdkBleChatVC(), animated: true) + navigationController?.pushViewController(ChatVC(), animated: true) } } diff --git a/DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/VC/SelectLanuageVC.swift b/DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/VC/LanuageVC.swift similarity index 93% rename from DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/VC/SelectLanuageVC.swift rename to DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/VC/LanuageVC.swift index 3f821a3..a3bbd63 100644 --- a/DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/VC/SelectLanuageVC.swift +++ b/DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/VC/LanuageVC.swift @@ -6,10 +6,9 @@ // import UIKit -import StarburstSdk import Toast_Swift -class SelectLanuageVC: BaseVC { +class LanuageVC: BaseVC { private var tableView: UITableView! private var list:[(name: String,value:String)] = [ ("简体中文" ,"zh-CN"), @@ -42,7 +41,7 @@ class SelectLanuageVC: BaseVC { } } } -extension SelectLanuageVC:UITableViewDataSource, UITableViewDelegate{ +extension LanuageVC:UITableViewDataSource, UITableViewDelegate{ func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return list.count } diff --git a/DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/VC/LogVC.swift b/DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/VC/LogVC.swift index 7b687b6..9c01013 100644 --- a/DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/VC/LogVC.swift +++ b/DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/VC/LogVC.swift @@ -12,7 +12,7 @@ class LogVC: UIViewController { var textView = UITextView() override func viewDidLoad() { - title = "log" + title = "app log" view.backgroundColor = .white self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: "save", style: .done, target: self, action: #selector(shareAction)) textView.isEditable = false @@ -23,7 +23,7 @@ class LogVC: UIViewController { override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) // var version = (Bundle.main.appVersion ?? "") + "\n" - let version = "2025151001" + "\n" + let version = "2025151801" + "\n" textView.text = "版本:\(version)" + DCLogger.logs.joined() let range = NSMakeRange(textView.text.count - 1, 1) textView.scrollRangeToVisible(range) diff --git a/DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/VC/MusicVC.swift b/DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/VC/MusicVC.swift new file mode 100644 index 0000000..04c8a65 --- /dev/null +++ b/DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/VC/MusicVC.swift @@ -0,0 +1,76 @@ +// +// MusicVC.swift +// Example +// +// Created by liangshi on 2025/12/17. +// + +import UIKit +import SnapKit +import DeepChordsSdk +import Toast_Swift +class MusicVC: BaseVC{ + let list = [DCMusic]() + + override func viewDidLoad() { + super.viewDidLoad() + // Do any additional setup after loading the view. + + setupViews() + loadData() + } + + func setupViews(){ + self.title = "音乐" + let tableView = UITableView(frame: CGRectMake(0, 80, self.view.bounds.size.width, self.view.bounds.size.height), style: .plain) + tableView.delegate = self + tableView.dataSource = self + tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell") + view.addSubview(tableView) + } +} + +extension MusicVC:UITableViewDelegate,UITableViewDataSource { + func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { + return 60 + } + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return list.count + } + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) + cell.textLabel?.text = list[indexPath.row].name + + return cell + } + func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + let data = list[indexPath.row] + navigationController?.pushViewController(PlayerVC(music: data), animated: true) + } + func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) { + if editingStyle == .delete { + guard let id = UInt16(list[indexPath.row].id) else {return} + DCDevice.shared.musicDelete(list: [id]) { result in + + } + + } + } + func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool { + return true + } + func tableView(_ tableView: UITableView, titleForDeleteConfirmationButtonForRowAt indexPath: IndexPath) -> String? { + return "删除" + } +} + +extension MusicVC { + func loadData(){ + DCDevice.shared.getMusicList(size: 10, index: 0) {[weak self] (list, result) in + guard let self = self else{return} + guard let list = list else{return} + } + } + +} diff --git a/DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/VC/PhoneRecordVC.swift b/DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/VC/PhoneRecordVC.swift new file mode 100644 index 0000000..d9eb756 --- /dev/null +++ b/DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/VC/PhoneRecordVC.swift @@ -0,0 +1,132 @@ +// +// Ble.swift +// Example +// +// Created by liangshi on 2025/12/9. +// + +import UIKit +import DeepChordsSdk + +class PhoneRecordVC: BaseVC { + var fromTextView = UITextView() + var toTextView = UITextView() + var recordBtn = UIButton(type: .custom) + + var fromLanguage = "zh-CN" + var toLanguage = "en-US" + var fromItem:UIBarButtonItem! + var toItem:UIBarButtonItem! + + + + override func viewDidLoad() { + super.viewDidLoad() + setupView() + setupSdk() + } + + override func viewWillDisappear(_ animated: Bool) { + super.viewWillDisappear(animated) + stopAction() + } + func setupView(){ + title = "手机录音" + view.backgroundColor = .white + + fromItem = UIBarButtonItem(title: "源语言", style: .done, target: self, action: #selector(fromLanguageAction)) + toItem = UIBarButtonItem(title: "目标语言", style: .done, target: self, action: #selector(toLanguageAction)) + self.navigationItem.rightBarButtonItems = [ + fromItem,toItem + ]; + + recordBtn.backgroundColor = .blue + recordBtn.setTitle("开始录音", for: .normal) + recordBtn.setTitle("停止录音", for: .selected) + recordBtn.addTarget(self, action: #selector(recordAction), for: .touchUpInside) + view.addSubview(recordBtn) + recordBtn.snp.makeConstraints { make in + make.width.centerX.bottom.equalToSuperview() + make.height.equalTo(80) + } + + fromTextView.isEditable = false + fromTextView.layer.borderColor = UIColor.black.cgColor + fromTextView.layer.borderWidth = 1 + view.addSubview(fromTextView) + fromTextView.snp.makeConstraints { make in + make.width.centerX.equalToSuperview() + make.top.equalToSuperview().inset(100) + make.height.equalTo((kScreenHei - 200)/2) + } + + toTextView.isEditable = false + toTextView.layer.borderColor = UIColor.black.cgColor + toTextView.layer.borderWidth = 1 + view.addSubview(toTextView) + toTextView.snp.makeConstraints { make in + make.width.centerX.equalToSuperview() + make.top.equalTo(fromTextView.snp.bottom).offset(20) + make.bottom.equalTo(recordBtn.snp.top) + } + } + func setupSdk(){ + DCRecord.shared.asrCallback = { [weak self] text in + guard let self = self else{return} + self.fromTextView.text = text + let range = NSMakeRange(fromTextView.text.count - 1, 1) + self.fromTextView.scrollRangeToVisible(range) + } + DCRecord.shared.translateCallback = { [weak self] text in + guard let self = self else{return} + self.toTextView.text = text + let range = NSMakeRange(toTextView.text.count - 1, 1) + self.toTextView.scrollRangeToVisible(range) + } + DCRecord.shared.filePathCallBack = { [weak self] path in + guard let self = self else{return} + } + } + + @objc func fromLanguageAction(){ + let vc = LanuageVC() + vc.didSelectedCallback = { (name,value) in + self.fromLanguage = value + self.fromItem.title = name + + } + navigationController?.pushViewController(vc, animated: true) + } + @objc func toLanguageAction(){ + let vc = LanuageVC() + vc.didSelectedCallback = { (name,value) in + self.toLanguage = value + self.toItem.title = name + + } + navigationController?.pushViewController(vc, animated: true) + } + + @objc func recordAction() { + self.recordBtn.isSelected = !self.recordBtn.isSelected + if self.recordBtn.isSelected { + startAction() + } + else{ + stopAction() + } + } +} + +extension PhoneRecordVC{ + func startAction(){ + DCRecord.shared.fromLanguage = fromLanguage + DCRecord.shared.toLanguage = toLanguage + DCRecord.shared.isTranslate = true + DCRecord.shared.start() + } + + func stopAction(){ + DCRecord.shared.stop() + } +} diff --git a/DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/VC/PlayerVC.swift b/DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/VC/PlayerVC.swift new file mode 100644 index 0000000..6c8d515 --- /dev/null +++ b/DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/VC/PlayerVC.swift @@ -0,0 +1,62 @@ +// +// PlayerVC.swift +// Example +// +// Created by liangshi on 2025/12/18. +// + +import UIKit +import SnapKit +import DeepChordsSdk +import Toast_Swift +class PlayerVC: BaseVC{ + let playBtn = UIButton(type: .custom) + let modeBtn = UIButton(type: .custom) + let music:DCMusic + init(music:DCMusic) { + self.music = music + super.init() + } + + @MainActor required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + override func viewDidLoad() { + super.viewDidLoad() + // Do any additional setup after loading the view. + + setupViews() + } + + func setupViews(){ + self.title = "eq" + + playBtn.do { v in + v.setTitle("播放", for: .normal) + v.setTitle("暂停", for: .selected) + v.addTarget(self, action: #selector(playBtnAction), for: .touchUpInside) + } + playBtn.frame = CGRect(x: 0, y: 0, width: 50, height: 44) + self.navigationItem.rightBarButtonItem = UIBarButtonItem(customView: playBtn) + + modeBtn.do { v in + v.setTitle("播放", for: .normal) + v.setTitle("暂停", for: .selected) + v.addTarget(self, action: #selector(playBtnAction), for: .touchUpInside) + } + } + @objc func playBtnAction(){ + guard let id = UInt16(music.id) else {return} + playBtn.isSelected = !playBtn.isSelected + if playBtn.isSelected { + DCDevice.shared.musicPlay(index: id) { index, result in + + } + } + else{ + DCDevice.shared.musicPause { index, result in + + } + } + } +} diff --git a/DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/VC/RoleVC.swift b/DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/VC/RoleVC.swift new file mode 100644 index 0000000..fabe947 --- /dev/null +++ b/DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/VC/RoleVC.swift @@ -0,0 +1,56 @@ +// +// Untitled.swift +// Example +// +// Created by liangshi on 2025/12/18. +// + +import UIKit +import Toast_Swift +import DeepChordsSdk + +class RoleVC: BaseVC { + private var tableView: UITableView! + private var list = [DCRole]() + var didSelectedCallback: ((DCRole) -> Void)? + + override func viewDidLoad() { + super.viewDidLoad() + view.backgroundColor = .white + + tableView = UITableView(frame:.zero, style:.plain) + tableView.dataSource = self + tableView.delegate = self + tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell") + view.addSubview(tableView) + tableView.snp.makeConstraints { make in + make.edges.equalToSuperview() + } + DCChat.shared.getVcSpeekerList { [weak self] (list, success) in + if let list = list{ + self?.list = list + self?.tableView.reloadData() + } + } + } +} +extension RoleVC:UITableViewDataSource, UITableViewDelegate{ + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return list.count + } + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) + let model = list[indexPath.row] + cell.textLabel?.text = model.name + + return cell + } + + func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + let model = list[indexPath.row] + didSelectedCallback?(model) + self.navigationController?.popViewController(animated: true) + + } +} diff --git a/DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/VC/SdkLogVC.swift b/DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/VC/SdkLogVC.swift index 373b67b..ae17077 100644 --- a/DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/VC/SdkLogVC.swift +++ b/DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/VC/SdkLogVC.swift @@ -12,7 +12,7 @@ class SdkLogVC: UIViewController { var textView = UITextView() override func viewDidLoad() { - title = "log" + title = "sdk log" view.backgroundColor = .white self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: "save", style: .done, target: self, action: #selector(shareAction)) textView.isEditable = false diff --git a/DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/tools/BleManager.swift b/DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/tools/BleManager.swift index 0fcd128..9b8878e 100644 --- a/DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/tools/BleManager.swift +++ b/DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/tools/BleManager.swift @@ -50,6 +50,9 @@ class BleManager: NSObject { func connectPeripheral(_ peripheral: CBPeripheral) { DCLogger.debug("正在连接...",peripheral) centralManager.connect(peripheral, options: [CBConnectPeripheralOptionNotifyOnConnectionKey: true]) + } + func cancel(){ + } func autoConnect(){ // if let peripheral = centralManager.retrieveConnectedPeripherals(withServices: [CBUUID(string: "AE30")]).first{ diff --git a/DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/tools/ChatManager.swift b/DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/tools/ChatManager.swift new file mode 100644 index 0000000..d40842d --- /dev/null +++ b/DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/tools/ChatManager.swift @@ -0,0 +1,173 @@ +// +// ChatManager.swift +// Example +// +// Created by liangshi on 2025/12/15. +// +import AVFoundation +import DeepChordsSdk + +/// Ai回应模型 +public class ChatCellModel:NSObject,NSCoding { + public var type: Int = 0//1发,0收 + public var content: String = "" + public var id:Int = 0 + + required public override init() { } + + public func encode(with aCoder: NSCoder) { + aCoder.encode("\(type)", forKey: "type") + aCoder.encode(content, forKey: "content") + aCoder.encode("\(id)", forKey: "id") + } + + required public init?(coder aDecoder: NSCoder) { + type = Int(aDecoder.decodeObject(forKey: "type") as? String ?? "0") ?? 0 + content = aDecoder.decodeObject(forKey: "content") as? String ?? "" + id = Int(aDecoder.decodeObject(forKey: "id") as? String ?? "0") ?? 0 + } +} + + +class ChatManager:NSObject{ + static let shared = ChatManager() + var isTtsPlaying = false + private var audioData: Data? + + var dataList:[ChatCellModel] = [] + var chatNeedReloadData:(()->Void)? + + var role:DCRole? { + didSet{ + setupChat() + } + } + + public func stop(){ + DCChat.shared.stopChat() + } + public func getVcSpeekerList(finished:@escaping ([DCRole]?,Bool)->Void){ + DCChat.shared.getVcSpeekerList(finished: finished) + } + + func setupSdk(){ + + PcmSound.sharedInstance().setup(16000, withchannels: 1) + let options: AVAudioSession.CategoryOptions = [.allowBluetooth] + do { + try AVAudioSession.sharedInstance().setCategory(.playback, options: [options]) + try AVAudioSession.sharedInstance().setActive(true) + } catch { + DCLogger.debug(error) + } + + setupChat() + } + + func setupChat(){ + DCChat.shared.role = role + DCChat.shared.initChat() + DCChat.shared.asrCallback = {[weak self] (text,index) in + guard let self = self else{return} + if let model = dataList.last,model.id == index,model.type == 1 { + model.content = text + } + else{ + let model = ChatCellModel() + model.type = 1 + model.content = text + model.id = Int(index) + dataList.append(model) + } + chatNeedReloadData?() + } + DCChat.shared.responseCallback = { [weak self] (text,index) in + guard let self = self else{return} + if let model = dataList.last,model.id == index,model.type == 2 { + model.content = text + } + else{ + let model = ChatCellModel() + model.type = 2 + model.content = text + model.id = Int(index) + dataList.append(model) + } + chatNeedReloadData?() + } + DCChat.shared.dataCallback = {[weak self] dialogId, data, finish in + // PcmSound.sharedInstance().delegate = self + guard let self = self else { return } + // if (!isTtsPlaying) { + // isTtsPlaying = true + // DCLogger.debug("开始播放") + // DCChat.shared.startPlayTtsAudio() + // } + // DCLogger.debug("收到音频数据", data?.count ?? 0) + // PcmSound.sharedInstance().feed(data) + + if finish { + self.saveToFile(data: self.audioData!) + self.audioData = nil + } else { + if self.audioData == nil { + self.audioData = Data() + } + self.audioData?.append(data!) + } + } + DCChat.shared.codeCallback = { code in + // if code == 20001 { + // if (self.isTtsPlaying) { + // self.isTtsPlaying = false + // PcmSound.sharedInstance().cleanup() + // PcmSound.sharedInstance().setup(16000, withchannels: 1) + // + //// var options: AVAudioSession.CategoryOptions = [.allowBluetooth] + //// do { + //// try AVAudioSession.sharedInstance().setCategory(.playback, options: [options]) + //// try AVAudioSession.sharedInstance().setActive(true) + //// } catch { + //// DCLogger.debug(error) + //// } + // } + // } + } + DCChat.shared.finishCallback = { path in + + } + } + + func saveToFile(data: Data) { + let filePath = NSHomeDirectory() + "/Documents/1.mp3" + let url = URL(fileURLWithPath: filePath) + do { + try data.write(to: url) + print("\(filePath)") + + var options: AVAudioSession.CategoryOptions = [.allowBluetooth] + do { + try AVAudioSession.sharedInstance().setCategory(.playback, options: [options]) + try AVAudioSession.sharedInstance().setActive(true) + } catch { + DCLogger.debug(error) + } + //这个要传开始播放的tts的dialogid + DCChat.shared.startPlayTtsAudio() + PlayerManager.shared.play(filePath: filePath,dialoIg:0) { (success,dialoIg) in + print("播放结束 = \(success)") + DCChat.shared.stopPlayTtsAudio() + } + } catch let error { + print("\(error)") + } + } +} +extension ChatManager : PcmSoundDelegate { + func remaining(_ frames: UInt) { + DCLogger.debug("[frame:\(frames)]"); + if (frames == 0) { + DCChat.shared.stopPlayTtsAudio() + } + } +} diff --git a/DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/VC/PcmSound.h b/DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/tools/PcmSound.h similarity index 100% rename from DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/VC/PcmSound.h rename to DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/tools/PcmSound.h diff --git a/DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/VC/PcmSound.m b/DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/tools/PcmSound.m similarity index 100% rename from DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/VC/PcmSound.m rename to DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/tools/PcmSound.m diff --git a/DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/VC/PlayerManager.swift b/DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/tools/PlayerManager.swift similarity index 100% rename from DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/VC/PlayerManager.swift rename to DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/tools/PlayerManager.swift diff --git a/DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/tools/then.swift b/DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/tools/then.swift new file mode 100644 index 0000000..e08a119 --- /dev/null +++ b/DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/tools/then.swift @@ -0,0 +1,90 @@ +// +// then.swift +// Example +// +// Created by liangshi on 2025/12/16. +// + +import Foundation +import CoreGraphics +#if os(iOS) || os(tvOS) +import UIKit.UIGeometry +#endif + +public protocol Then {} + +extension Then where Self: Any { + + /// Makes it available to set properties with closures just after initializing and copying the value types. + /// + /// let frame = CGRect().with { + /// $0.origin.x = 10 + /// $0.size.width = 100 + /// } + public func with(_ block: (inout Self) throws -> Void) rethrows -> Self { + var copy = self + try block(©) + return copy + } + + /// Makes it available to execute something with closures. + /// + /// UserDefaults.standard.do { + /// $0.set("devxoul", forKey: "username") + /// $0.set("devxoul@gmail.com", forKey: "email") + /// $0.synchronize() + /// } + public func `do`(_ block: (Self) throws -> Void) rethrows { + try block(self) + } + +} + +extension Then where Self: AnyObject { + + /// Makes it available to set properties with closures just after initializing. + /// + /// let label = UILabel().then { + /// $0.textAlignment = .center + /// $0.textColor = UIColor.black + /// $0.text = "Hello, World!" + /// } + public func then(_ block: (Self) throws -> Void) rethrows -> Self { + try block(self) + return self + } + +} + +extension NSObject: Then {} + +extension CGPoint: Then {} +extension CGRect: Then {} +extension CGSize: Then {} +extension CGVector: Then {} +extension Array: Then {} +extension Dictionary: Then {} +extension Set: Then {} + +#if os(iOS) || os(tvOS) +extension UIEdgeInsets: Then {} +extension UIOffset: Then {} +extension UIRectEdge: Then {} +#endif + + +extension Then where Self: UIView { + public func add(toSuperView superView: UIView) -> Self { + guard superview != self else { return self } + superView.addSubview(self) + return self + } +} + +extension Array where Element: UIView { + public func `do`(_ block: (Element) throws -> Void) rethrows { + for e in self { + try block(e) + } + } +}