骨传导耳机AI修改
authorliangzhenying <867174124@qq.com>
Thu, 18 Dec 2025 10:15:19 +0000 (18:15 +0800)
committerliangzhenying <867174124@qq.com>
Thu, 18 Dec 2025 10:15:19 +0000 (18:15 +0800)
33 files changed:
DeepChordsSdk_iOS/DeepChordsSdk/Example/DeepChordsSdk/DCBleManager.swift
DeepChordsSdk_iOS/DeepChordsSdk/Example/DeepChordsSdk/DCBleRecord.swift
DeepChordsSdk_iOS/DeepChordsSdk/Example/DeepChordsSdk/DCChat.swift
DeepChordsSdk_iOS/DeepChordsSdk/Example/DeepChordsSdk/DCDevice.swift
DeepChordsSdk_iOS/DeepChordsSdk/Example/DeepChordsSdk/DCMusic.swift [new file with mode: 0644]
DeepChordsSdk_iOS/DeepChordsSdk/Example/DeepChordsSdk/DCRecord.swift
DeepChordsSdk_iOS/DeepChordsSdk/Example/DeepChordsSdk/Data.swift
DeepChordsSdk_iOS/DeepChordsSdk/Example/DeepChordsSdk/DeepChordsAiSdk.swift
DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/VC/BaseVC.swift
DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/VC/BleSdkBleChatVC.swift [deleted file]
DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/VC/BleSdkEarPhonesAsrVC.swift [deleted file]
DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/VC/BleSdkPhoneAsrVC.swift [deleted file]
DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/VC/ChatManager.swift [deleted file]
DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/VC/ChatVC.swift [new file with mode: 0644]
DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/VC/Demo.h [deleted file]
DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/VC/Demo.m [deleted file]
DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/VC/DeviceListVC.swift
DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/VC/EarPhoneRecordVC.swift [new file with mode: 0644]
DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/VC/EqVC.swift [new file with mode: 0644]
DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/VC/HomeVC.swift
DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/VC/LanuageVC.swift [moved from DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/VC/SelectLanuageVC.swift with 93% similarity]
DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/VC/LogVC.swift
DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/VC/MusicVC.swift [new file with mode: 0644]
DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/VC/PhoneRecordVC.swift [new file with mode: 0644]
DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/VC/PlayerVC.swift [new file with mode: 0644]
DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/VC/RoleVC.swift [new file with mode: 0644]
DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/VC/SdkLogVC.swift
DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/tools/BleManager.swift
DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/tools/ChatManager.swift [new file with mode: 0644]
DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/tools/PcmSound.h [moved from DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/VC/PcmSound.h with 100% similarity]
DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/tools/PcmSound.m [moved from DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/VC/PcmSound.m with 100% similarity]
DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/tools/PlayerManager.swift [moved from DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/VC/PlayerManager.swift with 100% similarity]
DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/tools/then.swift [new file with mode: 0644]

index 4cd4c3d..e02c3e5 100644 (file)
@@ -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.count - 3)
+                //DCLogger.debug("传给字节sdk",subData.joined())
+                StarburstSdk.shared().receiveBleData(subData)
+            }else{
+                receiveNoticeData(data)
+            }
+        }
+        else{
+            DCLogger.debug("收到异常数据!!!!!!!!!!!")
+        }
+    }
+}
+
+extension DCBleManager{
+    func receiveNoticeData(_ data:Data){
+        if data[1] == 0x10,data[2] == 0x06{
+            DCDevice.shared.receiveNotice(data)
         }
-        
-        if let sender = BleSender.answer(data) {
-            sender.completion?(.response.code(.success).value(data))
-            sender.deinitTimer()
-            sender.remove()
-            return
+        else if data[1] == 0x0F{
+            switch data[2] {
+            case 0x01:
+                DCLogger.debug("打开AI对话")
+            case 0x02:
+                DCLogger.debug("关闭AI对话")
+                DCChat.shared.stopChat()
+            case 0x03:
+                DCLogger.debug("打开耳机录音")
+                DCBleRecord.shared.start()
+            case 0x04:
+                DCLogger.debug("关闭耳机录音")
+                DCBleRecord.shared.stop()
+            default:
+                break
+            }
+
+            
         }
-        else if data[1] == 0x0D{
-            let subData = data.subdata(in: 2..<data.count - 2)
-//            DCLogger.debug("传给字节sdk",subData.joined())
-            StarburstSdk.shared().receiveBleData(subData)
+    }
+    static func contentData(_ data:Data)->Data{
+        if data.count > 4 && data[0] == 0xBA {
+            return data.subdata(in: 2..<data.count - 3)
         }
+        return data
     }
 }
index ba9a340..b9ee18e 100644 (file)
@@ -9,62 +9,103 @@ import StarburstSdk
 
 public class DCBleRecord {
     public static let shared = DCBleRecord()
-    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 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()
     }
 }
index 2d1d4cb..adf6117 100644 (file)
 
 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)
