/* Begin PBXBuildFile section */
180255BA2C3BE6BD005AFC5F /* SendRecordTooBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 180255B92C3BE6BD005AFC5F /* SendRecordTooBar.swift */; };
18068D022CDCC57C006B6EF5 /* PcmPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18068D012CDCC57C006B6EF5 /* PcmPlayer.swift */; };
- 1811885B2C1BE80B00C23FAB /* 1.pcm in Resources */ = {isa = PBXBuildFile; fileRef = 181188592C1BE80B00C23FAB /* 1.pcm */; };
18132AF82C1C4D15002F6B52 /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 18132AF62C1C4D15002F6B52 /* libz.tbd */; };
- 181B3B372C5B1C54000AED7D /* AIAudio_2.sbc in Resources */ = {isa = PBXBuildFile; fileRef = 181B3B352C5B1C54000AED7D /* AIAudio_2.sbc */; };
18204BD42C193F9A00F94E69 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18204BD32C193F9A00F94E69 /* AppDelegate.swift */; };
18204BD62C193F9A00F94E69 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18204BD52C193F9A00F94E69 /* SceneDelegate.swift */; };
18204BD82C193F9A00F94E69 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18204BD72C193F9A00F94E69 /* ViewController.swift */; };
18204BDB2C193F9A00F94E69 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 18204BD92C193F9A00F94E69 /* Main.storyboard */; };
18204BDD2C193F9D00F94E69 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 18204BDC2C193F9D00F94E69 /* Assets.xcassets */; };
18204BE02C193F9D00F94E69 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 18204BDE2C193F9D00F94E69 /* LaunchScreen.storyboard */; };
- 18204BEE2C19428100F94E69 /* MixSdk.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 18204BEC2C19417000F94E69 /* MixSdk.framework */; };
1843A7962C4E060D00FC010F /* LogViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1843A7952C4E060D00FC010F /* LogViewController.swift */; };
184FFC492C218FC4006F664D /* PlayerManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 184FFC482C218FC4006F664D /* PlayerManager.swift */; };
184FFC4D2C2199E5006F664D /* SendToolBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 184FFC4C2C2199E5006F664D /* SendToolBar.swift */; };
- 186BFD4B2C57952600673D8C /* VoiceChatViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 186BFD4A2C57952600673D8C /* VoiceChatViewController.swift */; };
188695A62C5A3CAD00B9B89F /* Date.swift in Sources */ = {isa = PBXBuildFile; fileRef = 188695A52C5A3CAD00B9B89F /* Date.swift */; };
- 18A2DB6C2C40C5CC00A8731B /* AIAudio.sbc in Resources */ = {isa = PBXBuildFile; fileRef = 18A2DB6B2C40C5CC00A8731B /* AIAudio.sbc */; };
- 18ABF57A2C3F884600C60E9B /* 2.pcm in Resources */ = {isa = PBXBuildFile; fileRef = 18ABF5792C3F884600C60E9B /* 2.pcm */; };
+ 18B1A36E2CE1D44A00DD4A7C /* MixSdk.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 18204BEC2C19417000F94E69 /* MixSdk.framework */; };
+ 18B1A3702CE1E12800DD4A7C /* BluetoothManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18B1A36F2CE1E12800DD4A7C /* BluetoothManager.swift */; };
+ 18B1A3732CE1EF4900DD4A7C /* BleChatVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18B1A3722CE1EF4900DD4A7C /* BleChatVC.swift */; };
+ 18B1A3752CE1EF6200DD4A7C /* PhoneChatVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18B1A3742CE1EF6200DD4A7C /* PhoneChatVC.swift */; };
18C42C802C242AAC000D8860 /* liblzma.5.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 18C42C7E2C242AAC000D8860 /* liblzma.5.tbd */; };
18C42C822C242AD3000D8860 /* CoreAudio.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 18C42C812C242AD3000D8860 /* CoreAudio.framework */; };
18C42C842C242B01000D8860 /* libiconv.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 18C42C832C242B01000D8860 /* libiconv.tbd */; };
18C42C862C242B0B000D8860 /* libbz2.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 18C42C852C242B0A000D8860 /* libbz2.tbd */; };
- 18C856202C3B81B900EAE874 /* tts.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = 18C8561E2C3B81B800EAE874 /* tts.mp3 */; };
18DB20482C213D730070D571 /* AiMouseAudioRecord.m in Sources */ = {isa = PBXBuildFile; fileRef = 18DB20462C213D730070D571 /* AiMouseAudioRecord.m */; };
18DB20492C213D730070D571 /* AiMouseRecordManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 18DB20472C213D730070D571 /* AiMouseRecordManager.m */; };
18EBBA152CDE1791006F3B80 /* VoiceChatCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18EBBA142CDE1791006F3B80 /* VoiceChatCell.swift */; };
18EC39D12C4F421C00964461 /* Config.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18EC39D02C4F421C00964461 /* Config.swift */; };
- 18FF930C2C39263A0006452C /* cat.png in Resources */ = {isa = PBXBuildFile; fileRef = 18FF930A2C39263A0006452C /* cat.png */; };
F59940004E1C16053FBD42F5 /* Pods_Example.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4B555BC40ACD8F31DBCDD1A2 /* Pods_Example.framework */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
180255B92C3BE6BD005AFC5F /* SendRecordTooBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SendRecordTooBar.swift; sourceTree = "<group>"; };
18068D012CDCC57C006B6EF5 /* PcmPlayer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PcmPlayer.swift; sourceTree = "<group>"; };
- 181188592C1BE80B00C23FAB /* 1.pcm */ = {isa = PBXFileReference; lastKnownFileType = file; path = 1.pcm; sourceTree = "<group>"; };
18132AF62C1C4D15002F6B52 /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; };
- 181B3B352C5B1C54000AED7D /* AIAudio_2.sbc */ = {isa = PBXFileReference; lastKnownFileType = file; path = AIAudio_2.sbc; sourceTree = "<group>"; };
18204BD02C193F9A00F94E69 /* Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Example.app; sourceTree = BUILT_PRODUCTS_DIR; };
18204BD32C193F9A00F94E69 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
18204BD52C193F9A00F94E69 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = "<group>"; };
1843A7952C4E060D00FC010F /* LogViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogViewController.swift; sourceTree = "<group>"; };
184FFC482C218FC4006F664D /* PlayerManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerManager.swift; sourceTree = "<group>"; };
184FFC4C2C2199E5006F664D /* SendToolBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SendToolBar.swift; sourceTree = "<group>"; };
- 186BFD4A2C57952600673D8C /* VoiceChatViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VoiceChatViewController.swift; sourceTree = "<group>"; };
188695A52C5A3CAD00B9B89F /* Date.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Date.swift; sourceTree = "<group>"; };
- 18A2DB6B2C40C5CC00A8731B /* AIAudio.sbc */ = {isa = PBXFileReference; lastKnownFileType = file; path = AIAudio.sbc; sourceTree = "<group>"; };
- 18ABF5792C3F884600C60E9B /* 2.pcm */ = {isa = PBXFileReference; lastKnownFileType = file; path = 2.pcm; sourceTree = "<group>"; };
+ 18B1A36F2CE1E12800DD4A7C /* BluetoothManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BluetoothManager.swift; sourceTree = "<group>"; };
+ 18B1A3722CE1EF4900DD4A7C /* BleChatVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BleChatVC.swift; sourceTree = "<group>"; };
+ 18B1A3742CE1EF6200DD4A7C /* PhoneChatVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhoneChatVC.swift; sourceTree = "<group>"; };
18C42C7E2C242AAC000D8860 /* liblzma.5.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = liblzma.5.tbd; path = usr/lib/liblzma.5.tbd; sourceTree = SDKROOT; };
18C42C812C242AD3000D8860 /* CoreAudio.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreAudio.framework; path = System/Library/Frameworks/CoreAudio.framework; sourceTree = SDKROOT; };
18C42C832C242B01000D8860 /* libiconv.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libiconv.tbd; path = usr/lib/libiconv.tbd; sourceTree = SDKROOT; };
18C42C852C242B0A000D8860 /* libbz2.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libbz2.tbd; path = usr/lib/libbz2.tbd; sourceTree = SDKROOT; };
- 18C8561E2C3B81B800EAE874 /* tts.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; path = tts.mp3; sourceTree = "<group>"; };
18DB20432C213D720070D571 /* Example-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Example-Bridging-Header.h"; sourceTree = "<group>"; };
18DB20442C213D730070D571 /* AiMouseRecordManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AiMouseRecordManager.h; sourceTree = "<group>"; };
18DB20452C213D730070D571 /* AiMouseAudioRecord.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AiMouseAudioRecord.h; sourceTree = "<group>"; };
18DB20472C213D730070D571 /* AiMouseRecordManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AiMouseRecordManager.m; sourceTree = "<group>"; };
18EBBA142CDE1791006F3B80 /* VoiceChatCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VoiceChatCell.swift; sourceTree = "<group>"; };
18EC39D02C4F421C00964461 /* Config.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Config.swift; sourceTree = "<group>"; };
- 18FF930A2C39263A0006452C /* cat.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = cat.png; sourceTree = "<group>"; };
4B555BC40ACD8F31DBCDD1A2 /* Pods_Example.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Example.framework; sourceTree = BUILT_PRODUCTS_DIR; };
759E170040ACED4895C8A49D /* Pods-Example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Example.release.xcconfig"; path = "Target Support Files/Pods-Example/Pods-Example.release.xcconfig"; sourceTree = "<group>"; };
C85BC820E1AA39AC970C9E79 /* Pods-Example.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Example.debug.xcconfig"; path = "Target Support Files/Pods-Example/Pods-Example.debug.xcconfig"; sourceTree = "<group>"; };
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
+ 18B1A36E2CE1D44A00DD4A7C /* MixSdk.framework in Frameworks */,
18C42C862C242B0B000D8860 /* libbz2.tbd in Frameworks */,
18C42C842C242B01000D8860 /* libiconv.tbd in Frameworks */,
18C42C822C242AD3000D8860 /* CoreAudio.framework in Frameworks */,
18C42C802C242AAC000D8860 /* liblzma.5.tbd in Frameworks */,
18132AF82C1C4D15002F6B52 /* libz.tbd in Frameworks */,
- 18204BEE2C19428100F94E69 /* MixSdk.framework in Frameworks */,
F59940004E1C16053FBD42F5 /* Pods_Example.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
181188582C1BE80B00C23FAB /* Resource */ = {
isa = PBXGroup;
children = (
- 18FF930A2C39263A0006452C /* cat.png */,
- 18A2DB6B2C40C5CC00A8731B /* AIAudio.sbc */,
- 181B3B352C5B1C54000AED7D /* AIAudio_2.sbc */,
- 181188592C1BE80B00C23FAB /* 1.pcm */,
- 18ABF5792C3F884600C60E9B /* 2.pcm */,
- 18C8561E2C3B81B800EAE874 /* tts.mp3 */,
);
path = Resource;
sourceTree = "<group>";
18204BD22C193F9A00F94E69 /* Example */ = {
isa = PBXGroup;
children = (
+ 181188582C1BE80B00C23FAB /* Resource */,
186E75102C3E33EB0040671B /* Tools */,
18DB20412C213D600070D571 /* Record */,
- 181188582C1BE80B00C23FAB /* Resource */,
18204BD32C193F9A00F94E69 /* AppDelegate.swift */,
18204BD52C193F9A00F94E69 /* SceneDelegate.swift */,
- 18204BD72C193F9A00F94E69 /* ViewController.swift */,
- 186BFD4A2C57952600673D8C /* VoiceChatViewController.swift */,
1843A7952C4E060D00FC010F /* LogViewController.swift */,
+ 18204BD72C193F9A00F94E69 /* ViewController.swift */,
+ 18B1A36F2CE1E12800DD4A7C /* BluetoothManager.swift */,
+ 18B1A3742CE1EF6200DD4A7C /* PhoneChatVC.swift */,
+ 18B1A3722CE1EF4900DD4A7C /* BleChatVC.swift */,
18EBBA142CDE1791006F3B80 /* VoiceChatCell.swift */,
18EC39D02C4F421C00964461 /* Config.swift */,
18204BD92C193F9A00F94E69 /* Main.storyboard */,
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
- 181B3B372C5B1C54000AED7D /* AIAudio_2.sbc in Resources */,
- 18FF930C2C39263A0006452C /* cat.png in Resources */,
- 1811885B2C1BE80B00C23FAB /* 1.pcm in Resources */,
18204BE02C193F9D00F94E69 /* LaunchScreen.storyboard in Resources */,
- 18C856202C3B81B900EAE874 /* tts.mp3 in Resources */,
18204BDD2C193F9D00F94E69 /* Assets.xcassets in Resources */,
- 18A2DB6C2C40C5CC00A8731B /* AIAudio.sbc in Resources */,
- 18ABF57A2C3F884600C60E9B /* 2.pcm in Resources */,
18204BDB2C193F9A00F94E69 /* Main.storyboard in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
files = (
18DB20482C213D730070D571 /* AiMouseAudioRecord.m in Sources */,
18204BD82C193F9A00F94E69 /* ViewController.swift in Sources */,
- 186BFD4B2C57952600673D8C /* VoiceChatViewController.swift in Sources */,
1843A7962C4E060D00FC010F /* LogViewController.swift in Sources */,
18EBBA152CDE1791006F3B80 /* VoiceChatCell.swift in Sources */,
18068D022CDCC57C006B6EF5 /* PcmPlayer.swift in Sources */,
+ 18B1A3732CE1EF4900DD4A7C /* BleChatVC.swift in Sources */,
18204BD42C193F9A00F94E69 /* AppDelegate.swift in Sources */,
+ 18B1A3752CE1EF6200DD4A7C /* PhoneChatVC.swift in Sources */,
+ 18B1A3702CE1E12800DD4A7C /* BluetoothManager.swift in Sources */,
184FFC492C218FC4006F664D /* PlayerManager.swift in Sources */,
18DB20492C213D730070D571 /* AiMouseRecordManager.m in Sources */,
18204BD62C193F9A00F94E69 /* SceneDelegate.swift in Sources */,
);
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = Example/Info.plist;
- INFOPLIST_KEY_CFBundleDisplayName = MixSdk;
+ INFOPLIST_KEY_CFBundleDisplayName = VoiceChat;
+ INFOPLIST_KEY_LSApplicationCategoryType = "";
+ INFOPLIST_KEY_NSBluetoothAlwaysUsageDescription = "需要开启蓝牙功能";
INFOPLIST_KEY_NSMicrophoneUsageDescription = "要使用麦克风录音哦";
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen;
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.0;
- PRODUCT_BUNDLE_IDENTIFIER = ZhiHui.Example;
+ PRODUCT_BUNDLE_IDENTIFIER = voicechat.Example.ZhiHui;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_OBJC_BRIDGING_HEADER = "Example/Record/Example-Bridging-Header.h";
);
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = Example/Info.plist;
- INFOPLIST_KEY_CFBundleDisplayName = MixSdk;
+ INFOPLIST_KEY_CFBundleDisplayName = VoiceChat;
+ INFOPLIST_KEY_LSApplicationCategoryType = "";
+ INFOPLIST_KEY_NSBluetoothAlwaysUsageDescription = "需要开启蓝牙功能";
INFOPLIST_KEY_NSMicrophoneUsageDescription = "要使用麦克风录音哦";
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen;
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.0;
- PRODUCT_BUNDLE_IDENTIFIER = ZhiHui.Example;
+ PRODUCT_BUNDLE_IDENTIFIER = voicechat.Example.ZhiHui;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_OBJC_BRIDGING_HEADER = "Example/Record/Example-Bridging-Header.h";
//
import UIKit
-import MixSdk
import IQKeyboardManagerSwift
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
- MixSdk.shared.initSDK()
- MixSdk.shared.isDebug = true
-
IQKeyboardManager.shared.enable = true
IQKeyboardManager.shared.resignOnTouchOutside = true
--- /dev/null
+//
+// VoiceChatViewController.swift
+// Example
+//
+// Created by incar on 2024/7/29.
+//
+
+import UIKit
+import MixSdk
+import SnapKit
+import Toast_Swift
+
+
+
+class BleChatVC: UIViewController{
+ var voiceData:NSMutableData = NSMutableData()
+
+ let tableView = UITableView(frame:.zero, style: .plain)
+ let stateLabel = UIButton(type: .custom)
+ var dataArray = [CellModel]()
+
+ deinit {
+ print("销毁")
+ PlayerManager.shared.stop()
+ MixSdk.shared.voiceChat.finish()
+ }
+
+ override func viewDidLoad() {
+ super.viewDidLoad()
+ // Do any additional setup after loading the view.
+
+ setupViews()
+ setupVoiceChat()
+ }
+
+ func setupViews(){
+ self.title = "ble chat"
+ self.view.backgroundColor = .white
+ self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: "结束说话", style: .done, target: self, action: #selector(endSpeakAction))
+
+ tableView.delegate = self
+ tableView.dataSource = self
+ tableView.estimatedRowHeight = 60
+ tableView.register(VoiceChatCell.self, forCellReuseIdentifier: "VoiceChatCell")
+ view.addSubview(tableView)
+ tableView.snp.makeConstraints { make in
+ make.top.equalToSuperview().inset(80)
+ make.left.right.equalToSuperview()
+ make.bottom.equalToSuperview().inset(80)
+ }
+
+ stateLabel.backgroundColor = .blue
+ stateLabel.setTitle("", for: .normal)
+ stateLabel.addTarget(self, action: #selector(requestToSpeak), for: .touchUpInside)
+ view.addSubview(stateLabel)
+ stateLabel.snp.makeConstraints { make in
+ make.height.equalTo(80)
+ make.left.right.equalToSuperview()
+ make.bottom.equalToSuperview()
+ }
+ }
+
+ func setupVoiceChat(){
+ MixSdk.shared.ble.authCallBack = {[unowned self] in
+ self.view.makeToast("鉴权成功!" )
+ }
+ MixSdk.shared.voiceChat.start()
+ MixSdk.shared.voiceChat.eventCallBack = { [unowned self] model in
+
+ if model.event == "RespondingEnded"{
+ self.saveToFile()
+ }
+ if model.payload?.state == "Listening"{
+
+ self.stateLabel.setTitle("正在聆听...", for: .normal)
+ }
+ if model.payload?.state == "Thinking"{
+ self.stateLabel.setTitle("正在思考...", for: .normal)
+ }
+ if model.payload?.state == "Responding"{
+ self.stateLabel.setTitle("正在回复...", for: .normal)
+ self.voiceData = NSMutableData()
+ }
+
+ if model.event == "SpeechContent",let payload = model.payload,let text = payload.text,let isFinished = payload.finished,isFinished == true{
+ let cellModel = CellModel()
+ cellModel.text = text
+ cellModel.isSend = true
+ dataArray.append(cellModel)
+ tableView.reloadData()
+ tableView.scrollToRow(at: IndexPath(row: dataArray.count - 1, section: 0), at: .none, animated: true)
+ }
+ if model.event == "RespondingContent",let payload = model.payload,let text = payload.text,let isFinished = payload.finished,isFinished == true{
+ let cellModel = CellModel()
+ cellModel.text = text
+ cellModel.isSend = false
+ dataArray.append(cellModel)
+ tableView.reloadData()
+ tableView.scrollToRow(at: IndexPath(row: dataArray.count - 1, section: 0), at: .none, animated: true)
+ }
+ MixSdk.shared.voiceChat.replyCallBack = {[unowned self] data in
+ self.voiceData.append(data)
+ }
+ }
+ }
+
+ @objc func requestToSpeak(){
+ PlayerManager.shared.stop()
+ MixSdk.shared.voiceChat.requestToSpeak()
+ }
+ func saveToFile(){
+ let mp3Path = NSHomeDirectory() + "/Documents/1.mp3"
+
+ let url = URL(fileURLWithPath: mp3Path)
+ do {
+ try self.voiceData.write(to: url, options: .atomic)
+ print(mp3Path)
+ self.stateLabel.setTitle("正在播放...", for: .normal)
+ MixSdk.shared.voiceChat.startPlay()
+ PlayerManager.shared.play(filePath: mp3Path) {[unowned self] success in
+ self.stateLabel.setTitle("结束播放...", for: .normal)
+ MixSdk.shared.voiceChat.endPlay()
+ }
+ }
+ catch{
+ print(error)
+ }
+ }
+ @objc func endSpeakAction(){
+ MixSdk.shared.voiceChat.endSpeedch()
+ }
+}
+extension BleChatVC:UITableViewDelegate,UITableViewDataSource {
+ func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
+ return dataArray.count
+ }
+
+ func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
+ var cell = tableView.dequeueReusableCell(withIdentifier: "VoiceChatCell", for: indexPath) as? VoiceChatCell
+
+ if cell == nil{
+ cell = VoiceChatCell(style: .default, reuseIdentifier: "VoiceChatCell")
+ }
+ cell!.model = dataArray[indexPath.row]
+ return cell!
+ }
+ func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
+
+ }
+}
--- /dev/null
+//
+// IBluetooth.swift
+// IOTBluetooth
+//
+// Created by yaozuopan on 2024/2/4.
+//
+
+import Foundation
+import CoreBluetooth
+import MixSdk
+
+/// 主服务
+let kserviceUUID: String = "FFF1"
+let kVoiceUUID: String = "0783B03E-8535-B5A0-7140-A304D2194CB8"
+let kDeviceInfoUUID: String = "0783B63E-8535-B5A0-7140-A304D2495CB6"
+
+public typealias IBDiscoverHandler = (_ peripheral: CBPeripheral) -> Void
+public typealias IBDidConectedHandler = (_ peripheral: CBPeripheral) -> Void
+public typealias IBDisConectedHandler = (_ peripheral: CBPeripheral) -> Void
+
+class BluetoothManager: NSObject {
+ static let shared = BluetoothManager()
+
+ /// 2AE6
+ private var writeFileCharacteristic: CBCharacteristic!
+ /// 中央管理类,管理蓝牙的扫描连接
+ var centralManager: CBCentralManager!
+ /// 当前连接设备
+ var currentPeripheral: CBPeripheral?
+
+ /// 扫描到设备
+ var discoverCallBack:IBDiscoverHandler?
+ /// 连上设备
+ var didConnectedCallBack:IBDidConectedHandler?
+ /// 设备断开连接
+ var disConnectedCallBack:IBDisConectedHandler?
+
+ private override init() {
+ super.init()
+ centralManager = CBCentralManager(delegate: self, queue: nil)
+ setupFileSdk()
+ }
+
+ func setupFileSdk(){
+ //初始化
+ MixSdk.shared.initSDK()
+ //isDebug为true则打印日志,false则不打印日志
+ MixSdk.shared.isDebug = true
+ //设置sdk数据回调
+ MixSdk.shared.ble.sdkToDeviceCallback = {[unowned self] data in
+ //这里将回调的数据发给蓝牙设备
+ self.send(data)
+ }
+ }
+}
+
+extension BluetoothManager{
+ /// 开始扫描
+ func startScan() {
+ print("开始扫描...")
+ centralManager.scanForPeripherals(withServices: nil)
+// centralManager.scanForPeripherals(withServices: [CBUUID(string: kserviceUUID)], options: nil)
+ if let peripheral = centralManager.retrieveConnectedPeripherals(withServices: [CBUUID(string: kserviceUUID)]).first{
+ discoverCallBack?(peripheral)
+ }
+ }
+
+ /// 停止扫描
+ func stopScan(){
+ print("停止扫描蓝牙设备...")
+ centralManager.stopScan()
+ }
+
+ /// 连接蓝牙设备
+ func connect(_ peripheral: CBPeripheral) {
+ print("正在连接。。。", peripheral.name ?? "")
+ centralManager.connect(peripheral)
+ }
+
+ /// 断开与蓝牙设备的连接
+ func cancel() {
+ guard let peripheral = currentPeripheral else { return }
+ centralManager.cancelPeripheralConnection(peripheral)
+ }
+
+ func send(_ data: Data) {
+ guard let peripheral = currentPeripheral else{
+ print("手机未连上蓝牙设备!")
+ return
+ }
+ peripheral.writeValue(data, for: writeFileCharacteristic, type: .withoutResponse)
+ }
+}
+
+extension BluetoothManager: CBCentralManagerDelegate {
+ func centralManagerDidUpdateState(_ central: CBCentralManager) {
+ switch central.state {
+ case .unknown:
+ print("unknown")
+ case .resetting:
+ print("resetting")
+ case .unsupported:
+ print("unsupported")
+ case .unauthorized:
+ print("unauthorized")
+ case .poweredOff:
+ print("poweredOff")
+ case .poweredOn:
+ startScan()
+ print("poweredOn")
+ @unknown default:
+ break
+ }
+ }
+
+ func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
+// IBManager.discoverHandler?(device)
+ discoverCallBack?(peripheral)
+ }
+
+ func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
+ currentPeripheral = peripheral
+ peripheral.delegate = self
+ peripheral.discoverServices([CBUUID(string: kserviceUUID)])
+ print("连接成功", peripheral.name ?? "")
+ didConnectedCallBack?(peripheral)
+ }
+
+ func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) {
+ print("连接失败",error?.localizedDescription ?? "" )
+ currentPeripheral = nil
+ }
+
+ func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {
+ print("已断开连接")
+ disConnectedCallBack?(peripheral)
+ }
+}
+
+extension BluetoothManager: CBPeripheralDelegate {
+ func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
+ peripheral
+ .services?
+ .forEach {
+ peripheral.discoverCharacteristics(nil, for: $0)
+ }
+ }
+ func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
+ service
+ .characteristics?
+ .forEach {
+ if $0.uuid.uuidString == kVoiceUUID {
+ peripheral.setNotifyValue(true, for: $0)
+ return
+ }
+ if $0.uuid.uuidString == kDeviceInfoUUID {
+ writeFileCharacteristic = $0
+ peripheral.setNotifyValue(true, for: $0)
+ return
+ }
+ }
+ }
+ func peripheral(_ peripheral: CBPeripheral, didUpdateNotificationStateFor characteristic: CBCharacteristic, error: Error?) {
+ peripheral.readValue(for: characteristic)
+ }
+
+ func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
+ // 收到蓝牙设备的数据,转发给sdk处理
+ if characteristic.uuid.uuidString == kVoiceUUID {
+ MixSdk.shared.ble.sendToSdk(voiceData: characteristic.value)
+ }
+ else if characteristic.uuid.uuidString == kDeviceInfoUUID{
+ MixSdk.shared.ble.sendToSdk(infoData: characteristic.value)
+ }
+ }
+
+ func peripheral(_ peripheral: CBPeripheral, didWriteValueFor characteristic: CBCharacteristic, error: Error?) {
+ // sdk将回调的数据传给ble设备后,告知sdk写入的情况
+// MixSdk.shared.ble.didWriteToDevice(characteristic: characteristic, error: error)
+ }
+}
import Foundation
-
-let gptKey = "sk-proj-04ogvrZQqyaiv1OWw8OZT3BlbkFJG9Gyr8oFvRdmr3oXQNbX"
-let gptModel = "gpt-4-turbo"
-
//废弃
//let deviceId = "281106AFB0F7"
//let productId = "pafOCKLiUbeVHbdZlddH"
-class VoiceChatViewController: UIViewController{
-
+class PhoneChatVC: UIViewController{
let recordMamager = AiMouseRecordManager()
var voiceData:NSMutableData = NSMutableData()
let tableView = UITableView(frame:.zero, style: .plain)
- let stateLabel = UILabel()
+ let stateLabel = UIButton(type: .custom)
var dataArray = [CellModel]()
+
deinit {
print("销毁")
-
+ PlayerManager.shared.stop()
recordMamager.stop()
MixSdk.shared.voiceChat.finish()
+
}
override func viewDidLoad() {
}
func setupViews(){
- self.title = "VoiceChat"
+ self.title = "phone chat"
self.view.backgroundColor = .white
tableView.snp.makeConstraints { make in
make.top.equalToSuperview().inset(80)
make.left.right.equalToSuperview()
- make.bottom.equalToSuperview().offset(80)
+ make.bottom.equalToSuperview().inset(80)
}
stateLabel.backgroundColor = .blue
- stateLabel.textAlignment = .center
- stateLabel.textColor = .white
+ stateLabel.setTitle("", for: .normal)
+ stateLabel.addTarget(self, action: #selector(requestToSpeak), for: .touchUpInside)
view.addSubview(stateLabel)
stateLabel.snp.makeConstraints { make in
make.height.equalTo(80)
}
recordMamager.start()
}
-
func setupVoiceChat(){
MixSdk.shared.voiceChat.start()
- MixSdk.shared.voiceChat.eventCallBack = { [weak self] model in
- guard let self = self else {
- return
- }
+ MixSdk.shared.voiceChat.eventCallBack = { [unowned self] model in
if model.event == "RespondingEnded"{
self.saveToFile()
- self.voiceData = NSMutableData()
}
if model.payload?.state == "Listening"{
- self.stateLabel.text = "正在聆听..."
+ self.stateLabel.setTitle("正在聆听...", for: .normal)
}
if model.payload?.state == "Thinking"{
- self.stateLabel.text = "正在思考..."
+ self.stateLabel.setTitle("正在思考...", for: .normal)
}
if model.payload?.state == "Responding"{
- self.stateLabel.text = "正在回复..."
+ self.stateLabel.setTitle("正在回复...", for: .normal)
+ self.voiceData = NSMutableData()
}
if model.event == "SpeechContent",let payload = model.payload,let text = payload.text,let isFinished = payload.finished,isFinished == true{
let cellModel = CellModel()
cellModel.text = text
- cellModel.isSend = false
+ cellModel.isSend = true
dataArray.append(cellModel)
tableView.reloadData()
- tableView.scrollToRow(at: IndexPath(row: dataArray.count, section: 0), at: .none, animated: true)
+ tableView.scrollToRow(at: IndexPath(row: dataArray.count - 1, section: 0), at: .none, animated: true)
}
if model.event == "RespondingContent",let payload = model.payload,let text = payload.text,let isFinished = payload.finished,isFinished == true{
let cellModel = CellModel()
cellModel.isSend = false
dataArray.append(cellModel)
tableView.reloadData()
- tableView.scrollToRow(at: IndexPath(row: dataArray.count, section: 0), at: .none, animated: true)
+ tableView.scrollToRow(at: IndexPath(row: dataArray.count - 1, section: 0), at: .none, animated: true)
}
- MixSdk.shared.voiceChat.replyCallBack = {[weak self] data in
- self?.voiceData.append(data)
+ MixSdk.shared.voiceChat.replyCallBack = {[unowned self] data in
+ self.voiceData.append(data)
}
}
}
+
+ @objc func requestToSpeak(){
+ PlayerManager.shared.stop()
+ MixSdk.shared.voiceChat.requestToSpeak()
+ }
func saveToFile(){
- let mp3Path = NSHomeDirectory() + "/Documents/1.mp3"
+ let mp3Path = NSHomeDirectory() + "/Documents/1111\(Int(Date().timeIntervalSince1970)).mp3"
let url = URL(fileURLWithPath: mp3Path)
do {
try self.voiceData.write(to: url, options: .atomic)
print(mp3Path)
- self.stateLabel.text = "正在播放..."
+ self.stateLabel.setTitle("正在播放...", for: .normal)
MixSdk.shared.voiceChat.startPlay()
- PlayerManager.shared.play(filePath: mp3Path) { success in
- self.stateLabel.text = "结束播放..."
+ PlayerManager.shared.play(filePath: mp3Path) {[unowned self] success in
+ self.stateLabel.setTitle("结束播放...", for: .normal)
MixSdk.shared.voiceChat.endPlay()
}
}
}
}
}
-extension VoiceChatViewController:UITableViewDelegate,UITableViewDataSource {
+extension PhoneChatVC:UITableViewDelegate,UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dataArray.count
}
#import "AiMouseAudioRecord.h"
#import "AiMouseRecordManager.h"
+#import "AiMouseAudioRecord.h"
+////
+//// Date.swift
+//// Example
+////
+//// Created by incar on 2024/7/31.
+////
//
-// Date.swift
-// Example
+//import Foundation
//
-// Created by incar on 2024/7/31.
-//
-
-import Foundation
-
-// MARK: - Extension Date
-extension Date {
-
- static let formatter = DateFormatter()
-
- struct Format {
-
- let rawValue: String
- init(_ rawValue: String) {
- self.rawValue = rawValue
- }
-
- /// e.g. 01:01:01
- static let time: Format = .init("HH:mm:ss")
-
- /// e.g. 01:01
- static let shortTime: Format = .init("HH:mm")
-
- /// e.g. 01-01
- static let date: Format = .init("MM-dd")
-
- /// e.g. 01月01日
- static let date1: Format = .init("MM月dd日")
-
- /// e.g. 2020-01-01
- static let short: Format = .init("yyyy-MM-dd")
-
- /// e.g. 2020年01月01日
- static let short1: Format = .init("yyy年MM月dd日")
-
- /// e.g. 2020-01-01 01:01
- static let middle: Format = .init("yyyy-MM-dd HH:mm")
-
- /// e.g. 2020年01月01日 01:01
- static let middle1: Format = .init("yyy年MM月dd日 HH:mm")
-
- /// e.g. 2020-01-01 01:01:01
- static let long: Format = .init("yyyy-MM-dd HH:mm:ss")
-
- /// e.g. 2020年01月01日 01:01:01
- static let long1: Format = .init("yyyy年MM月dd日 HH:mm:ss")
-
- /// e.g. 2020-01-01 01:01:01.088
- static let full: Format = .init("yyyy-MM-dd HH:mm:ss.SSS")
-
- /// e.g. 2020年01月01日 01:01:01.088
- static let full1: Format = .init("yyyy年MM月dd日 HH:mm:ss.SSS")
- }
-
- /// 格式化成本地时间
- func format(_ format: Date.Format = .middle) -> String {
- Self.formatter.dateFormat = format.rawValue
- return Self.formatter.string(from: self)
- }
-
- enum Unit {
- case minute(Int)
- case hour(Int)
- case day(Int)
-
- /// 转换成秒
- var second: TimeInterval {
- switch self {
- case .minute(let minute): return 60.0 * Double(minute)
- case .hour(let hour): return 60.0 * 60.0 * Double(hour)
- case .day(let day): return 60.0 * 60.0 * 24.0 * Double(day)
- }
- }
- }
-}
-
-let today: Date = .init()
-
-/// 本地化
-extension Date {
- /// 星期几
- enum Week: Int {
- /// 星期日
- case sun = 1
- /// 星期一
- case mon
- /// 星期二
- case tue
- /// 星期三
- case wed
- /// 星期四
- case thur
- /// 星期五
- case fri
- /// 星期六
- case sat
- }
-
-// /// 当天的第一秒
-// var fromTimeInterval: TimeInterval {
-// let today = format(.short)
-// return today.timeIntervalSince1970(.short)
+//// MARK: - Extension Date
+//extension Date {
+//
+// static let formatter = DateFormatter()
+//
+// struct Format {
+//
+// let rawValue: String
+// init(_ rawValue: String) {
+// self.rawValue = rawValue
+// }
+//
+// /// e.g. 01:01:01
+// static let time: Format = .init("HH:mm:ss")
+//
+// /// e.g. 01:01
+// static let shortTime: Format = .init("HH:mm")
+//
+// /// e.g. 01-01
+// static let date: Format = .init("MM-dd")
+//
+// /// e.g. 01月01日
+// static let date1: Format = .init("MM月dd日")
+//
+// /// e.g. 2020-01-01
+// static let short: Format = .init("yyyy-MM-dd")
+//
+// /// e.g. 2020年01月01日
+// static let short1: Format = .init("yyy年MM月dd日")
+//
+// /// e.g. 2020-01-01 01:01
+// static let middle: Format = .init("yyyy-MM-dd HH:mm")
+//
+// /// e.g. 2020年01月01日 01:01
+// static let middle1: Format = .init("yyy年MM月dd日 HH:mm")
+//
+// /// e.g. 2020-01-01 01:01:01
+// static let long: Format = .init("yyyy-MM-dd HH:mm:ss")
+//
+// /// e.g. 2020年01月01日 01:01:01
+// static let long1: Format = .init("yyyy年MM月dd日 HH:mm:ss")
+//
+// /// e.g. 2020-01-01 01:01:01.088
+// static let full: Format = .init("yyyy-MM-dd HH:mm:ss.SSS")
+//
+// /// e.g. 2020年01月01日 01:01:01.088
+// static let full1: Format = .init("yyyy年MM月dd日 HH:mm:ss.SSS")
+// }
+//
+// /// 格式化成本地时间
+// func format(_ format: Date.Format = .middle) -> String {
+// Self.formatter.dateFormat = format.rawValue
+// return Self.formatter.string(from: self)
+// }
+//
+// enum Unit {
+// case minute(Int)
+// case hour(Int)
+// case day(Int)
+//
+// /// 转换成秒
+// var second: TimeInterval {
+// switch self {
+// case .minute(let minute): return 60.0 * Double(minute)
+// case .hour(let hour): return 60.0 * 60.0 * Double(hour)
+// case .day(let day): return 60.0 * 60.0 * 24.0 * Double(day)
+// }
+// }
// }
+//}
//
-// /// 当天最后一秒
-// var toTimeInterval: TimeInterval { fromTimeInterval + Unit.day(1).second }
-
- /// 哪一年
- var year: Int { component(.year) }
-
- /// 哪个月
- var month: Int { component(.month) }
-
- /// 当月的第几天
- var day: Int { component(.day) }
-
- /// 当月的第几天
- var hour: Int { component(.hour)}
-
- /// 当月的第几天
- var minute: Int { component(.minute)}
-
- /// 当月的第几天
- var second: Int { component(.second)}
-
- /// 当月的第几周
- var weekOfMonth: Int { component(.weekOfMonth) }
-
- /// 今年的第几周
- var weekOfYear: Int { component(.weekOfYear) }
-
- /// 星期几
- var weekDay: Week { Week(rawValue: component(.weekday))! }
-
- /// 假设当天是周五,表示本月的第几个周五
- var weekdayOrdinal: Int { component(.weekdayOrdinal) }
-
- private func component(_ component: Calendar.Component) -> Int {
- return Calendar.autoupdatingCurrent.component(component, from: self)
- }
-}
+//let today: Date = .init()
+//
+///// 本地化
+//extension Date {
+// /// 星期几
+// enum Week: Int {
+// /// 星期日
+// case sun = 1
+// /// 星期一
+// case mon
+// /// 星期二
+// case tue
+// /// 星期三
+// case wed
+// /// 星期四
+// case thur
+// /// 星期五
+// case fri
+// /// 星期六
+// case sat
+// }
+//
+//// /// 当天的第一秒
+//// var fromTimeInterval: TimeInterval {
+//// let today = format(.short)
+//// return today.timeIntervalSince1970(.short)
+//// }
+////
+//// /// 当天最后一秒
+//// var toTimeInterval: TimeInterval { fromTimeInterval + Unit.day(1).second }
+//
+// /// 哪一年
+// var year: Int { component(.year) }
+//
+// /// 哪个月
+// var month: Int { component(.month) }
+//
+// /// 当月的第几天
+// var day: Int { component(.day) }
+//
+// /// 当月的第几天
+// var hour: Int { component(.hour)}
+//
+// /// 当月的第几天
+// var minute: Int { component(.minute)}
+//
+// /// 当月的第几天
+// var second: Int { component(.second)}
+//
+// /// 当月的第几周
+// var weekOfMonth: Int { component(.weekOfMonth) }
+//
+// /// 今年的第几周
+// var weekOfYear: Int { component(.weekOfYear) }
+//
+// /// 星期几
+// var weekDay: Week { Week(rawValue: component(.weekday))! }
+//
+// /// 假设当天是周五,表示本月的第几个周五
+// var weekdayOrdinal: Int { component(.weekdayOrdinal) }
+//
+// private func component(_ component: Calendar.Component) -> Int {
+// return Calendar.autoupdatingCurrent.component(component, from: self)
+// }
+//}
-//import AVFoundation
//
-//class PcmPlayer {
-// private var audioEngine: AVAudioEngine!
-// private var audioPlayerNode: AVAudioPlayerNode!
-// private var audioFormat: AVAudioFormat!
-//
-// init(sampleRate: Double, channels: AVAudioChannelCount, bitsPerSample: Int) {
-// audioEngine = AVAudioEngine()
-// audioPlayerNode = AVAudioPlayerNode()
-//
-// let formatSettings = [
-// AVFormatIDKey: kAudioFormatLinearPCM,
-// AVSampleRateKey: sampleRate,
-// AVNumberOfChannelsKey: channels,
-// AVLinearPCMBitDepthKey: bitsPerSample,
-// AVLinearPCMIsBigEndianKey: false,
-// AVLinearPCMIsFloatKey: false
-// ] as [String : Any]
-//
-// audioFormat = AVAudioFormat(settings: formatSettings)!
-// }
//
-// func startPlaying() {
-// audioEngine.attach(audioPlayerNode)
-// audioEngine.connect(audioPlayerNode, to: audioEngine.mainMixerNode, format: audioFormat)
//
-// do {
-// try audioEngine.start()
-// } catch {
-// print("Failed to start audio engine: \(error)")
-// return
-// }
+//import SwiftAudio
//
-// audioPlayerNode.play()
//
-// DispatchQueue.global(qos:.default).async {
-// while true {
-// // 模拟接收 PCM 数据的逻辑
-// let pcmData = [UInt8](repeating: 0xAA, count: 1024) // 这里只是一个简单的示例数据,您需要替换为实际接收到的数据
-// let audioBuffer = AVAudioPCMBuffer(pcmFormat: self.audioFormat, frameCapacity: AVAudioFrameCount(pcmData.count / self.audioFormat.streamDescription.pointee.mBytesPerFrame))
-// audioBuffer.frameLength = audioBuffer.frameCapacity
-// memcpy(audioBuffer.int16ChannelData![0], pcmData, pcmData.count)
-// self.audioPlayerNode.scheduleBuffer(audioBuffer, at: nil, options: [], completionHandler: nil)
+//class PcmPlayer {
+// public static let shared = PcmPlayer()
+// private var finishCallback:((_ success:Bool) -> Void)?
+//
+// let player = QueuedAudioPlayer()
+//
+// init(){
+// player.event.stateChange.addListener(self, handleAudioPlayerStateChange)
+// // player.remoteCommandController.handlePlayCommand = { (event) in
+// //
+// // }
+// }
+//
+//
+//
+// func handleAudioPlayerStateChange(state: AudioPlayerState) {
+// DispatchQueue.main.sync {
+// print("state = \(state)")
+// if state == .paused {
+// self.finishCallback?(true)
// }
// }
// }
//}
//
-//// 使用示例
-////let pcmPlayer = PcmPlayer(sampleRate: 44100.0, channels: 2, bitsPerSample: 16)
-////pcmPlayer.startPlaying()
+//extension PcmPlayer{
+//
+// // func playFile(pcmData: Data) {
+//
+// func playFile(filePath: String,finished: ((_ success: Bool) -> Void)? = nil) {
+// print("声音播放: \(filePath)")
+// self.finishCallback = finished
+//
+// let audioItem = DefaultAudioItem(audioUrl: filePath, sourceType: .file)
+// try? player.add(item: audioItem, playWhenReady: true)
+// try? player.next()
+// }
+//}
import Foundation
import AVFoundation
-class PlayerManager:NSObject,AVAudioPlayerDelegate{
+class PlayerManager:NSObject{
public static let shared = PlayerManager()
private var finishCallback:((_ success:Bool) -> Void)?
}
}
+ func stop(){
+ if player.isPlaying{
+ player.stop()
+ }
+ }
+}
+
+extension PlayerManager:AVAudioPlayerDelegate{
func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) {
// 播放结束时调用
print("播放结束 \(flag)")
// ViewController.swift
// Example
//
-// Created by incar on 2024/6/12.
-//
import UIKit
-import MixSdk
-import SnapKit
+import CoreBluetooth
import Toast_Swift
+import MixSdk
-class ViewController: UIViewController{
- let list:[(name:String,action:String)] = [
- ("注册","registerAction"),
- ("登录","loginAction"),
-
- ("VoiceChat","voiceChatAction"),
- ]
+class ViewController: UIViewController {
+ let tableView = UITableView(frame:.zero, style: .plain)
+ var dataArray = [CBPeripheral]()
+
override func viewDidLoad() {
super.viewDidLoad()
- // Do any additional setup after loading the view.
- self.title = "MixSdk"
- let tableView = UITableView(frame: CGRectMake(0, 0, self.view.bounds.size.width, self.view.bounds.size.height), style: .plain)
+ title = "扫描"
+ setupViews()
+ setupBle()
+ }
+
+ override func viewDidAppear(_ animated: Bool) {
+ super.viewDidAppear(animated)
+ scanAction()
+ }
+
+ override func viewDidDisappear(_ animated: Bool) {
+ super.viewDidDisappear(animated)
+ BluetoothManager.shared.stopScan()
+ }
+
+ func setupViews(){
+ self.title = "VoiceChat"
+ self.view.backgroundColor = .white
+ self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: "扫描", style: .done, target: self, action: #selector(scanAction))
+ self.navigationItem.leftBarButtonItem = UIBarButtonItem(title: "注册", style: .done, target: self, action: #selector(loginAction))
+
tableView.delegate = self
tableView.dataSource = self
- tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
+ tableView.estimatedRowHeight = 40
+ tableView.register(UITableViewCell.self, forCellReuseIdentifier: "UITableViewCell")
view.addSubview(tableView)
-
+ tableView.snp.makeConstraints { make in
+ make.top.equalToSuperview().inset(80)
+ make.left.right.equalToSuperview()
+ make.bottom.equalToSuperview().offset(80)
+ }
+ }
+
+ func setupBle() {
+ BluetoothManager.shared.discoverCallBack = { [unowned self] peripheral in
+ if dataArray.contains(peripheral){
+ return
+ }
+ if peripheral.name == nil || (peripheral.name!.isEmpty){
+ return
+ }
+ print(peripheral.name)
+ dataArray.append(peripheral)
+ tableView.reloadData()
+ }
+ BluetoothManager.shared.didConnectedCallBack = {[unowned self] peripheral in
+ self.view.hideToastActivity()
+// let vc = PhoneChatVC()
+ let vc = BleChatVC()
+ self.navigationController?.pushViewController(vc, animated: true)
+ }
+ BluetoothManager.shared.disConnectedCallBack = { [unowned self] peripheral in
+ print("蓝牙已经断开!!!!!!!!!!!")
+ self.view.makeToast("蓝牙已经断开!")
+ }
+ }
+
+ @objc func scanAction(){
+ BluetoothManager.shared.startScan()
}
}
-extension ViewController:UITableViewDelegate,UITableViewDataSource {
- func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
- return 60
- }
+extension ViewController: UITableViewDelegate, UITableViewDataSource {
+
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
- return list.count
+ return dataArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
- let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
- cell.textLabel?.text = list[indexPath.row].name
-
+ let cell = tableView.dequeueReusableCell(withIdentifier: "UITableViewCell", for: indexPath)
+
+ cell.textLabel?.text = dataArray[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)
+ view.makeToastActivity(.center)
+ let device = dataArray[indexPath.row]
+ BluetoothManager.shared.connect(device)
+
+// let vc = PhoneChatVC()
+// let vc = BleChatVC()
+// self.navigationController?.pushViewController(vc, animated: true)
}
}
-
extension ViewController{
@objc func registerAction() {
view.makeToastActivity(.center)
}
}
}
-extension ViewController{
- @objc func voiceChatAction() {
- if !MixSdk.shared.auth.authorized {
- self.view.makeToast("unAuthorized!")
- return;
- }
- let vc = VoiceChatViewController()
- self.navigationController?.pushViewController(vc, animated: true)
- }
-
-}
-
18A2DD382C40D94900A8731B /* BigInt.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DCC62C40D94700A8731B /* BigInt.swift */; };
18A2DD392C40D94900A8731B /* FloatingPointConversion.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DCC72C40D94700A8731B /* FloatingPointConversion.swift */; };
18A2DD3A2C40D94900A8731B /* Array+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DCC82C40D94700A8731B /* Array+Extension.swift */; };
+ 18AA04692CE444D40008A345 /* MSBleSender.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18AA04682CE444D40008A345 /* MSBleSender.swift */; };
+ 18AA046C2CE45E800008A345 /* MSBleInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18AA046B2CE45E800008A345 /* MSBleInfo.swift */; };
+ 18B1A38C2CE3547F00DD4A7C /* MSBle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18B1A38B2CE3547F00DD4A7C /* MSBle.swift */; };
18C42B4B2C23DA29000D8860 /* SbcToPcm.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18C42B4A2C23DA29000D8860 /* SbcToPcm.swift */; };
18C42BE72C242202000D8860 /* version.h in Headers */ = {isa = PBXBuildFile; fileRef = 18C42B5E2C242202000D8860 /* version.h */; };
18C42BE82C242202000D8860 /* buffersrc.h in Headers */ = {isa = PBXBuildFile; fileRef = 18C42B5F2C242202000D8860 /* buffersrc.h */; };
18A2DCC62C40D94700A8731B /* BigInt.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BigInt.swift; sourceTree = "<group>"; };
18A2DCC72C40D94700A8731B /* FloatingPointConversion.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FloatingPointConversion.swift; sourceTree = "<group>"; };
18A2DCC82C40D94700A8731B /* Array+Extension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Array+Extension.swift"; sourceTree = "<group>"; };
+ 18AA04682CE444D40008A345 /* MSBleSender.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MSBleSender.swift; sourceTree = "<group>"; };
+ 18AA046B2CE45E800008A345 /* MSBleInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MSBleInfo.swift; sourceTree = "<group>"; };
+ 18B1A38B2CE3547F00DD4A7C /* MSBle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MSBle.swift; sourceTree = "<group>"; };
18C42B4A2C23DA29000D8860 /* SbcToPcm.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SbcToPcm.swift; sourceTree = "<group>"; };
18C42B5E2C242202000D8860 /* version.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = version.h; sourceTree = "<group>"; };
18C42B5F2C242202000D8860 /* buffersrc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = buffersrc.h; sourceTree = "<group>"; };
18C42C762C24241A000D8860 /* MixSdk-Bridging-Header.h */,
18204BEF2C19457600F94E69 /* MSHttpManager.swift */,
1811882E2C1A89E600C23FAB /* MixSdk.swift */,
+ 18B1A38B2CE3547F00DD4A7C /* MSBle.swift */,
+ 18AA046B2CE45E800008A345 /* MSBleInfo.swift */,
+ 18AA04682CE444D40008A345 /* MSBleSender.swift */,
18204BC52C193E4B00F94E69 /* MSAuth.swift */,
18314A882C3CD55E0028CE50 /* MSAuthModel.swift */,
186BFD482C57915500673D8C /* MSVoiceChat.swift */,
18A2DCFE2C40D94800A8731B /* Authenticator.swift in Sources */,
18A2DD172C40D94900A8731B /* OCB.swift in Sources */,
18C42C7B2C24270D000D8860 /* MSAudioConvert.swift in Sources */,
+ 18B1A38C2CE3547F00DD4A7C /* MSBle.swift in Sources */,
18A2DCCC2C40D94800A8731B /* ASN1Decoder.swift in Sources */,
18A2DCDE2C40D94800A8731B /* CMAC.swift in Sources */,
18A2DD002C40D94800A8731B /* Operators.swift in Sources */,
18A2DD1D2C40D94900A8731B /* BlockMode.swift in Sources */,
18A2DD282C40D94900A8731B /* Addition.swift in Sources */,
18A2DCD92C40D94800A8731B /* CBCMAC.swift in Sources */,
+ 18AA046C2CE45E800008A345 /* MSBleInfo.swift in Sources */,
18A2DD132C40D94900A8731B /* PKCS1v15.swift in Sources */,
18A2DCCE2C40D94800A8731B /* StreamDecryptor.swift in Sources */,
18A2DD0F2C40D94900A8731B /* Padding.swift in Sources */,
18A2DD182C40D94900A8731B /* CFB.swift in Sources */,
18A2DCD12C40D94800A8731B /* UInt64+Extension.swift in Sources */,
18A2DD292C40D94900A8731B /* StringConversion.swift in Sources */,
+ 18AA04692CE444D40008A345 /* MSBleSender.swift in Sources */,
18A2DCFC2C40D94800A8731B /* ZeroPadding.swift in Sources */,
18A2DCDC2C40D94800A8731B /* Bit.swift in Sources */,
18A2DD0B2C40D94800A8731B /* Utils.swift in Sources */,
//
import Foundation
-///// 注册返回模型
-//public class MSRegisterModel:Codable{
-// /// 是否成功
-// public var success:Bool = false
-// /// 提示语
-// public var msg:String = "请求失败"
-// /// 状态码
-// public var code:Int = 400
-// /// 内容
-// public var data:MSRegisterData?
-//
-//}
+
/// 注册返回模型
public class MSRegisterModel{
/// 设备ID
--- /dev/null
+//
+// MSBle.swift
+// MixSdk
+//
+// Created by incar on 2024/11/12.
+//
+
+import Foundation
+
+
+
+/// SDK 管理类
+public class MSBle {
+ public var authCallBack:(()->Void)?
+ public var didAuth:Bool{
+ return MixSdk.shared.auth.authorized
+ }//是否正在鉴权
+
+ var lastSender:MSBleSender?
+ var deviceInfo:MSBleInfo?
+ var isAuthing = false//是否正在鉴权
+
+
+ init() {
+
+ }
+
+ /// sdk发送数据回调,app将此数据转发给蓝牙设备,写入设备成功后,需要调didWriteToDevice(characteristic:, error:)告知sdk是否写入成功
+ public var sdkToDeviceCallback:((_ data: Data) -> Void)?
+
+ /// 收到蓝牙设备传过来的数据,转发给sdk处理
+ public func sendToSdk(voiceData data: Data?){
+ guard let data = data else{
+ Logger.log("[recv][voice]")
+ return
+ }
+// Logger.log("[recv][voice] \(data)")
+// if self.didAuth == false{
+//
+// DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
+// self.startAuth()
+// }
+// return
+// }
+ if(data.count > 16){
+ MSAudioConvert.sbcTopcm(data){ pcm in
+ MixSdk.shared.voiceChat.sendVoice(pcmData: pcm)
+ }
+ }
+ }
+ /// 收到蓝牙设备传过来的数据,转发给sdk处理
+ public func sendToSdk(infoData data: Data?){
+ guard let data = data else{
+ Logger.log("[recv][info]")
+ return
+ }
+ Logger.log("[recv][info] " + data.joined("") + MSUtils.data(toUtf8String: data))
+// if MSUtils.data(toHexString: data) == "7b2272737369223a2230227d"{
+// return
+// }
+// if let sender = lastSender{
+// lastSender = nil
+// sender.completion?(.response.code(.success).value(data))
+// sender.remove()
+//
+// deinitTimer()
+// }
+ }
+ /// sdk传给ble设备后,告知sdk写入的情况
+// public func didWriteToDevice(characteristic: CBCharacteristic, error: Error?){
+// print(characteristic.uuid.uuidString, "write success")
+// if let error = error {
+// print("发送失败 ",characteristic.uuid.uuidString,error.localizedDescription)
+// }
+// else{
+// if characteristic.uuid.uuidString == writeFileCharacteristicUUID{
+// IBBluetooth.shared.didWriteToDevice()
+// }
+// }
+// }
+}
+
+
+extension MSBle{
+ public func startAuth(){
+ if isAuthing {
+ return
+ }
+ isAuthing = true
+ requestDeviceInfo {[weak self] info in
+ guard let self = self else {return}
+ guard let info = info else {
+ Logger.log("获取设备信息失败!!!")
+ isAuthing = false
+ return
+ }
+ Logger.log("设备信息 = \(info.description)")
+ deviceInfo = info
+
+ deviceInfo!.deviceId = "28110687C456"
+ deviceInfo!.productId = "WTxAKo2qD2pXOZZLW3zz"
+ deviceInfo!.productKey = "YqggeKCxfEOVQGjUYrpQQdEstGpvojzn"
+ deviceInfo!.password = ""
+
+ self.startLogin {[weak self] token, error in
+ guard let self = self else {return}
+ if let _ = token {
+ self.authCallBack?()
+ }
+ else{
+// self.authCallBack?(false)
+ }
+ isAuthing = false
+ }
+ }
+ }
+ func requestDeviceInfo(finished: @escaping (MSBleInfo?) -> Void){
+ let deviceInfo = MSBleInfo()
+ self.requestDeviceId {[weak self] value in
+ guard let self = self else {return}
+ if let value = value{
+ deviceInfo.deviceId = value
+
+ self.requestProductKey { value in
+ if let value = value{
+ deviceInfo.productKey = value
+
+ self.requestSecretKey { value in
+ if let value = value{
+ deviceInfo.productId = value
+
+ self.requestDeviceSecret { value in
+ if let value = value{
+ deviceInfo.password = value
+ finished(deviceInfo)
+ return
+ }
+ else{
+ finished(nil)
+ }
+ }
+ }
+ else{
+ finished(nil)
+ }
+ }
+ }
+ else{
+ finished(nil)
+ }
+ }
+ }
+ else{
+ finished(nil)
+ }
+ }
+ }
+
+ func requestDeviceId(finished: @escaping (String?) -> Void){
+ let sender = dictionaryToSender(["cmd":"get_MAC"])
+ sender.completion = { result in
+ Logger.log("\(result.description)")
+ if result.isSuccess,let value = MSUtils.data(toDictionary: result.data)["MAC"] as? String{
+ finished(value)
+ }
+ else{
+ finished(nil)
+ }
+ }
+ sendToBle(sender)
+ }
+ func requestProductKey(finished: @escaping (String?) -> Void){
+ let sender = dictionaryToSender(["cmd":"get_productKey"])
+ sender.completion = { result in
+ Logger.log("\(result.description)")
+ if result.isSuccess,let value = MSUtils.data(toDictionary: result.data)["productKey"] as? String{
+ finished(value)
+ }
+ else{
+ finished(nil)
+ }
+ }
+ sendToBle(sender)
+ }
+ func requestSecretKey(finished: @escaping (String?) -> Void){
+ let sender = dictionaryToSender(["cmd":"get_secretKey"])
+ sender.completion = { result in
+ Logger.log("\(result.description)")
+ if result.isSuccess,let value = MSUtils.data(toDictionary: result.data)["secretKey"] as? String{
+ finished(value)
+ }
+ else{
+ finished(nil)
+ }
+ }
+ sendToBle(sender)
+ }
+ func requestDeviceSecret(finished: @escaping (String?) -> Void){
+ let sender = dictionaryToSender(["cmd":"get_deviceSecret"])
+ sender.completion = { result in
+ Logger.log("\(result.description)")
+ if result.isSuccess,let value = MSUtils.data(toDictionary: result.data)["deviceSecret"] as? String{
+ finished(value)
+ }
+ else{
+ finished(nil)
+ }
+ }
+ sendToBle(sender)
+ }
+
+
+ func sendToBle(_ sender:MSBleSender){
+ if let _ = lastSender {
+ sender.completion(.response.code(.failure))
+ sender.remove()
+ Logger.log("有命令正在执行中...")
+ if Self.timer == nil{
+ startTimer()
+ }
+ return
+ }
+ Thread.sleep(forTimeInterval:0.02)
+ lastSender = sender
+ startTimer()
+ for data in sender.list{
+ if let sendToBleBlock = self.sdkToDeviceCallback {
+ sendToBleBlock(data)
+ }
+ }
+ }
+}
+
+
+extension MSBle{
+ static var timer: Timer?
+ func startTimer() {
+ deinitTimer()
+
+ Self.timer = Timer.scheduledTimer(withTimeInterval: 5, repeats: false, block: { [unowned self] _ in
+ deinitTimer()
+ if let sender = lastSender{
+ lastSender = nil
+ sender.completion?(.response.code(.timeout))
+ sender.remove()
+ }
+ })
+// Logger.log("创建定时器")
+ }
+
+ func deinitTimer() {
+ guard let _ = Self.timer else { return }
+ Self.timer?.invalidate()
+ Self.timer = nil
+// Logger.log("销毁定时器")
+ }
+}
+
+
+extension MSBle{
+ func dictionaryToSender(_ dict: [String: Any]) ->MSBleSender{
+ let sender = MSBleSender()
+ let data = MSUtils.dictionary(toData: dict)
+ let str = MSUtils.data(toUtf8String: data)
+ let list = splitCMD(str: str)
+ Logger.log("[send][info][all][string] \(str)")
+ if list.count > 1 {
+ // 开始标志
+ sender.list.append(stringToData(cmd: "-$$\0"))
+ // 指令
+ for i in 0..<list.count {
+ sender.list.append(stringToData(cmd: "\(list[i])\0"))
+ }
+ // 结束标志
+ sender.list.append(stringToData(cmd: "$$-\0"))
+ } else {
+ let data = stringToData(cmd: "-$$\(str)$$-\n")
+ sender.list.append(data)
+ }
+
+ return sender
+ }
+
+ func stringToData(cmd: String) ->Data{
+ let data = cmd.data(using:.utf8)!
+ return data
+ }
+
+ func splitCMD(str: String) -> [String] {
+ var list = [String]()
+ var str = str
+ let len = 30
+ while str.count > 0 {
+ if str.count > len {
+ let sub = String(str.prefix(len))
+ list.append(sub)
+ str = String(str.suffix(str.count - len))
+ } else {
+ list.append(str)
+ break
+ }
+ }
+ return list
+ }
+}
+
+extension MSBle{
+ func registerAction(completeHandler: @escaping MSLoginCallBack) {
+ MixSdk.shared.auth.register(productId: deviceInfo!.productId, productKey: deviceInfo!.productKey, deviceId: deviceInfo!.deviceId){[weak self] password,error in
+ if let password = password{
+ self?.deviceInfo?.password = password
+ self?.startLogin(completeHandler: completeHandler)
+ }
+ else{
+ completeHandler(nil,error)
+ }
+ }
+ }
+
+ func startLogin(completeHandler: @escaping MSLoginCallBack){
+ if let info = deviceInfo,info.password.isEmpty{
+ registerAction(completeHandler: completeHandler)
+ return
+ }
+
+ MixSdk.shared.auth.login(productKey:deviceInfo!.productKey,deviceId: deviceInfo!.deviceId, password: deviceInfo!.password){[weak self] token,error in
+ if let err = error{
+ if case .reponseError(let code,_) = err,code == 50021{
+ //50021是账号或密码错误.可以在这里调用注册,重新注册后,之前的密码会被覆盖,APP更新保存的新密码
+ self?.registerAction(completeHandler: completeHandler)
+ }
+ else{
+ completeHandler(nil,err)
+ }
+ }
+ else{
+ completeHandler(token,nil)
+ }
+ }
+ }
+}
--- /dev/null
+//
+// MSBleInfo.swift
+// MixSdk
+//
+// Created by incar on 2024/11/13.
+//
+
+import Foundation
+class MSBleInfo{
+ var deviceId = ""
+ var productId = ""
+ var productKey = ""
+ var password = ""
+
+ public var description: String{
+ return "MSBleInfo(deviceId:\(deviceId), productId:\(productId), productKey:\(productKey), password:\(password))"
+ }
+}
+
--- /dev/null
+//
+// MSBleSender.swift
+// MixSdk
+//
+// Created by incar on 2024/11/13.
+//
+
+import Foundation
+
+class MSBleSender{
+ var completion: MSCompletionHandler!
+ var list = [Data]()
+
+ static var all: [MSBleSender] = []
+
+ init() {
+ Self.all.append(self)
+ }
+
+ /// 从缓存实例集合中移除该实例
+ func remove() {
+ Self.all.removeAll { $0 === self }
+ }
+
+}
+
+public typealias MSCompletionHandler = (_ result: MSResponse) -> Void
+
+/// 状态码
+public enum MSCode: Int,CustomStringConvertible {
+
+ public var description: String{
+ return "MSCode(rawValue:\(rawValue), message:\(message))"
+ }
+
+ /// 操作成功
+ case success = 1000
+ /// 操作失败
+ case failure = 1001
+ /// 请求超时
+ case timeout = 1004
+ /// 未知错误
+ case unknow = 1005
+
+ var message:String{
+ switch self {
+ case .failure:
+ return "失败"
+ case .success:
+ return "成功"
+ case .timeout:
+ return "超时"
+ default:
+ return "未知错误"
+ }
+ }
+}
+
+public class MSResponse {
+ public private(set) var code: MSCode = .success
+ public private(set) var data: Data = Data()
+ static var response: MSResponse { MSResponse() }
+
+
+ var isSuccess:Bool{
+ return code == .success
+ }
+
+ public var description: String{
+ return code.description + "data = " + data.joined()
+ }
+
+ @discardableResult
+ func code(_ value: MSCode) -> Self {
+ code = value
+ return self
+ }
+
+ @discardableResult
+ func value(_ value: Data) -> Self {
+ self.data = value
+ return self
+ }
+}
/// 开始说话,在发送语音数据前,一定要先调用该方法
public func startSpeedch(){
- if !MixSdk.shared.auth.authorized {
- Logger.log("还未鉴权")
- return
- }
+// if !MixSdk.shared.auth.authorized {
+// Logger.log("还未鉴权")
+// return
+// }
let params:[String:Any] =
[
"header":[
/// 结束说话
public func endSpeedch(){
- if !MixSdk.shared.auth.authorized {
- Logger.log("还未鉴权")
- return
- }
+// if !MixSdk.shared.auth.authorized {
+// Logger.log("还未鉴权")
+// return
+// }
let params:[String:Any] =
[
"header":[
/// 发送语音数据,format pcm,采样率 16000
/// - Parameter data: 语音数据
public func sendVoice(pcmData:Data){
- if !MixSdk.shared.auth.authorized {
- Logger.log("还未鉴权")
- return
- }
+// if !MixSdk.shared.auth.authorized {
+// Logger.log("还未鉴权")
+// return
+// }
if currentEvent?.payload?.state == "Listening"{
Logger.log("send \(pcmData)")
socket.send(data: pcmData)
}
else{
- Logger.log("当前状态不可以发送语音")
+// Logger.log("当前状态不可以发送语音")
}
}
/// 发送语音数据,format sbc,采样率 16000
/// - Parameter data: 语音数据
public func sendVoice(sbcData:Data){
- if !MixSdk.shared.auth.authorized {
- Logger.log("还未鉴权")
- return
- }
+// if !MixSdk.shared.auth.authorized {
+// Logger.log("还未鉴权")
+// return
+// }
MSAudioConvert.sbcTopcm(sbcData){[weak self] pcmData in
Logger.log("send \(pcmData)")
self?.socket.send(data: pcmData)
private func startTask(){
let taskId = "\(Int(Date().timeIntervalSince1970)*1000)".md5().lowercased();
self.taskId = taskId
+// let payload:[String : Any] = [
+// "device_id":"test_device_id",
+// "user_agent":"python",
+// "in_format":"pcm",
+// "in_sample_rate":16000,
+// "out_format":"mp3",
+// "sample_rate":16000,// 返回音频采样率
+// "space_time":100,
+// "volume":"10",
+// "voice": "huoshan::zh_female_shuangkuaisisi_moon_bigtts", // 音色配置 默认使用阿里云的音色 longzhe 规则 aliyun::[音色] 或者 huoshan::[音色] 例如 aliyun::longzhe 或者 huoshan::zh_female_shuangkuaisisi_moon_bigtts
+// "voice_detection": "false" // 开启自动断句 enabled/disable
+// ]
let payload:[String : Any] = [
"device_id":"test_device_id",
"user_agent":"python",
"in_format":"pcm",
"in_sample_rate":16000,
"out_format":"mp3",
- "sample_rate":16000,
- "space_time":100,
- "volume":"2",
- "voice":"longhua"
+ "sample_rate":16000,// 返回音频采样率
+ "space_time":0,
+ "volume":"10",
+ "voice": "huoshan::zh male dongfanghaoran moon bigtts", // 音色配置 默认使用阿里云的音色 longzhe 规则 aliyun::[音色] 或者 huoshan::[音色] 例如 aliyun::longzhe 或者 huoshan::zh_female_shuangkuaisisi_moon_bigtts
+ "voice_detection": "false" // 开启自动断句 enabled/disable
]
-
let params:[String:Any] =
[
"header":[
Logger.log("send" ,cmd)
socket.send(text: cmd)
}
+
+ func closeWebSocket(){
+ if connectState == .connected {
+ socket?.close(10100,reason: "任务失败")
+ }
+ }
}
guard let data = text.data,let model = JSON.decode(MSVoiceChatEvent.self, from: data) else{
return
}
- if model.event == "",let dialogId = model.payload?.dialog_id{
+ 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.payload?.state == "Listening"{
startSpeedch()
}
// Created by incar on 2024/6/13.
//
-//根据需求, AISDK 还需提供以下三个方法:
-//1.国内模型切换的调用方法(国内模型需求定为文心一言、KIMI、豆包)
-//2.ChatGPT Key 的合规可用的校验方法,返回校验结果
-//3.ChatGPT 模型单独切换的调用方法(gpt3.5-turbo、gpt-4-turbo、gpt-4o)
-
-import Foundation
-
-// https://apifox.com/apidoc/shared-054b7e2c-cf4b-4e2b-875c-79fda14ddbbf?pwd=xRzzNg7l
-// 访问密码: xRzzNg7l
-//let wsBaseURL = "ws://121.40.67.151:9099/"
-//let httpBaseURL = "http://121.40.67.151:9099/"
-
-//let wsBaseURL = "ws://118.145.158.207:80/"
-//let httpBaseURL = "http://118.145.158.207:80/"
-
-
-
-//Asr、TTS、翻译、OCR、AIGC
-//platform 参数支持
-//
-//1.
-//aliyun
-//
-//2.
-//huoshan
-//
-//Chat 大模型对话
-//platform 参数支持
-//
-//1.
-//baidu (文心一言 模型:ernie-3.5-8k-0329)
-//
-//2.
-//moonshot (kimi 模型:moonshot-v1-8k)
-//
-//3.
-//aliyun (通义千问 模型:qwen-plus)
-//
-//4.
-//huoshan (豆包 模型:ep-20240611101421-5qk8s)
-
-
class MSConfig{
static let wsBaseURL = "ws://amp.raisound.com/"
static let httpBaseURL = "https://amp.raisound.com/"
-// static let authBaseURL = "http://180.184.206.142/bz-api/"
static let authBaseURL = "http://dmp.raisound.com/bz-api/"
-// static let authBaseURL = "http://180.184.206.142/bz-api/"
static let wsHeartBeatTime = 5.0
+
+// static let wsBaseURL = "ws://118.145.158.207:22080/"
+// static let httpBaseURL = "https://amp.raisound.com/"
+// static let authBaseURL = "http://dmp.raisound.com/bz-api/"
+// static let wsHeartBeatTime = 5.0
+
-// static func http(api:String)->String{
-// return httpBaseURL + api + "?platform=\(MixSdk.shared.aiPlatform.value)&is_auth=1&dt=\(MixSdk.shared.auth.deviceToken)"
-// return httpBaseURL + api + "?platform=\(MixSdk.shared.aiPlatform.value)&is_auth=1&dt=\(MixSdk.shared.auth.deviceToken)"
-// return httpBaseURL + api + ""
-// }
static func authUrl(api:String)->String{
return authBaseURL + api
}
- static func aigcUrl(api:String)->String{
- return httpBaseURL + api + "?platform=aliyun&is_auth=1&dt=\(MixSdk.shared.auth.deviceToken)"
- // return httpBaseURL + api + ""
- }
- static func translateUrl(api:String)->String{
- return httpBaseURL + api + "?platform=aliyun&is_auth=1&dt=\(MixSdk.shared.auth.deviceToken)"
- // return httpBaseURL + api + "?platform=\(MixSdk.shared.aiPlatform.value)&is_auth=1&dt=\(MixSdk.shared.auth.deviceToken)"
- }
- static func asr(api:String)->String{
- return httpBaseURL + api + "?platform=aliyun&is_auth=1&dt=\(MixSdk.shared.auth.deviceToken)"
- }
- static func webSocketUrl(api:String,addPlatform:Bool)->String{
- return wsBaseURL + api + "?platform=aliyun&is_auth=1&dt=\(MixSdk.shared.auth.deviceToken)"
- }
-}
-
-
-
-/// Ai平台类型
-public enum MSPlatform {
- /// 通义千问
- case aliyun
- /// 讯飞星火
- case xunfei
- /// 文心一言
- case yiyan
- /// 豆包
- case doubao
- /// Kimi
- case kimi
- /// chatGPT
- case chatGPT
-
- internal var value:String{
- switch self {
- case .aliyun:
- return "aliyun"
- case .xunfei:
- return "xunfei"
- case .yiyan:
- return "baidu"
- case .doubao:
- return "huoshan"
- case .kimi:
- return "moonshot"
- case .chatGPT:
- return "chatGPT"
- }
- }
-
- public var title:String{
- switch self {
- case .aliyun:
- return "通义千问"
- case .xunfei:
- return "讯飞星火"
- case .yiyan:
- return "文心一言"
- case .doubao:
- return "豆包"
- case .kimi:
- return "kimi"
- case .chatGPT:
- return "ChatGPT"
- }
+ static func webSocketUrl(api:String,addPlatform:Bool)->String{
+// return wsBaseURL + api + "?platform=aliyun&is_auth=1&dt=\(MixSdk.shared.auth.deviceToken)"
+ return "ws://118.145.158.207:22080/" + api + "?is_auth=0"
}
}
-
/// SDK 管理类
public class MixSdk:NSObject {
public static let shared = MixSdk()
/// 初始化工作
public func initSDK() {
- Logger.log("MixSdk version:",MixSdk.version)
+ Logger.log("MixSdk version:",MixSdk.shared.version)
}
/// 日志
public var logs: [String] { Logger.logs }
/// SDK 版本号
- public let version: String = "1.0.15"
+ public let version: String = "1.0.0"
/// 默认为 false
public var isDebug: Bool = false {
didSet { Logger.isDebug = isDebug }
}
-
/// 鉴权
public let auth = MSAuth()
+ /// 蓝牙管理
+ public let ble = MSBle()
+
/// 语音聊天
public let voiceChat = MSVoiceChat()
func setupWebsoket(){
}
-
/// 清空日志
public func cleanLog(){
Logger.logs.removeAll()
}
}
+
SnapKit: d612e99e678a2d3b95bf60b0705ed0a35c03484a
Toast-Swift: dd369e68ee529f542c65bd0d0c6c50cd743cf6f2
-PODFILE CHECKSUM: 1bf708b0b20ea5799d088a09a56f74d1537472c4
+PODFILE CHECKSUM: 9895aef7274631d7896464b4e401f72df13e34f4
-COCOAPODS: 1.14.3
+COCOAPODS: 1.15.2
SnapKit: d612e99e678a2d3b95bf60b0705ed0a35c03484a
Toast-Swift: dd369e68ee529f542c65bd0d0c6c50cd743cf6f2
-PODFILE CHECKSUM: 1bf708b0b20ea5799d088a09a56f74d1537472c4
+PODFILE CHECKSUM: 9895aef7274631d7896464b4e401f72df13e34f4
-COCOAPODS: 1.14.3
+COCOAPODS: 1.15.2
pod 'SnapKit', '~> 5.7.0'
pod 'Toast-Swift'
pod 'IQKeyboardManagerSwift'
+# pod 'SwiftAudio'
end