From: liangzhenying <867174124@qq.com> Date: Wed, 13 Nov 2024 09:31:19 +0000 (+0800) Subject: 富瑞坤鼠标调通鉴权 X-Git-Url: http://git.snicker.com.cn/sitemap.xml?a=commitdiff_plain;h=c02db7a48787c391f3b1212bb17a821f41125901;p=mirrors%2FVoiceChatSdk_iOS.git 富瑞坤鼠标调通鉴权 --- diff --git a/MixSdk/Example/Example.xcodeproj/project.pbxproj b/MixSdk/Example/Example.xcodeproj/project.pbxproj index a3a295d..b31a3b9 100644 --- a/MixSdk/Example/Example.xcodeproj/project.pbxproj +++ b/MixSdk/Example/Example.xcodeproj/project.pbxproj @@ -9,33 +9,29 @@ /* 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 */ @@ -52,9 +48,7 @@ /* Begin PBXFileReference section */ 180255B92C3BE6BD005AFC5F /* SendRecordTooBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SendRecordTooBar.swift; sourceTree = ""; }; 18068D012CDCC57C006B6EF5 /* PcmPlayer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PcmPlayer.swift; sourceTree = ""; }; - 181188592C1BE80B00C23FAB /* 1.pcm */ = {isa = PBXFileReference; lastKnownFileType = file; path = 1.pcm; sourceTree = ""; }; 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 = ""; }; 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 = ""; }; 18204BD52C193F9A00F94E69 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; @@ -67,15 +61,14 @@ 1843A7952C4E060D00FC010F /* LogViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogViewController.swift; sourceTree = ""; }; 184FFC482C218FC4006F664D /* PlayerManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerManager.swift; sourceTree = ""; }; 184FFC4C2C2199E5006F664D /* SendToolBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SendToolBar.swift; sourceTree = ""; }; - 186BFD4A2C57952600673D8C /* VoiceChatViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VoiceChatViewController.swift; sourceTree = ""; }; 188695A52C5A3CAD00B9B89F /* Date.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Date.swift; sourceTree = ""; }; - 18A2DB6B2C40C5CC00A8731B /* AIAudio.sbc */ = {isa = PBXFileReference; lastKnownFileType = file; path = AIAudio.sbc; sourceTree = ""; }; - 18ABF5792C3F884600C60E9B /* 2.pcm */ = {isa = PBXFileReference; lastKnownFileType = file; path = 2.pcm; sourceTree = ""; }; + 18B1A36F2CE1E12800DD4A7C /* BluetoothManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BluetoothManager.swift; sourceTree = ""; }; + 18B1A3722CE1EF4900DD4A7C /* BleChatVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BleChatVC.swift; sourceTree = ""; }; + 18B1A3742CE1EF6200DD4A7C /* PhoneChatVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhoneChatVC.swift; sourceTree = ""; }; 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 = ""; }; 18DB20432C213D720070D571 /* Example-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Example-Bridging-Header.h"; sourceTree = ""; }; 18DB20442C213D730070D571 /* AiMouseRecordManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AiMouseRecordManager.h; sourceTree = ""; }; 18DB20452C213D730070D571 /* AiMouseAudioRecord.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AiMouseAudioRecord.h; sourceTree = ""; }; @@ -83,7 +76,6 @@ 18DB20472C213D730070D571 /* AiMouseRecordManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AiMouseRecordManager.m; sourceTree = ""; }; 18EBBA142CDE1791006F3B80 /* VoiceChatCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VoiceChatCell.swift; sourceTree = ""; }; 18EC39D02C4F421C00964461 /* Config.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Config.swift; sourceTree = ""; }; - 18FF930A2C39263A0006452C /* cat.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = cat.png; sourceTree = ""; }; 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 = ""; }; 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 = ""; }; @@ -94,12 +86,12 @@ 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; @@ -110,12 +102,6 @@ 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 = ""; @@ -142,14 +128,16 @@ 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 */, @@ -292,14 +280,8 @@ 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; @@ -355,11 +337,13 @@ 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 */, @@ -528,7 +512,9 @@ ); 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; @@ -541,7 +527,7 @@ "@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"; @@ -569,7 +555,9 @@ ); 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; @@ -582,7 +570,7 @@ "@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"; diff --git a/MixSdk/Example/Example/AppDelegate.swift b/MixSdk/Example/Example/AppDelegate.swift index 91ddc4f..b0cf395 100644 --- a/MixSdk/Example/Example/AppDelegate.swift +++ b/MixSdk/Example/Example/AppDelegate.swift @@ -6,7 +6,6 @@ // import UIKit -import MixSdk import IQKeyboardManagerSwift @main class AppDelegate: UIResponder, UIApplicationDelegate { @@ -15,9 +14,6 @@ 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 diff --git a/MixSdk/Example/Example/BleChatVC.swift b/MixSdk/Example/Example/BleChatVC.swift new file mode 100644 index 0000000..8b1fe4f --- /dev/null +++ b/MixSdk/Example/Example/BleChatVC.swift @@ -0,0 +1,150 @@ +// +// 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) { + + } +} diff --git a/MixSdk/Example/Example/BluetoothManager.swift b/MixSdk/Example/Example/BluetoothManager.swift new file mode 100644 index 0000000..b01d12c --- /dev/null +++ b/MixSdk/Example/Example/BluetoothManager.swift @@ -0,0 +1,181 @@ +// +// 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) + } +} diff --git a/MixSdk/Example/Example/Config.swift b/MixSdk/Example/Example/Config.swift index 577ece6..c426ed0 100644 --- a/MixSdk/Example/Example/Config.swift +++ b/MixSdk/Example/Example/Config.swift @@ -8,10 +8,6 @@ import Foundation - -let gptKey = "sk-proj-04ogvrZQqyaiv1OWw8OZT3BlbkFJG9Gyr8oFvRdmr3oXQNbX" -let gptModel = "gpt-4-turbo" - //废弃 //let deviceId = "281106AFB0F7" //let productId = "pafOCKLiUbeVHbdZlddH" diff --git a/MixSdk/Example/Example/VoiceChatViewController.swift b/MixSdk/Example/Example/PhoneChatVC.swift similarity index 73% rename from MixSdk/Example/Example/VoiceChatViewController.swift rename to MixSdk/Example/Example/PhoneChatVC.swift index 2c95182..a3a32a1 100644 --- a/MixSdk/Example/Example/VoiceChatViewController.swift +++ b/MixSdk/Example/Example/PhoneChatVC.swift @@ -12,20 +12,21 @@ import Toast_Swift -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() { @@ -38,7 +39,7 @@ class VoiceChatViewController: UIViewController{ } func setupViews(){ - self.title = "VoiceChat" + self.title = "phone chat" self.view.backgroundColor = .white @@ -50,12 +51,12 @@ class VoiceChatViewController: UIViewController{ 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) @@ -69,34 +70,30 @@ class VoiceChatViewController: UIViewController{ } 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() @@ -104,24 +101,29 @@ class VoiceChatViewController: UIViewController{ 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() } } @@ -130,7 +132,7 @@ class VoiceChatViewController: UIViewController{ } } } -extension VoiceChatViewController:UITableViewDelegate,UITableViewDataSource { +extension PhoneChatVC:UITableViewDelegate,UITableViewDataSource { func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return dataArray.count } diff --git a/MixSdk/Example/Example/Record/Example-Bridging-Header.h b/MixSdk/Example/Example/Record/Example-Bridging-Header.h index 16dc85b..4c447f5 100644 --- a/MixSdk/Example/Example/Record/Example-Bridging-Header.h +++ b/MixSdk/Example/Example/Record/Example-Bridging-Header.h @@ -4,3 +4,4 @@ #import "AiMouseAudioRecord.h" #import "AiMouseRecordManager.h" +#import "AiMouseAudioRecord.h" diff --git a/MixSdk/Example/Example/Resource/1.pcm b/MixSdk/Example/Example/Resource/1.pcm deleted file mode 100644 index 53b3ea9..0000000 Binary files a/MixSdk/Example/Example/Resource/1.pcm and /dev/null differ diff --git a/MixSdk/Example/Example/Resource/2.pcm b/MixSdk/Example/Example/Resource/2.pcm deleted file mode 100644 index 6c888f9..0000000 Binary files a/MixSdk/Example/Example/Resource/2.pcm and /dev/null differ diff --git a/MixSdk/Example/Example/Resource/AIAudio.sbc b/MixSdk/Example/Example/Resource/AIAudio.sbc deleted file mode 100644 index d96322c..0000000 Binary files a/MixSdk/Example/Example/Resource/AIAudio.sbc and /dev/null differ diff --git a/MixSdk/Example/Example/Resource/AIAudio_2.sbc b/MixSdk/Example/Example/Resource/AIAudio_2.sbc deleted file mode 100644 index d15112d..0000000 Binary files a/MixSdk/Example/Example/Resource/AIAudio_2.sbc and /dev/null differ diff --git a/MixSdk/Example/Example/Resource/cat.png b/MixSdk/Example/Example/Resource/cat.png deleted file mode 100644 index 83c5a57..0000000 Binary files a/MixSdk/Example/Example/Resource/cat.png and /dev/null differ diff --git a/MixSdk/Example/Example/Resource/tts.mp3 b/MixSdk/Example/Example/Resource/tts.mp3 deleted file mode 100644 index c9e45e0..0000000 Binary files a/MixSdk/Example/Example/Resource/tts.mp3 and /dev/null differ diff --git a/MixSdk/Example/Example/Tools/Date.swift b/MixSdk/Example/Example/Tools/Date.swift index b8b71d7..743386c 100644 --- a/MixSdk/Example/Example/Tools/Date.swift +++ b/MixSdk/Example/Example/Tools/Date.swift @@ -1,145 +1,145 @@ +//// +//// 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) +// } +//} diff --git a/MixSdk/Example/Example/Tools/PcmPlayer.swift b/MixSdk/Example/Example/Tools/PcmPlayer.swift index 5854b34..acb3a06 100644 --- a/MixSdk/Example/Example/Tools/PcmPlayer.swift +++ b/MixSdk/Example/Example/Tools/PcmPlayer.swift @@ -1,52 +1,44 @@ -//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() +// } +//} diff --git a/MixSdk/Example/Example/Tools/PlayerManager.swift b/MixSdk/Example/Example/Tools/PlayerManager.swift index 3a96d17..26860cd 100644 --- a/MixSdk/Example/Example/Tools/PlayerManager.swift +++ b/MixSdk/Example/Example/Tools/PlayerManager.swift @@ -8,7 +8,7 @@ import Foundation import AVFoundation -class PlayerManager:NSObject,AVAudioPlayerDelegate{ +class PlayerManager:NSObject{ public static let shared = PlayerManager() private var finishCallback:((_ success:Bool) -> Void)? @@ -37,6 +37,14 @@ class PlayerManager:NSObject,AVAudioPlayerDelegate{ } } + func stop(){ + if player.isPlaying{ + player.stop() + } + } +} + +extension PlayerManager:AVAudioPlayerDelegate{ func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) { // 播放结束时调用 print("播放结束 \(flag)") diff --git a/MixSdk/Example/Example/ViewController.swift b/MixSdk/Example/Example/ViewController.swift index 9599a1d..ab4e995 100644 --- a/MixSdk/Example/Example/ViewController.swift +++ b/MixSdk/Example/Example/ViewController.swift @@ -2,56 +2,104 @@ // 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) @@ -95,15 +143,3 @@ extension ViewController{ } } } -extension ViewController{ - @objc func voiceChatAction() { - if !MixSdk.shared.auth.authorized { - self.view.makeToast("unAuthorized!") - return; - } - let vc = VoiceChatViewController() - self.navigationController?.pushViewController(vc, animated: true) - } - -} - diff --git a/MixSdk/MixSdk/MixSdk.xcodeproj/project.pbxproj b/MixSdk/MixSdk/MixSdk.xcodeproj/project.pbxproj index b5e9796..08f8561 100644 --- a/MixSdk/MixSdk/MixSdk.xcodeproj/project.pbxproj +++ b/MixSdk/MixSdk/MixSdk.xcodeproj/project.pbxproj @@ -155,6 +155,9 @@ 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 */; }; @@ -443,6 +446,9 @@ 18A2DCC62C40D94700A8731B /* BigInt.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BigInt.swift; sourceTree = ""; }; 18A2DCC72C40D94700A8731B /* FloatingPointConversion.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FloatingPointConversion.swift; sourceTree = ""; }; 18A2DCC82C40D94700A8731B /* Array+Extension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Array+Extension.swift"; sourceTree = ""; }; + 18AA04682CE444D40008A345 /* MSBleSender.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MSBleSender.swift; sourceTree = ""; }; + 18AA046B2CE45E800008A345 /* MSBleInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MSBleInfo.swift; sourceTree = ""; }; + 18B1A38B2CE3547F00DD4A7C /* MSBle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MSBle.swift; sourceTree = ""; }; 18C42B4A2C23DA29000D8860 /* SbcToPcm.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SbcToPcm.swift; sourceTree = ""; }; 18C42B5E2C242202000D8860 /* version.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = version.h; sourceTree = ""; }; 18C42B5F2C242202000D8860 /* buffersrc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = buffersrc.h; sourceTree = ""; }; @@ -661,6 +667,9 @@ 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 */, @@ -1349,6 +1358,7 @@ 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 */, @@ -1384,6 +1394,7 @@ 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 */, @@ -1450,6 +1461,7 @@ 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 */, diff --git a/MixSdk/MixSdk/MixSdk/MSAuthModel.swift b/MixSdk/MixSdk/MixSdk/MSAuthModel.swift index 288a94d..2964ca2 100644 --- a/MixSdk/MixSdk/MixSdk/MSAuthModel.swift +++ b/MixSdk/MixSdk/MixSdk/MSAuthModel.swift @@ -6,18 +6,7 @@ // 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 diff --git a/MixSdk/MixSdk/MixSdk/MSBle.swift b/MixSdk/MixSdk/MixSdk/MSBle.swift new file mode 100644 index 0000000..f09006d --- /dev/null +++ b/MixSdk/MixSdk/MixSdk/MSBle.swift @@ -0,0 +1,341 @@ +// +// 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..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) + } + } + } +} diff --git a/MixSdk/MixSdk/MixSdk/MSBleInfo.swift b/MixSdk/MixSdk/MixSdk/MSBleInfo.swift new file mode 100644 index 0000000..5bb32fc --- /dev/null +++ b/MixSdk/MixSdk/MixSdk/MSBleInfo.swift @@ -0,0 +1,19 @@ +// +// 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))" + } +} + diff --git a/MixSdk/MixSdk/MixSdk/MSBleSender.swift b/MixSdk/MixSdk/MixSdk/MSBleSender.swift new file mode 100644 index 0000000..e0446d7 --- /dev/null +++ b/MixSdk/MixSdk/MixSdk/MSBleSender.swift @@ -0,0 +1,84 @@ +// +// 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 + } +} diff --git a/MixSdk/MixSdk/MixSdk/MSVoiceChat.swift b/MixSdk/MixSdk/MixSdk/MSVoiceChat.swift index 87fa6b3..ecdc87d 100644 --- a/MixSdk/MixSdk/MixSdk/MSVoiceChat.swift +++ b/MixSdk/MixSdk/MixSdk/MSVoiceChat.swift @@ -76,10 +76,10 @@ public class MSVoiceChat { /// 开始说话,在发送语音数据前,一定要先调用该方法 public func startSpeedch(){ - if !MixSdk.shared.auth.authorized { - Logger.log("还未鉴权") - return - } +// if !MixSdk.shared.auth.authorized { +// Logger.log("还未鉴权") +// return +// } let params:[String:Any] = [ "header":[ @@ -97,10 +97,10 @@ public class MSVoiceChat { /// 结束说话 public func endSpeedch(){ - if !MixSdk.shared.auth.authorized { - Logger.log("还未鉴权") - return - } +// if !MixSdk.shared.auth.authorized { +// Logger.log("还未鉴权") +// return +// } let params:[String:Any] = [ "header":[ @@ -119,26 +119,26 @@ public class MSVoiceChat { /// 发送语音数据,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) @@ -224,18 +224,30 @@ extension MSVoiceChat{ 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":[ @@ -248,6 +260,12 @@ extension MSVoiceChat{ Logger.log("send" ,cmd) socket.send(text: cmd) } + + func closeWebSocket(){ + if connectState == .connected { + socket?.close(10100,reason: "任务失败") + } + } } @@ -272,12 +290,17 @@ extension MSVoiceChat:WebSocketDelegate{ 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() } diff --git a/MixSdk/MixSdk/MixSdk/MixSdk.swift b/MixSdk/MixSdk/MixSdk/MixSdk.swift index a298e0a..b9649a6 100644 --- a/MixSdk/MixSdk/MixSdk/MixSdk.swift +++ b/MixSdk/MixSdk/MixSdk/MixSdk.swift @@ -5,136 +5,30 @@ // 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() @@ -146,32 +40,34 @@ public class MixSdk:NSObject { /// 初始化工作 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() } } + diff --git a/MixSdk/Podfile.lock b/MixSdk/Podfile.lock index 9690266..985a9ec 100644 --- a/MixSdk/Podfile.lock +++ b/MixSdk/Podfile.lock @@ -19,6 +19,6 @@ SPEC CHECKSUMS: SnapKit: d612e99e678a2d3b95bf60b0705ed0a35c03484a Toast-Swift: dd369e68ee529f542c65bd0d0c6c50cd743cf6f2 -PODFILE CHECKSUM: 1bf708b0b20ea5799d088a09a56f74d1537472c4 +PODFILE CHECKSUM: 9895aef7274631d7896464b4e401f72df13e34f4 -COCOAPODS: 1.14.3 +COCOAPODS: 1.15.2 diff --git a/MixSdk/Pods/Manifest.lock b/MixSdk/Pods/Manifest.lock index 9690266..985a9ec 100644 --- a/MixSdk/Pods/Manifest.lock +++ b/MixSdk/Pods/Manifest.lock @@ -19,6 +19,6 @@ SPEC CHECKSUMS: SnapKit: d612e99e678a2d3b95bf60b0705ed0a35c03484a Toast-Swift: dd369e68ee529f542c65bd0d0c6c50cd743cf6f2 -PODFILE CHECKSUM: 1bf708b0b20ea5799d088a09a56f74d1537472c4 +PODFILE CHECKSUM: 9895aef7274631d7896464b4e401f72df13e34f4 -COCOAPODS: 1.14.3 +COCOAPODS: 1.15.2 diff --git a/MixSdk/podfile b/MixSdk/podfile index 6cd91a1..7eaea03 100644 --- a/MixSdk/podfile +++ b/MixSdk/podfile @@ -10,4 +10,5 @@ target 'Example' do pod 'SnapKit', '~> 5.7.0' pod 'Toast-Swift' pod 'IQKeyboardManagerSwift' +# pod 'SwiftAudio' end