var textList:[IBAiResponse] = []
var playIndex = 0
var voiceData = NSMutableData()
+ var ttsData = Data()
override func viewDidLoad() {
setupChatCallBack()
}
+ override func viewWillDisappear(_ animated: Bool) {
+ super.viewWillDisappear(animated)
+ PlayerManager.shared.stop()
+ }
func setupViews(){
//设置默认平台
guard let self = self else {return}
self.title = mode.title
}
- IBAiManager.shared.cancelThink = {[weak self] in
- IBSpeaker.shared.stop()
+ IBAiManager.shared.cancelThink = {[weak self] in
+// IBSpeaker.shared.stop()
+ PlayerManager.shared.stop()
}
IBAiManager.shared.aiResponse = {[weak self] response in
- print(response.description)
+ print("ai回复 === \(response.description)")
guard let self = self else {return}
if response.type == 1 || response.type == 2{
if let model = list.last,model.reponseId == response.reponseId {
list.removeLast()
list.append(response)
-// IBSpeaker.shared.appendText(response.totalContent)
}
else{
list.append(response)
}
}
if response.type == 4{
- IBSpeaker.shared.appendText(response.totalContent)
+ self.saveToFile()
}
}
+
+ IBAiManager.shared.ttsDataCallBack = { [weak self] data in
+ guard let self = self else {return}
+ self.ttsData.append(data)
+ }
}
+
+
@objc func changePlatform(){
let vc = UIAlertController(title: nil, message: "请选择", preferredStyle: .actionSheet)
vc.addAction( UIAlertAction(title: MSPlatform.doubao.title, style: .default) { action in
}
}
-//
-//extension ChatViewController{
-//func setupCallBack(){
-// MixSdk.shared.tts.eventCallBack = { [weak self] model in
-// guard let self = self else {return}
-// if model.event == "SentenceEnd" {
-// runSynthesis()
-// }
-// }
-// MixSdk.shared.tts.voiceCallBack = {[weak self] data in
-// guard let self = self else {return}
-// self.voiceData.append(data)
-// }
-//}
-//
-//func saveToFile(){
-// let filePath = NSHomeDirectory() + "/Documents/1.wav"
-// print(filePath)
-//
-// let url = URL(fileURLWithPath: filePath)
-// do {
-// try self.voiceData.write(to: url, options: .atomic)
-// PlayerManager.shared.play(filePath: filePath)
-// }
-// catch{
-// print(error)
-// }
-//}
-//@objc func btnAction(button:UIButton){
-// button.isSelected = !button.isSelected
-// if button.isSelected {
-// startSynthesis()
-// }
-// else{
-// stopSynthesis()
-// }
-//}
-//}
-//
-//
-//extension ChatViewController{
-// @objc func startSynthesis(){
-// self.voiceData = NSMutableData()
-// MixSdk.shared.tts.startSynthesis()
-// runSynthesis()
-// }
-//
-// @objc func stopSynthesis(){
-// MixSdk.shared.tts.stopSynthesis()
-// self.saveToFile()
-// }
-//
-// @objc func runSynthesis(){
-// if playIndex < self.textList.count{
-// let text = textList[playIndex].content
-// MixSdk.shared.tts.runSynthesis(text: text)
-// self.playIndex = playIndex + 1
-// }
-// }
-//}
+
+extension ChatViewController {
+ func saveToFile(){
+ let pcmPath = NSHomeDirectory() + "/Documents/1.pcm"
+ let mp3Path = NSHomeDirectory() + "/Documents/1.mp3"
+ print(mp3Path)
+
+ let url = URL(fileURLWithPath: pcmPath)
+ do {
+ try self.ttsData.write(to: url, options: .atomic)
+ self.ttsData = Data()
+ print(pcmPath)
+ let result = MSAudioConvert.pcmToMp3(pcmPath: pcmPath, pcmSampleRate: 16000, mp3Path: mp3Path)
+ if result {
+ PlayerManager.shared.play(filePath: mp3Path)
+ }
+ }
+ catch{
+ print(error)
+ }
+ }
+}
--- /dev/null
+//
+// PlayerManager.swift
+// Example
+//
+// Created by incar on 2024/6/18.
+//
+
+import Foundation
+import AVFoundation
+
+class PlayerManager:NSObject{
+ public static let shared = PlayerManager()
+ private var finishCallback:((_ success:Bool) -> Void)?
+
+ private override init() {
+ super.init()
+ }
+
+ var player = AVAudioPlayer()
+
+ func play(filePath:String,finished: ((_ success: Bool) -> Void)? = nil) {
+ print("声音播放: \(filePath)")
+ self.finishCallback = finished
+ let url = URL(fileURLWithPath: filePath)
+ do {
+ try player = AVAudioPlayer(contentsOf: url)
+ player.prepareToPlay()
+ player.delegate = self
+ player.play()
+ } catch let error as NSError {
+ print("声音播放失败 \(error.description)")
+ self.finishCallback?(false)
+ }
+ }
+
+ func stop(){
+ if player.isPlaying{
+ player.stop()
+ }
+ }
+}
+
+extension PlayerManager:AVAudioPlayerDelegate{
+ func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) {
+ print("播放结束 \(flag)")
+ self.finishCallback?(true)
+ }
+
+ func audioPlayerDecodeErrorDidOccur(_ player: AVAudioPlayer, error: Error?) {
+ if let error = error {
+ print("解码错误: \(error.localizedDescription)")
+ }
+ self.finishCallback?(false)
+ }
+}
+
+
3F6A07CC2D2D2C3F00F4122A /* IBPassword.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3F6A07CB2D2D2C3F00F4122A /* IBPassword.swift */; };
3F8167242D5DDE1F002936D5 /* IBSpeaker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3F8167232D5DDE1F002936D5 /* IBSpeaker.swift */; };
3F9355B72D38FF2100C54DAB /* IBPush.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3F9355B62D38FF1900C54DAB /* IBPush.swift */; };
+ 3FBD71C52D7E95AC009E6F3C /* IBAiManager2.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3FBD71C42D7E959E009E6F3C /* IBAiManager2.swift */; };
+ 3FBD72272D7EE1A4009E6F3C /* PlayerManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3FBD72262D7EE1A4009E6F3C /* PlayerManager.swift */; };
3FCB3D182D7AE0D000D75ABB /* IOTBluetooth.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8A0A39BB2B980BEF006A3B18 /* IOTBluetooth.framework */; };
3FCB3D1E2D7AE3B300D75ABB /* MixSdk.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3FCB3D142D7ADFD500D75ABB /* MixSdk.framework */; };
3FCFBFDE2D548D70003FC090 /* pumpkin.lzma in Resources */ = {isa = PBXBuildFile; fileRef = 3FCFBFDD2D548D70003FC090 /* pumpkin.lzma */; };
3F6A07CB2D2D2C3F00F4122A /* IBPassword.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IBPassword.swift; sourceTree = "<group>"; };
3F8167232D5DDE1F002936D5 /* IBSpeaker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IBSpeaker.swift; sourceTree = "<group>"; };
3F9355B62D38FF1900C54DAB /* IBPush.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IBPush.swift; sourceTree = "<group>"; };
+ 3FBD71C42D7E959E009E6F3C /* IBAiManager2.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IBAiManager2.swift; sourceTree = "<group>"; };
+ 3FBD72262D7EE1A4009E6F3C /* PlayerManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerManager.swift; sourceTree = "<group>"; };
3FCB3D0F2D7ADFD500D75ABB /* MixSdk.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = MixSdk.xcodeproj; path = IOTBluetooth/MixSdk/MixSdk/MixSdk.xcodeproj; sourceTree = "<group>"; };
3FCB3D162D7AE08D00D75ABB /* MixTools.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MixTools.framework; path = IOTBluetooth/MixSdk/MixSdk/MixTools.framework; sourceTree = "<group>"; };
3FCFBFDD2D548D70003FC090 /* pumpkin.lzma */ = {isa = PBXFileReference; lastKnownFileType = file; path = pumpkin.lzma; sourceTree = "<group>"; };
18EF69522C45136800AA0A36 /* IBPairling.swift */,
8A9F6AC02BA04BE100DA520F /* IBManager.swift */,
18EF69502C45130200AA0A36 /* IBAiManager.swift */,
+ 3FBD71C42D7E959E009E6F3C /* IBAiManager2.swift */,
8A0A39BE2B980BEF006A3B18 /* IOTBluetooth.h */,
);
path = IOTBluetooth;
18113B292C1318FD00E04997 /* Resource */,
8A1DFF512B9EB4F800B735FF /* AppDelegate.swift */,
8A1DFF552B9EB4F800B735FF /* ViewController.swift */,
+ 3FBD72262D7EE1A4009E6F3C /* PlayerManager.swift */,
8A34B9262BA2AB66000F66BD /* ConnectViewController.swift */,
8A76B6082BA84C2B0016F2AB /* AbilityViewController.swift */,
3F2AF7F92D57118D00635545 /* ChatViewController.swift */,
8A0A39C62B980C88006A3B18 /* IBluetooth.swift in Sources */,
8A0A39CB2B980CFC006A3B18 /* Logger.swift in Sources */,
189C158F2C05F85200676A80 /* IBHistoryStep.swift in Sources */,
+ 3FBD71C52D7E95AC009E6F3C /* IBAiManager2.swift in Sources */,
8A5822D12BA1C80700590AB6 /* LRC.swift in Sources */,
8A0A39DA2B981037006A3B18 /* Date.swift in Sources */,
181441D52C116AE500CCDD46 /* IBDial.swift in Sources */,
files = (
8A878FE02BA93CF1008C7F26 /* Datas.swift in Sources */,
8A34B92B2BA2D446000F66BD /* BaseViewController.swift in Sources */,
+ 3FBD72272D7EE1A4009E6F3C /* PlayerManager.swift in Sources */,
8A76B6092BA84C2B0016F2AB /* AbilityViewController.swift in Sources */,
3F2AF8352D573B6200635545 /* ChatCell.swift in Sources */,
3F2AF82F2D57125D00635545 /* ChatViewController.swift in Sources */,
<key>Example.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
- <integer>8</integer>
+ <integer>9</integer>
</dict>
<key>IOTBluetooth.xcscheme_^#shared#^_</key>
<dict>
public var aiResponse:((_ result:IBAiResponse) -> Void)?
/// 取消当前思考
public var cancelThink:(() -> Void)?
+ /// tts数据回调
+ public var ttsDataCallBack:((_ data:Data) -> Void)?
var chatModeMap:[Int:MSPlatform] = [0:.yiyan,
1:.chatGPT,
var responseId = ""
var reply = ""
+
var isLogin:Bool{
return MixSdk.shared.auth.authorized
}
//取消当前思考
func cancelThinkAction(){
MixSdk.shared.chat.reset()
+ MixSdk.shared.voiceChat.reset()
cancelThink?()
}
func aiRequest(_ data:Data){
}
else{
MixSdk.shared.setPlatform(platform,key: nil)
+ //开始任务
+ MixSdk.shared.voiceChat.start()
}
}
func fectchAiAudio(){
MixSdk.shared.stt.sentenceRecognize(filePath: pcmPath,format:"pcm",sample_rate:"16000",lang: "zh-cn") {[weak self] text, error in
guard let self = self else {return}
if let text = text,text.count > 0{
- Logger.log(text)
+ Logger.log("asr = ",text)
let model = IBAiResponse(responseId)
model.type = 1
model.content = text
sendToChatGpt(content: text)
}
else{
- MixSdk.shared.chat.send(content: text)
+ MixSdk.shared.voiceChat.send(content: text)
}
}
else{
}
}
-
+ //事件回调
+ MixSdk.shared.voiceChat.callBack = { [weak self] model in
+ guard let self = self else {return}
+ Logger.log("\(model.type) : \(model.content ?? "")")
+
+ if model.type == "start" {
+ reply = ""
+ }
+ else if model.type == "end" {
+ let model = IBAiResponse(responseId)
+ model.type = 4
+ model.state = "end"
+ model.totalContent = reply
+ sendRelpy(model: model)
+ }
+ else if model.type == "middle"{
+ reply += model.content ?? ""
+ let data = IBAiResponse(responseId)
+ data.state = "middle"
+ data.type = 2
+ data.content = model.content ?? ""
+ data.totalContent = reply
+ sendRelpy(model: data)
+ }
+ }
+ //⾳频⽂件回调
+ MixSdk.shared.voiceChat.replyCallBack = {[unowned self] data in
+ ttsDataCallBack?(data)
+ }
}
func sendRelpy(model:IBAiResponse){
Logger.log(result)
}
}
+
+
}
extension IBAiManager{
--- /dev/null
+////
+//// IBAiManager2.swift
+//// IOTBluetooth
+////
+//// Created by liangshi on 2025/3/10.
+////
+//
+//////
+////// IBAiManager.swift
+////// IOTBluetooth
+//////
+////// Created by incar on 2024/7/15.
+//////
+////
+//import Foundation
+//import MixSdk
+//
+////public typealias IBProgressHandler = (_ total:Int, _ current:Int) -> Void
+//
+//public class IBAiManager{
+// public static let shared = IBAiManager()
+// /// 产品id
+// public var productId:String = ""
+// /// 产品key
+// public var productKey:String = ""
+// /// chatGptKey
+// public var chatGptKey:String?
+// /// chatGpt model gpt3.5-turbo、gpt-4-turbo、gpt-4o 默认是 gpt-4-turbo
+// public var gptModel:String = "gpt-4-turbo"
+//
+// public var platform:MSPlatform{
+// return MixSdk.shared.aiPlatform
+// }
+// /// 使用大模型
+// public var useMode:((_ result:MSPlatform) -> Void)?
+// /// 使用大模型
+// public var aiResponse:((_ result:IBAiResponse) -> Void)?
+// /// 取消当前思考
+// public var cancelThink:(() -> Void)?
+//
+// var chatModeMap:[Int:MSPlatform] = [0:.yiyan,
+// 1:.chatGPT,
+// 2:.kimi,
+// 3:.doubao,
+// 4:.deepseek,
+// 5:.xunfei,
+// ]
+//
+// var responseId = ""
+// var reply = ""
+//
+// var isLogin:Bool{
+// return MixSdk.shared.auth.authorized
+// }
+//
+//
+// init(){
+// MixSdk.shared.isDebug = true
+// setupChatCallBack()
+// }
+//
+// func aiValid(_ data:Data){
+// let result = IBGeneralParser.dataPacket(data)
+// let platform = chatModeMap[result.int] ?? .doubao
+// let key = platform == .chatGPT ? chatGptKey : nil
+// useMode?(platform)
+//
+// if !isLogin{
+// Logger.log("ai未登录!!!")
+// sendAiState(isValid: false, gptKey: key)
+// login()
+// }
+// else{
+// if platform == .chatGPT {
+// checkChatptValid(key ?? "") {[weak self] isValid, error in
+// guard let self = self else {return}
+// if let valid = isValid{
+// sendAiState(isValid: valid, gptKey: key)
+// }
+// else {
+// sendAiState(isValid: false, gptKey: key)
+// }
+// }
+// }
+// else{
+// sendAiState(isValid: true, gptKey: nil)
+// }
+// }
+// }
+//
+// public func checkChatptValid(_ key:String ,completeHandler: @escaping MSChatGptCheckKeyCallBack){
+// guard let key = chatGptKey else{
+// Logger.log("请先设置chatGPT key")
+// completeHandler(false,nil)
+// return
+// }
+// MixSdk.shared.chatGpt.isValidKey(key, completeHandler: completeHandler)
+// }
+// func sendAiState(isValid:Bool,gptKey:String?){
+// let model = IBAiValid()
+// model.isValid = isValid
+// model.key = gptKey
+// IBSender()
+// .ability(IBAbility.aiValid)
+// .parameters([.aiValid(model)])
+// .response { result in
+//
+// }
+// }
+//
+// //取消当前思考
+// func cancelThinkAction(){
+// MixSdk.shared.chat.reset()
+// cancelThink?()
+// }
+// func aiRequest(_ data:Data){
+// let result = IBGeneralParser.dataPacket(data)
+// let code = result.int
+// IBSender()
+// .ability(IBAbility.aiReady)
+// .parameters([.value(1)])
+// .response {[weak self] result in
+// self?.fectchAiAudio()
+// }
+//
+//
+// let platform = chatModeMap[code] ?? .doubao
+// Logger.log("使用模型:",platform.title)
+// useMode?(platform)
+//
+// if platform == .chatGPT {
+// MixSdk.shared.chatGpt.changeModel(model:gptModel)
+// MixSdk.shared.setPlatform(platform,key: nil)
+// }
+// else{
+// MixSdk.shared.setPlatform(platform,key: nil)
+// }
+// }
+// func fectchAiAudio(){
+// IBSender()
+// .ability(IBAbility.fetchAiAudio)
+// .parameters([])
+// .response {[weak self] result in
+// guard let self = self else {return}
+// responseId = "\(Date().timeIntervalSince1970)"
+// if result.isSuccess,let data = result.value as? Data{
+// receiveAiData(sbcData: data)
+// }
+// else{
+// Logger.log(result)
+// let model = IBAiResponse(responseId)
+// model.type = 0
+// model.state = "end"
+// model.content = "录音解析错误"
+// sendRelpy(model: model)
+// }
+// }
+// }
+//}
+//extension IBAiManager{
+// func receiveAiData(sbcData:Data){
+// let pcmData = MSAudioConvert.sbcTopcm(sbcData)
+// if pcmData.count == 0{
+// Logger.log("sbc2pcm失败")
+// let model = IBAiResponse(responseId)
+// model.type = 0
+// model.state = "end"
+// model.content = "语音识别失败"
+// sendRelpy(model: model)
+// return
+// }
+// guard let pcmPath = IBTools.saveToFile(data: pcmData) else {return}
+// MixSdk.shared.stt.sentenceRecognize(filePath: pcmPath,format:"pcm",sample_rate:"16000",lang: "zh-cn") {[weak self] text, error in
+// guard let self = self else {return}
+// if let text = text,text.count > 0{
+// Logger.log(text)
+// let model = IBAiResponse(responseId)
+// model.type = 1
+// model.content = text
+// model.state = "end"
+// model.totalContent = text
+// sendRelpy(model: model)
+// responseId = "\(Date().timeIntervalSince1970)"
+//
+// if MixSdk.shared.aiPlatform == .chatGPT {
+// sendToChatGpt(content: text)
+// }
+// else{
+// MixSdk.shared.chat.send(content: text)
+// }
+// }
+// else{
+// Logger.log(error ?? "")
+// let model = IBAiResponse(responseId)
+// model.type = 3
+// model.state = "end"
+// sendRelpy(model: model)
+// }
+// }
+// }
+//
+// func sendToChatGpt(content text:String){
+// reply = ""
+// MixSdk.shared.chatGpt.send(content: text) {[weak self] response, error in
+// guard let self = self else {return}
+// if let model = response,let choices = model.choices.first {
+// // choices.message.content
+// reply = choices.message.content
+// let data1 = IBAiResponse(responseId)
+// data1.state = "middle"
+// data1.type = 2
+// data1.content = choices.message.content
+// data1.totalContent = reply
+// sendRelpy(model: data1)
+//
+// let data2 = IBAiResponse(responseId)
+// data2.state = "end"
+// data2.type = 4
+// data2.content = choices.message.content
+// data2.totalContent = reply
+// sendRelpy(model: data2)
+// }
+// else if let err = error{
+// //错误
+// Logger.log(error ?? "")
+// let model = IBAiResponse(responseId)
+// model.type = 3
+// model.state = "end"
+// sendRelpy(model: model)
+// }
+// }
+// }
+//
+//
+// func setupChatCallBack(){
+// let chat = MixSdk.shared.chat
+// chat.callBack = {[weak self] model in
+// guard let self = self else {return}
+// Logger.log("\(model.type) : \(model.content ?? "")")
+//
+// if model.type == "start" {
+// reply = ""
+// }
+// else if model.type == "end" {
+// let model = IBAiResponse(responseId)
+// model.type = 4
+// model.state = "end"
+// model.totalContent = reply
+// sendRelpy(model: model)
+// }
+// else if model.type == "middle"{
+// reply += model.content ?? ""
+// let data = IBAiResponse(responseId)
+// data.state = "middle"
+// data.type = 2
+// data.content = model.content ?? ""
+// data.totalContent = reply
+// sendRelpy(model: data)
+// }
+// }
+//
+//
+// }
+//
+// func sendRelpy(model:IBAiResponse){
+// self.aiResponse?(model)
+// IBSender()
+// .ability(IBAbility.aiResponse)
+// .parameters([.aiResponse(model)])
+// .response { result in
+// Logger.log(result)
+// }
+// }
+//}
+//
+//extension IBAiManager{
+// func login(){
+// let mac = IBBluetooth.shared.currentConnected.macAddress.split(separator: ":").joined().uppercased()
+// login(deviceId:mac ) { token, error in
+//
+// }
+// }
+// /// 登录
+// /// - Parameters:
+// /// - deviceId: 设备ID
+// /// - completeHandler: 结果回调
+// func login(deviceId:String,completeHandler: @escaping MSLoginCallBack){
+// if productId.isEmpty || productKey.isEmpty {
+// Logger.log("请先设置productId和productKey!")
+// return
+// }
+// MixSdk.shared.isDebug = true
+// if let password = UserDefaults.standard.string(forKey: deviceId){
+// startLogin(deviceId: deviceId, password: password, completeHandler: completeHandler)
+// }
+// else{
+// startRegister(deviceId: deviceId, completeHandler: completeHandler)
+// }
+// }
+//
+// func startLogin(deviceId:String,password:String,completeHandler: @escaping MSLoginCallBack) {
+// MixSdk.shared.auth.login(productKey:productKey,deviceId: deviceId, password: password){[weak self] token,error in
+// if let err = error{
+// if case .reponseError(let code,_) = err,code == 50021{
+// //50021是账号或密码错误.可以在这里调用注册,重新注册后,之前的密码会被覆盖,APP更新保存的新密码
+// self?.startRegister( deviceId: deviceId, completeHandler: completeHandler)
+// }
+// }
+// else{
+// completeHandler(token,error)
+// }
+// }
+// }
+//
+// func startRegister(deviceId:String,completeHandler: @escaping MSRegisterCallBack){
+// MixSdk.shared.auth.register(productId: productId, productKey: productKey, deviceId: deviceId){[weak self] password,error in
+// if let pwd = password{
+// UserDefaults.standard.setValue(password, forKey: deviceId)
+// //deviceId和password APP要确保这个一一对应。
+// self?.startLogin(deviceId: deviceId, password: pwd, completeHandler: completeHandler)
+// }
+// else{
+// completeHandler(nil,error)
+// }
+// }
+// }
+//}
let pkgIndex = data[11...14].int
let pkgLen = data[15...16].int
- Logger.log(
- mark, uuid, "->",
- "pkgTotal = \(pkgTotal),dataTotal = \(dataTotal),pkgIndex = \(pkgIndex),pkgLen = \(pkgLen)",
- data.joined(" "))
+// Logger.log(
+// mark, uuid, "->",
+// "pkgTotal = \(pkgTotal),dataTotal = \(dataTotal),pkgIndex = \(pkgIndex),pkgLen = \(pkgLen)",
+// data.joined(" "))
if pkgIndex == 1 {
headData = data.prefix(17)
lastData = data.suffix(1)
Logger.log(error)
}
if let data = characteristic.value {
- Logger.log( "[receive] \(characteristic.uuid.uuidString) " + data.joined(" "))
+// Logger.log( "[receive] \(characteristic.uuid.uuidString) " + data.joined(" "))
}
receiveManager.receice(characteristic: characteristic)
}
18FBC2B72C38DA05002240AF /* MSChatGpt.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18FBC2B62C38DA05002240AF /* MSChatGpt.swift */; };
18FBC2B92C38DA1D002240AF /* MSChatGptModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18FBC2B82C38DA1D002240AF /* MSChatGptModel.swift */; };
3F3BD3BC2D2631D5001D4F6E /* MixTools.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3F3BD3BB2D2631D5001D4F6E /* MixTools.framework */; };
+ 3FBD721C2D7EDB77009E6F3C /* MSAsr.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3FBD721B2D7EDB6D009E6F3C /* MSAsr.swift */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
18FBC2B62C38DA05002240AF /* MSChatGpt.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MSChatGpt.swift; sourceTree = "<group>"; };
18FBC2B82C38DA1D002240AF /* MSChatGptModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MSChatGptModel.swift; sourceTree = "<group>"; };
3F3BD3BB2D2631D5001D4F6E /* MixTools.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = MixTools.framework; sourceTree = "<group>"; };
+ 3FBD721B2D7EDB6D009E6F3C /* MSAsr.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MSAsr.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
18132AEE2C1C25CC002F6B52 /* MSAigc.swift */,
184FFC532C21B091006F664D /* MSAigcModel.swift */,
181188562C1BE24200C23FAB /* MSStt.swift */,
+ 3FBD721B2D7EDB6D009E6F3C /* MSAsr.swift */,
18C42B382C22F1FA000D8860 /* MSSttModel.swift */,
18B74A1B2C1FD44A00FABBA9 /* MSTts.swift */,
18C42B3C2C22F2C4000D8860 /* MSTtsModel.swift */,
18C42B4B2C23DA29000D8860 /* SbcToPcm.swift in Sources */,
18A2DCE02C40D94800A8731B /* Collection+Extension.swift in Sources */,
18A2DD112C40D94900A8731B /* PKCS5.swift in Sources */,
+ 3FBD721C2D7EDB77009E6F3C /* MSAsr.swift in Sources */,
18A2DCD22C40D94800A8731B /* Cryptors.swift in Sources */,
18A2DCD82C40D94800A8731B /* AEAD.swift in Sources */,
18A2DD1F2C40D94900A8731B /* CipherModeWorker.swift in Sources */,
<key>MixSdk.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
- <integer>0</integer>
+ <integer>1</integer>
</dict>
</dict>
</dict>
--- /dev/null
+//
+// MSAsr.swift
+// MixSdk
+//
+// Created by liangshi on 2025/3/10.
+//
+
+import Foundation
+
+/// 语音转文字类
+public class MSAsr {
+ var timer:Timer?
+ var socket:WebSocket!
+ var taskId:String?
+
+ /// 事件回调
+ public var eventCallBack:MSSttEventCallBack?
+ /// 连接状态回调
+ public var connectStateCallBack:MSConnectStateCallBack?
+
+
+ /// 连接状态
+ public var connectState:MSConnectState{
+ if socket.readyState == .open {
+ return .connected
+ }
+ else{
+ return .disConnected
+ }
+ }
+
+ func setupWebSocket(){
+ openWebSocket()
+ setupHeartBeat()
+ }
+
+ func openWebSocket(){
+ let url = MSConfig.webSocketUrl(api: "asr/ws",addPlatform: false)
+ Logger.log(url)
+ socket = WebSocket(url)
+ socket.delegate = self
+ }
+ func setupHeartBeat(){
+ timer?.invalidate()
+ timer = Timer.scheduledTimer(withTimeInterval: MSConfig.wsHeartBeatTime, repeats: true) { [weak self] _ in
+ guard let self = self else {return}
+ if socket.readyState == .open{
+ self.socket.ping()
+ }
+ else{
+ self.openWebSocket()
+ }
+ }
+ RunLoop.current.add(timer!, forMode: .common)
+ }
+
+ /// 一句话识别
+ /// - Parameters:
+ /// - filePath: 语音文件路径
+ /// - format: 语音格式,单声道(mono)16bit采样位数音频,包括无压缩的PCM、WAV、OPUS、AMR、SPEEX、MP3、AAC格式。
+ /// - sample_rate:采样率
+ /// - lang: zh-cn 中文/en-us 英文
+ /// - completeHandler: 结果回调
+ public func sentenceRecognize(filePath:String,format:String? = nil,sample_rate:String? = nil,lang:String? = nil,completeHandler: @escaping MSSttCallBack){
+ if !MixSdk.shared.auth.authorized {
+ completeHandler(nil,.unauthorised)
+ return
+ }
+ guard let fileData = MSTool.readFile(from: filePath) else {
+ completeHandler(nil,.invalidRequest)
+ return
+ }
+
+ var url = MSConfig.asr(api: "asr/sentence-recognize")
+ if let value = format{
+ url += "&format=\(value)&"
+ }
+ if let value = lang{
+ url += "lang=\(value)&"
+ }
+ if let value = sample_rate{
+ url += "sample_rate=\(value)&"
+ }
+ Logger.log(url)
+
+ var request = URLRequest(url: URL(string:url)!)
+ request.httpMethod = "POST"
+ request.setValue("application/json", forHTTPHeaderField: "Content-Type")
+ request.httpBody = fileData
+ let task = URLSession.shared.dataTask(with: request) { (data, response, error) in
+ DispatchQueue.main.sync {
+ let (dict,error) = MSHttpManager.receiveData(data,response,error)
+ if let error = error {
+ completeHandler(nil,error)
+ return
+ }
+ guard let dict = dict else {return completeHandler(nil,error)}
+ if let status = dict["status"] as? Int,status == 0,let data = dict["data"] as? [String:Any], let result = data["result"] as? String{
+ completeHandler(result,nil)
+ }
+ else{
+ completeHandler(nil,MSError.reponseError(dict["status"] as? Int ?? 0, "请求失败"))
+ }
+ }
+ }
+ task.resume()
+ }
+}
+
+public extension MSAsr{
+ /// 开始转换
+ func startTranscription(){
+ if !MixSdk.shared.auth.authorized {
+ Logger.log("还未鉴权")
+ return
+ }
+ let taskId = "\(Int(Date().timeIntervalSince1970)*1000)".md5().lowercased();
+ self.taskId = taskId
+ let params = [
+ "header":[
+ "event":"StartTranscription",
+ "trace_id":taskId,
+ ],
+ "payload":[
+ "format":"pcm",
+ "sample_rate":16000
+ ]
+ ]
+ let cmd = params.jsonString() ?? ""
+ Logger.log(cmd)
+ socket.send(text: cmd )
+ }
+
+ /// 结束转换
+ func stopTranscription(){
+ if !MixSdk.shared.auth.authorized {
+ Logger.log("还未鉴权")
+ return
+ }
+ let params = [
+ "header":[
+ "event":"StopTranscription",
+ "trace_id":self.taskId ?? ""
+ ]
+ ]
+ let cmd = params.jsonString() ?? ""
+ Logger.log(cmd)
+ socket.send(text: cmd )
+
+ }
+
+
+ /// 发送语音数据,format pcm,采样率 16000
+ /// - Parameter data: 语音数据
+ func sendVoice(pcmData:Data){
+ if !MixSdk.shared.auth.authorized {
+ Logger.log("还未鉴权")
+ return
+ }
+ Logger.log("send \(pcmData)")
+ socket.send(data: pcmData)
+ }
+
+ /// 发送语音数据,format sbc,采样率 16000
+ /// - Parameter data: 语音数据
+ func sendVoice(sbcData:Data){
+ if !MixSdk.shared.auth.authorized {
+ Logger.log("还未鉴权")
+ return
+ }
+ MSAudioConvert.sbcTopcm(sbcData){[weak self] pcm in
+ self?.socket.send(data: pcm)
+ }
+ }
+}
+
+
+extension MSAsr:WebSocketDelegate{
+ func webSocketOpen() {
+ Logger.log("MSStt webSocketOpen")
+ connectStateCallBack?(.connected)
+ }
+
+ func webSocketClose(_ code: Int, reason: String, wasClean: Bool) {
+ Logger.log("MSStt webSocketClose",code,reason,wasClean)
+ connectStateCallBack?(.disConnected)
+ }
+
+ func webSocketError(_ error: NSError) {
+ // Logger.log("MSStt webSocketError",error)
+ }
+ func webSocketMessageText(_ text: String) {
+ Logger.log("MSStt receive",text)
+ if let data = text.data,let model = JSON.decode(MSSttEvent.self, from: data) {
+ eventCallBack?(model)
+ }
+ else if let data = text.data,let model = JSON.decode(MSSttEventHead.self, from: data) {
+ eventCallBack?(model.header)
+ // openWebSocket()
+ }
+ }
+
+ func webSocketMessageData(_ data: Data){
+ Logger.log("MSStt receive",data)
+ }
+
+ func webSocketPong(){
+ // Logger.log("MSStt Pong")
+ }
+
+ func webSocketEnd(_ code: Int, reason: String, wasClean: Bool, error: NSError?){
+ // Logger.log("MSStt webSocketEnd",code,reason,wasClean,error ?? "")
+ }
+}
+
Logger.log("sign = ",pwd)
Logger.log(url,params)
+ MixSdk.shared.voiceChat.deviceId = deviceId
+
HTTP.POST(url,parameters: params,requestSerializer: JSONParameterSerializer()){ response in
DispatchQueue.main.sync {
let (dict,error) = MSHttpManager.receiveData(response)
//// Created by incar on 2024/7/29.
////
//
-//import Foundation
-//
-///// 语音回复回调
-//public typealias MSVoiceChatReplyCallBack = (_ data: Data) -> Void
-///// 事件回调
-//public typealias MSVoiceChatEventCallBack = (_ model:MSVoiceChatModel) -> Void
-//
-/////
-//public class MSVoiceChat {
-// var timer:Timer?
-// var socket:WebSocket!
-//
+import Foundation
+
+/// 语音回复回调
+public typealias MSVoiceChatReplyCallBack = (_ data: Data) -> Void
+/// 事件回调
+public typealias MSVoiceChatEventCallBack = (_ model:MSVoiceChatEvent) -> Void
+
+
+public class MSVoiceChat {
+ var socket:WebSocket?
+ var timer:Timer?
// /// 事件回调
// public var eventCallBack:MSVoiceChatEventCallBack?
-// /// 语音数据回调
-// public var replyCallBack:MSVoiceChatReplyCallBack?
-//
-// /// 连接状态回调
-// public var connectStateCallBack:MSConnectStateCallBack?
-//
-//
-// /// 任务id
-// var taskId:String?
-// /// 对话id
-// var dialogId:String = ""
-//
-// /// 连接状态
-// public var connectState:MSConnectState{
-// if socket.readyState == .open {
-// return .connected
-// }
-// else{
-// return .disConnected
-// }
-// }
-// init(){
-// setupWebSocket()
-// }
-//
-// func setupWebSocket(){
-// openWebSocket()
-// setupHeartBeat()
-// }
-//
-// func openWebSocket(){
-// // let url = MSConfig.webSocketUrl(api: "chat/ws/watches",addPlatform: true)
-// let token = "cbb6c5cb8b8a4eada3dccd4f2ab2e17d"
-// let url = "wss://nls-gateway-cn-beijing.aliyuncs.com/ws/v1?" + "token=\(token)"
-// Logger.log(url)
-// socket = WebSocket(url)
-// socket.delegate = self
-// }
-// func setupHeartBeat(){
-// timer?.invalidate()
-// timer = Timer.scheduledTimer(withTimeInterval: MSConfig.wsHeartBeatTime, repeats: true) { [weak self] _ in
-// guard let self = self else {return}
-// if socket.readyState == .open{
-// self.socket.ping()
-// }
-// else{
-// self.openWebSocket()
-// }
-// }
-// RunLoop.current.add(timer!, forMode: .common)
-// }
-//
-// /// name指令名称,如Start,SendSpeech。
-// func getParams(name:String, _ params:inout [String:Any])->[String:Any]{
-//// d1efdb7da6d443a780ab74c92001c647
-//// 66539a5f03d2468f9be3d55f2a3ebe07
-//// gkc1wwuxvm0egh40hqmrumxjfjetofc0
-//// let message_id = MSTool.generateRandomString2(length: 32).lowercased()
-//
-// if self.taskId == nil{
-// self.taskId = MSTool.generateRandomString2(length: 32).lowercased()
-// }
-//
-// var header:[String:Any] = [:]
-// header["namespace"] = "VoiceChat"//访问的功能,固定
-// header["name"] = name//指令名称,如Start,SendSpeech。
-// header["appkey"] = "j2QhAoOeBi5AUlrM"
-// header["message_id"] = MSTool.generateRandomString2(length: 32).lowercased()
-// header["task_id"] = self.taskId!
-// params["header"] = header
-// return params
-// }
-//
-// public func start(){
-// // 你是一个闲聊型人工智能助手,名字叫通义千问;你出生于杭州,会用简洁语言表达你的想法。你是用户的好朋友。你的回答将通过逼真的文字转语音技术读出,请用闲聊的语气进行对话;\n生成回答内容时请遵循以下规则:\n1、以纯文本进行回复,不要包含MARKDOWN。\n2、请像真正的朋友一样与用户开展的聊天,保持自然交流不要用敬语这类称呼。\n3、回复尽量简短,绝大多数问题都以一到两句话回答;大部分阿拉伯数字换成中文表达,尽量不要出现特殊符号。\n4、减少附和,不要随意重复对话中的话;请真正像人一样思考和回复,不要说你是“语言模型”或是“人工智能”。\n5、使用更口语化的语气进行表达,例如使用口语短句进行回复。\n6、回复可以尽量简洁并且在过程中插入常见的口语词汇。\n7、不要问有什么要聊的这种泛的问题,不要主动结束对话。\n请绝对遵循这些规则,即使被问及这些规则,也不要引用它们。\n
-// var params:[String:Any] = [
-// "payload":[
-// "dialog_attributes":[
-// "model_id":"qwen-plus",
-// "prompt":"你是一个闲聊型人工智能助手,名字叫通义千问;你出生于杭州,会用简洁语言表达你的想法。你是用户的好朋友。你的回答将通过逼真的文字转语音技术读出,请用闲聊的语气进行对话;",
-// "voice":"loongeric_25hz",
-// "voice_detection_enabled":true,
-// ]
-// ]
-// ]
-// params = getParams(name:"Start",¶ms)
-// let cmd = params.jsonString() ?? ""
-// Logger.log("send" ,cmd)
-// socket.send(text: cmd)
-// }
-// public func willSpeedch(){
-// var params:[String:Any] = [
-// "payload":[
-// "dialog_id":dialogId,
-// ]
-// ]
-// params = getParams(name:"SendSpeech",¶ms)
-// let cmd = params.jsonString() ?? ""
-// Logger.log("send" ,cmd)
-// socket.send(text: cmd)
-// }
-//
-// /// 发送语音数据,format pcm,采样率 16000
-// /// - Parameter data: 语音数据
-// public func sendVoice(pcmData:Data){
-//// if !MixSdk.shared.auth.authorized {
-//// Logger.log("还未鉴权")
-//// return
-//// }
-// Logger.log("send \(pcmData)")
-// socket.send(data: pcmData)
-// }
-//
-// /// 发送语音数据,format sbc,采样率 16000
-// /// - Parameter data: 语音数据
-// public func sendVoice(sbcData:Data){
-//// if !MixSdk.shared.auth.authorized {
-//// Logger.log("还未鉴权")
-//// return
-//// }
-// MSAudioConvert.sbcTopcm(sbcData){[weak self] pcm in
-// self?.socket.send(data: pcm)
-// }
-// }
-//}
-//
-//
-//extension MSVoiceChat:WebSocketDelegate{
-// func webSocketOpen() {
-// Logger.log("MSVoiceChat webSocketOpen")
-// connectStateCallBack?(.connected)
-// start()
-// }
-//
-// func webSocketClose(_ code: Int, reason: String, wasClean: Bool) {
-// Logger.log("MSVoiceChat webSocketClose",code,reason,wasClean)
-// connectStateCallBack?(.disConnected)
-// }
-//
-// func webSocketError(_ error: NSError) {
-// Logger.log("MSVoiceChat webSocketError",error)
-// }
-//
-// func webSocketMessageText(_ text: String) {
-// Logger.log("MSVoiceChat receive",text)
-// guard let data = text.data,let model = JSON.decode(MSVoiceChatModel.self, from: data) else{
-// return
-// }
-// if let dialog_id = model.payload?.dialog_id{
-// self.dialogId = dialog_id
-// }
-// eventCallBack?(model)
-// }
-//
-// func webSocketMessageData(_ data: Data){
-// Logger.log("MSVoiceChat receive data ",data)
-// replyCallBack?(data)
-// // if let model = MSChatModel.decode(data){
-// // callBack?(model)
-// // }
-// }
-//
-// func webSocketPong(){
-// // Logger.log("MSVoiceChat Pong")
-// }
-//
-// func webSocketEnd(_ code: Int, reason: String, wasClean: Bool, error: NSError?){
-// Logger.log("MSVoiceChat webSocketEnd",code,reason,wasClean,error ?? "")
-// }
-//}
+ /// 对话回调
+ public var callBack:MSChatCallBack?
+ /// 语音数据回调
+ public var replyCallBack:MSVoiceChatReplyCallBack?
+
+ /// 连接状态回调
+ public var connectStateCallBack:MSConnectStateCallBack?
+
+ /// 当前出在的事件状态
+ public var currentEvent:MSVoiceChatEvent?
+
+ /// 设备ID/用户ID
+ var deviceId:String = ""
+
+ /// 任务id
+ var taskId:String?
+ var dialogId:String = ""
+
+ /// 最后的聊天内容
+ var lastChatModel:MSChatModel?
+
+ /// 连接状态
+ public var connectState:MSConnectState{
+ if let s = socket, s.readyState == .open {
+ return .connected
+ }
+ else{
+ return .disConnected
+ }
+ }
+ init(){
+
+ }
+
+ /// 开始任务
+ public func start(){
+ openWebSocket()
+ setupHeartBeat()
+ }
+
+ func setupHeartBeat(){
+ timer?.invalidate()
+ timer = Timer.scheduledTimer(withTimeInterval: MSConfig.wsHeartBeatTime, repeats: true) { [weak self] _ in
+ guard let self = self else {return}
+ if socket?.readyState == .open{
+ self.socket?.ping()
+ }
+ else{
+ self.openWebSocket()
+ }
+ }
+ RunLoop.current.add(timer!, forMode: .common)
+ }
+ func closeWebSocket(){
+ if connectState == .connected {
+ socket?.close(10100,reason: "正常结束")
+ }
+ if let model = lastChatModel,model.type != "end"{
+ lastChatModel = nil
+ let endModel = MSChatModel()
+ endModel.type = "end"
+ callBack?(endModel)
+ }
+ taskId = nil;
+ dialogId = "";
+ currentEvent = nil;
+ }
+ /// 重置聊天对话
+ public func reset(){
+ closeWebSocket()
+ }
+
+
+ /// 开始说话,在发送语音数据前,一定要先调用该方法
+ private func startSpeedch(){
+
+ let params:[String:Any] =
+ [
+ "header":[
+ "trace_id":self.taskId,
+ "event":"Start"
+ ],
+ "payload":[
+// "device_id": "test_device_id", // 设备ID/用户ID
+ "device_id": deviceId, // 设备ID/用户ID
+ "user_agent": "python", // 浏览器uset-gaent 自定义
+ "dialog_id":dialogId, // 继承上轮对话 空新建一个对话
+ "out_format": "pcm", // 返回音频格式 支持 pcm、opus
+ "volume": "10", // 音量设置
+ "sample_rate": 16000, // 返回音频采样率
+ "voice": "zh_female_shuangkuaisisi_moon_bigtts", // 音色配置
+ "simple_response": true, // 设置是否返回增量文本内容,false 实时返回临时内容 true 返回实时增量内容
+ "custom_variables": [ // 自定义参数
+ "bot_name": "柒先生"
+ ],
+ "session_id":self.taskId// 每轮交互保持一致 发送start -> 大模型返回语音-> 本地播放完毕
+ ]
+ ]
+ let cmd = params.jsonString() ?? ""
+ Logger.log("send" ,cmd)
+ socket?.send(text: cmd)
+
+ let tmpModel = MSChatModel()
+ tmpModel.type = "start"
+ tmpModel.content = nil
+ lastChatModel = tmpModel
+ callBack?(tmpModel)
+ }
+
+ /// 结束说话
+ public func endSpeedch(){
+ let params:[String:Any] =
+ [
+ "header":[
+ "trace_id":self.taskId,
+ "event":"StopSpeech"
+ ],
+ "payload":[
+ "dialog_id":dialogId
+ ]
+ ]
+ let cmd = params.jsonString() ?? ""
+ Logger.log("send" ,cmd)
+ socket?.send(text: cmd)
+ }
+
+ /// 发送内容
+ /// - Parameter content: 内容
+ public func send(content:String){
+ let params:[String:Any] = [
+ "header": [
+ "trace_id": self.taskId,
+ "event": "RequestToRespond"
+ ],
+ "payload": [
+ "dialog_id": dialogId,
+ "type": "prompt", // prompt 直接将文本提交给大模型 transcript 直接将文本转成音频
+ "text": content,
+ "session_id": self.taskId
+ ]
+ ]
+
+ let cmd = params.jsonString() ?? ""
+ Logger.log("send" ,cmd)
+ socket?.send(text: cmd)
+ }
+
+ /// 打断,请求说话
+ public func requestToSpeak(){
+ Logger.log("开始请求说话")
+ let params:[String:Any] =
+ [
+ "header":[
+ "trace_id":self.taskId,
+ "event":"RequestToSpeak"
+ ],
+ "payload":[
+ "dialog_id":dialogId,
+ "session_id": self.taskId,
+ ]
+ ]
+ let cmd = params.jsonString() ?? ""
+ Logger.log("send" ,cmd)
+ socket?.send(text: cmd)
+
+
+ }
+ /// 本地开始播放
+ public func startPlay(){
+ let params:[String:Any] =
+ [
+ "header":[
+ "trace_id":self.taskId,
+ "event":"LocalRespondingStarted"
+ ],
+ "payload":[
+ "dialog_id":dialogId,
+ "session_id": self.taskId,
+ ]
+ ]
+ let cmd = params.jsonString() ?? ""
+ Logger.log("send" ,cmd)
+ socket?.send(text: cmd)
+ }
+
+ /// 本地结束播放
+ public func endPlay(){
+ let params:[String:Any] =
+ [
+ "header":[
+ "trace_id":self.taskId,
+ "event":"LocalRespondingEnded"
+ ],
+ "payload":[
+ "dialog_id":dialogId,
+ "session_id": self.taskId,
+ ]
+ ]
+ let cmd = params.jsonString() ?? ""
+ Logger.log("send" ,cmd)
+ socket?.send(text: cmd)
+ }
+}
+
+extension MSVoiceChat{
+ private func openWebSocket(){
+ taskId = "\(Int(Date().timeIntervalSince1970)*1000)".md5().lowercased()
+ dialogId = "";
+ currentEvent = nil;
+
+// let url = MSConfig.webSocketUrl(api: "voice-chat/ws",addPlatform: false)
+ let url = "ws://118.145.158.207:22082/voice-chat/ws?platform=raisound&bot_type=\(2)&is_auth=0&dt=\(MixSdk.shared.auth.deviceToken)"
+ Logger.log(url)
+ socket = WebSocket(url)
+ socket?.delegate = self
+ }
+}
+
+
+extension MSVoiceChat:WebSocketDelegate{
+ func webSocketOpen() {
+ Logger.log("MSVoiceChat webSocketOpen")
+ connectStateCallBack?(.connected)
+ startSpeedch()
+// startTask()
+ }
+
+ func webSocketClose(_ code: Int, reason: String, wasClean: Bool) {
+ Logger.log("MSVoiceChat webSocketClose",code,reason,wasClean)
+ connectStateCallBack?(.disConnected)
+ }
+
+ func webSocketError(_ error: NSError) {
+ Logger.log("MSVoiceChat webSocketError",error)
+ }
+
+ func webSocketMessageText(_ text: String) {
+ Logger.log("MSVoiceChat receive",text)
+ guard let data = text.data,let model = JSON.decode(MSVoiceChatEvent.self, from: data) else{
+ return
+ }
+ if model.event == "TaskFailed"{
+ closeWebSocket()
+ return
+ }
+ else if model.event == "Started",let dialogId = model.payload?.dialog_id{
+ self.dialogId = dialogId
+ }
+ else if model.event == "DialogStateChanged"{
+ currentEvent = model
+ }
+
+
+ if model.event == "RespondingEnded"{
+ let tmpModel = MSChatModel()
+ tmpModel.type = "end"
+ tmpModel.content = nil
+ lastChatModel = tmpModel
+ callBack?(tmpModel)
+ }
+ if model.event == "RespondingContent",let payload = model.payload,let text = payload.text{
+ let tmpModel = MSChatModel()
+ tmpModel.type = "middle"
+ tmpModel.content = text
+ lastChatModel = tmpModel
+ callBack?(tmpModel)
+ }
+ }
+
+ func webSocketMessageData(_ data: Data){
+ Logger.log("MSVoiceChat receive data ",data)
+ replyCallBack?(data)
+ }
+
+ func webSocketPong(){
+ //Logger.log("MSVoiceChat Pong")
+ }
+
+ func webSocketEnd(_ code: Int, reason: String, wasClean: Bool, error: NSError?){
+ Logger.log("MSVoiceChat webSocketEnd",code,reason,wasClean,error ?? "")
+ }
+}
//// Created by incar on 2024/7/31.
////
//
-//import Foundation
-/////
-//public class MSVoiceChatModel:Codable{
-// public var header:MSVoiceChatHeader = MSVoiceChatHeader()
-// public var payload:MSVoiceChatPayLoad?
-//}
-//
-//public class MSVoiceChatHeader:Codable{
-// public var namespace:String = ""
-// ///
-// public var name:String = ""
-// /// 状态码
-// public var status:Int = 0
-// /// 消息id
-// public var message_id:String = ""
-// /// 任务id
-// public var task_id:String = ""
-// /// 提示语
-// public var status_text:String = ""
-//}
-//public class MSVoiceChatPayLoad:Codable{
-// /// 会话id
-// public var dialog_id:String?
-// ///
-// public var state:String?
-//}
+import Foundation
+///
+/// voiceChat事件
+public class MSVoiceChatEvent:Codable{
+ /// 事件
+ /// Started
+ /// DialogStateChanged
+ /// SpeechContent
+ /// RespondingContent
+ /// RespondingStarted
+ /// RespondingEnded
+ /// LocalRespondingStarted
+ /// LocalRespondingEnded
+ /// TaskFailed
+ public var event:String = ""
+ ///
+ public var msg:String = ""
+ /// 状态码 20000000
+ public var status:Int = 0
+ /// 任务id
+ public var task_id:String = ""
+ /// 内容
+ public var payload:MSVoiceChatPayLoad?
+}
+
+public class MSVoiceChatPayLoad:Codable{
+ /// 会话id
+ public var dialog_id:String?
+ /// Listening 可以说话
+ /// Thinking 正在思考
+ /// Responding 正在回复
+ public var state:String?
+
+ /// 是否结束
+ public var finished:Bool?
+ /// 识别的文字
+ public var text:String? = ""
+}
return "deepseek"
}
}
+ internal var voiceChatValue:String{
+ switch self {
+ case .aliyun:
+ return "qianwen"
+ case .xunfei:
+ return "xinghuo"
+ case .yiyan:
+ return "wenxin"
+ case .doubao:
+ return "doubao"
+ case .kimi:
+ return "kimi"
+ case .chatGPT:
+ return "chatGPT"
+ case .deepseek:
+ return "deepseek"
+ }
+ }
public var title:String{
switch self {
/// 大模型对话
public let chat = MSChat()
+ /// 语音对话二代
+ public let voiceChat = MSVoiceChat()
+
/// 文字转语音
public let tts = MSTts()
public let chatGptTrans = MSChatGptTranscription()
-// /// 语音聊天
-// public let voiceChat = MSVoiceChat()
-
-
/// 切换平台回调
var changePlatformCallBack:MSPlatformChangeCallBack?
public var aiPlatform:MSPlatform = .doubao
/// aikey
public var aiKey:String? = ""
-
-// /// 鉴权
-// public lazy var auth:MSAuth = {
-// return MSAuth()
-// }()
func setupWebsoket(){
MixSdk.shared.stt.setupWebSocket()
<key>isShown</key>
<false/>
<key>orderHint</key>
- <integer>2</integer>
+ <integer>3</integer>
</dict>
<key>IQKeyboardManagerSwift.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
- <integer>1</integer>
+ <integer>2</integer>
</dict>
<key>Pods-Example.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
- <integer>3</integer>
+ <integer>4</integer>
</dict>
<key>SnapKit-SnapKit_Privacy.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
- <integer>5</integer>
+ <integer>6</integer>
</dict>
<key>SnapKit.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
- <integer>4</integer>
+ <integer>5</integer>
</dict>
<key>Toast-Swift-Toast-Swift.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
- <integer>7</integer>
+ <integer>8</integer>
</dict>
<key>Toast-Swift.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
- <integer>6</integer>
+ <integer>7</integer>
</dict>
</dict>
<key>SuppressBuildableAutocreation</key>
--- /dev/null
+{
+ "buildCommand" : {
+ "command" : "build",
+ "skipDependencies" : false,
+ "style" : "buildOnly"
+ },
+ "configuredTargets" : [
+
+ ],
+ "continueBuildingAfterErrors" : false,
+ "dependencyScope" : "workspace",
+ "enableIndexBuildArena" : false,
+ "hideShellScriptEnvironment" : false,
+ "parameters" : {
+ "action" : "build",
+ "overrides" : {
+
+ }
+ },
+ "qos" : "default",
+ "schemeCommand" : "launch",
+ "showNonLoggedProgress" : true,
+ "useDryRun" : false,
+ "useImplicitDependencies" : false,
+ "useLegacyBuildLocations" : false,
+ "useParallelTargets" : true
+}
\ No newline at end of file
--- /dev/null
+{"client":{"name":"basic","version":0,"file-system":"device-agnostic","perform-ownership-analysis":"no"},"targets":{"":["<all>"]},"commands":{"<all>":{"tool":"phony","inputs":["<WorkspaceHeaderMapVFSFilesWritten>"],"outputs":["<all>"]},"P0:::Gate WorkspaceHeaderMapVFSFilesWritten":{"tool":"phony","inputs":[],"outputs":["<WorkspaceHeaderMapVFSFilesWritten>"]}}}
\ No newline at end of file
--- /dev/null
+Target dependency graph (0 target)
\ No newline at end of file