index 74bbf2d..4300bea 100644 (file)
@@ -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..<result.data.count - 3)
+                let str = String(data: data, encoding: .utf8)
+                DCLogger.debug(str ?? "")
+                guard let list = JSON.decode([DCMusic].self, from: data) else { return }
+//                var list = [DCMusic]()
+                finished(list,true)
+            }
+            else{
+                finished(nil,false)
+            }
+        }
+        DCBleManager.shared.send(sender)
+    }
+    
+//    0x00:顺序循环
+//    0x01:随机
+//    0x02:单曲
+    public func setMusicPlayMode(mode:UInt8,finished:@escaping (Bool)->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 (file)
index 0000000..e6d8925
--- /dev/null
@@ -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
+}
index 2fc6c5f..b931197 100644 (file)
@@ -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)
         }
 
     }
index e3a160c..d59dcb4 100644 (file)
@@ -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<UInt8>, 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<T: Decodable>(_ decodable: T.Type, from data: Data) -> T? {
+        do {
+            return try JSONDecoder().decode(decodable.self, from: data)
+        } catch { }
+        return nil
+    }
+    
+    /// JSON decode
+    static func decode<T: Decodable>(_ decodable: T.Type, from jsonString: String) -> T? {
+        guard let data = jsonString.data(using: .utf8) else { return nil }
+        return decode(decodable, from: data)
+    }
+}
index eb5c089..8ef5819 100644 (file)
@@ -29,7 +29,7 @@ public class DeepChordsAiSdk{
 
 
         //debug模式会打印日志
-        StarburstConfig.shared().isDebug = true
+        StarburstConfig.shared().isDebug = false
         // sdk缓存路径
         StarburstConfig.shared().sdkFolder = NSHomeDirectory() + "/Documents/DeepChordsSdk"
         // 日志路径
index e37c695..3b4e8c0 100644 (file)
@@ -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 (file)
index 6aadc88..0000000
+++ /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 (file)
index d371c46..0000000
+++ /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 (file)
index 41cbaac..0000000
+++ /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 (file)
index 0359daf..0000000
+++ /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 (file)
index 0000000..8e663c6
--- /dev/null
@@ -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 (file)
index 00760aa..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-//
-//  Demo.h
-//  Example
-//
-//  Created by liangshi on 2025/12/15.
-//
-
-#import <Foundation/Foundation.h>
-
-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 (file)
index 9d53b65..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-//
-//  Demo.m
-//  Example
-//
-//  Created by liangshi on 2025/12/15.
-//
-
-#import "Demo.h"
-
-@implementation Demo
-
-@end
index ed0a221..c01add6 100644 (file)
@@ -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 (file)
index 0000000..7a344e2
--- /dev/null
@@ -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 (file)
index 0000000..46de61d
--- /dev/null
@@ -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()
+            }
+        }
+
+    }
+}
index e4ac6bc..0787bdc 100644 (file)
@@ -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)
     }
 }
 
@@ -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
     }
index 7b687b6..9c01013 100644 (file)
@@ -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 (file)
index 0000000..04c8a65
--- /dev/null
@@ -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 (file)
index 0000000..d9eb756
--- /dev/null
@@ -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 (file)
index 0000000..6c8d515
--- /dev/null
@@ -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 (file)
index 0000000..fabe947
--- /dev/null
@@ -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)
+        
+    }
+}
index 373b67b..ae17077 100644 (file)
@@ -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
index 0fcd128..9b8878e 100644 (file)
@@ -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 (file)
index 0000000..d40842d
--- /dev/null
@@ -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/tools/then.swift b/DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/tools/then.swift
new file mode 100644 (file)
index 0000000..e08a119
--- /dev/null
@@ -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(&copy)
+        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)
+        }
+    }
+}