}
func setupVoiceChat(){
- MSVoiceChatSdk.shared.ble.authCallBack = {[unowned self] in
+ MSVoiceChatSdk.shared.auth.authCallBack = {[unowned self] in
self.view.makeToast("鉴权成功!" )
}
MSVoiceChatSdk.shared.voiceChat.start()
import CoreBluetooth
import MSVoiceChatSdk
-/// 主服务
-let kserviceUUID: String = "FFF1"
-let kVoiceUUID: String = "0783B03E-8535-B5A0-7140-A304D2194CB8"
-let kDeviceInfoUUID: String = "0783B63E-8535-B5A0-7140-A304D2495CB6"
+let kserviceUUID: String = "FE01"
+let kWriteUUID: String = "0xFF01"
+let kNotifyUUID: String = "FF02"
public typealias IBDiscoverHandler = (_ peripheral: CBPeripheral) -> Void
public typealias IBDidConectedHandler = (_ peripheral: CBPeripheral) -> Void
class BluetoothManager: NSObject {
static let shared = BluetoothManager()
- /// 2AE6
- private var writeFileCharacteristic: CBCharacteristic!
+ private var writeCharacteristic: CBCharacteristic!
/// 中央管理类,管理蓝牙的扫描连接
var centralManager: CBCentralManager!
/// 当前连接设备
var didConnectedCallBack:IBDidConectedHandler?
/// 设备断开连接
var disConnectedCallBack:IBDisConectedHandler?
+ /// 收到数据
+ var didReceiveDataCallback: ((Data) -> Void)?
private override init() {
super.init()
centralManager = CBCentralManager(delegate: self, queue: nil)
- setupFileSdk()
- }
-
- func setupFileSdk(){
- //初始化
- MSVoiceChatSdk.shared.initSDK()
- //isDebug为true则打印日志,false则不打印日志
- MSVoiceChatSdk.shared.isDebug = true
- //设置sdk数据回调
- MSVoiceChatSdk.shared.ble.sdkToDeviceCallback = {[unowned self] data in
- //这里将回调的数据发给蓝牙设备
- self.send(data)
- }
}
}
func startScan() {
print("开始扫描...")
centralManager.scanForPeripherals(withServices: nil)
-// centralManager.scanForPeripherals(withServices: [CBUUID(string: kserviceUUID)], options: nil)
+ // centralManager.scanForPeripherals(withServices: [CBUUID(string: kserviceUUID)], options: nil)
if let peripheral = centralManager.retrieveConnectedPeripherals(withServices: [CBUUID(string: kserviceUUID)]).first{
discoverCallBack?(peripheral)
}
print("手机未连上蓝牙设备!")
return
}
- peripheral.writeValue(data, for: writeFileCharacteristic, type: .withoutResponse)
+ peripheral.writeValue(data, for: writeCharacteristic, type: .withoutResponse)
}
}
break
}
}
-
+
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
-// IBManager.discoverHandler?(device)
discoverCallBack?(peripheral)
}
extension BluetoothManager: CBPeripheralDelegate {
func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
- peripheral
- .services?
- .forEach {
- peripheral.discoverCharacteristics(nil, for: $0)
- }
+ 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
- }
+ service.characteristics?.forEach {
+ if $0.uuid.uuidString == kNotifyUUID {
+ peripheral.setNotifyValue(true, for: $0)
+ return
+ }
+ if $0.uuid.uuidString == kWriteUUID {
+ writeCharacteristic = $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 {
- MSVoiceChatSdk.shared.ble.sendToSdk(voiceData: characteristic.value)
- }
- else if characteristic.uuid.uuidString == kDeviceInfoUUID{
- MSVoiceChatSdk.shared.ble.sendToSdk(infoData: characteristic.value)
+ if let data = characteristic.value{
+ didReceiveDataCallback?(data)
}
}
func peripheral(_ peripheral: CBPeripheral, didWriteValueFor characteristic: CBCharacteristic, error: Error?) {
- // sdk将回调的数据传给ble设备后,告知sdk写入的情况
-// MSVoiceChatSdk.shared.ble.didWriteToDevice(characteristic: characteristic, error: error)
+
}
}
class ViewController: UIViewController {
+
+
let tableView = UITableView(frame:.zero, style: .plain)
var dataArray = [CBPeripheral]()
title = "扫描"
setupViews()
setupBle()
+ setupVoiceChatSdk()
}
override func viewDidAppear(_ animated: Bool) {
}
}
+ func setupVoiceChatSdk(){
+ //初始化
+ MSVoiceChatSdk.shared.initSDK()
+ //isDebug为true则打印日志,false则不打印日志
+ MSVoiceChatSdk.shared.isDebug = true
+ //设置sdk数据回调
+ MSVoiceChatSdk.shared.ble.sdkToDeviceCallback = {data in
+ //这里将回调的数据发给蓝牙设备
+ BluetoothManager.shared.send(data)
+ }
+ }
+
func setupBle() {
BluetoothManager.shared.discoverCallBack = { [unowned self] peripheral in
if dataArray.contains(peripheral){
BluetoothManager.shared.didConnectedCallBack = {[unowned self] peripheral in
self.view.hideToastActivity()
// let vc = PhoneChatVC()
- let vc = BleChatVC()
- self.navigationController?.pushViewController(vc, animated: true)
+// let vc = BleChatVC()
+// self.navigationController?.pushViewController(vc, animated: true)
+ MSVoiceChatSdk.shared.ble.didConnectBle()
}
BluetoothManager.shared.disConnectedCallBack = { [unowned self] peripheral in
print("蓝牙已经断开!!!!!!!!!!!")
self.view.makeToast("蓝牙已经断开!")
+ MSVoiceChatSdk.shared.ble.didDisconnectBle()
+ }
+ BluetoothManager.shared.didReceiveDataCallback = {data in
+ // 收到蓝牙设备的数据,转发给sdk处理
+ MSVoiceChatSdk.shared.ble.sendToSdk(data)
}
}
}
extension ViewController{
@objc func registerAction() {
+// let data = MSTool.hexStringToData("000000000000000000000000000064")
+// let data = MSTool.hexStringToData("000000000000000000000000000141333832302d5635382d323032343132303431343038333432")
+// let data = MSTool.hexStringToData("0000000000000000000000000002303045303831444335333141")
+// let data = MSTool.hexStringToData("00000000000000000000000002000400003e80")
+// MSVoiceChatSdk.shared.ble.sendToSdk(data)
view.makeToastActivity(.center)
MSVoiceChatSdk.shared.auth.register(productId: productId, productKey: productKey, deviceId: deviceId){[weak self] password,error in
self?.view.hideToastActivity()
18314AA32C3D37EE0028CE50 /* AudioTransform.h in Headers */ = {isa = PBXBuildFile; fileRef = 18314A9F2C3D37ED0028CE50 /* AudioTransform.h */; };
18314AA42C3D37EE0028CE50 /* AudioTransform.m in Sources */ = {isa = PBXBuildFile; fileRef = 18314AA02C3D37ED0028CE50 /* AudioTransform.m */; };
186BFD492C57915500673D8C /* MSVoiceChat.swift in Sources */ = {isa = PBXBuildFile; fileRef = 186BFD482C57915500673D8C /* MSVoiceChat.swift */; };
- 187C54642C6B7C4B0022C3D4 /* MSCompress.swift in Sources */ = {isa = PBXBuildFile; fileRef = 187C54632C6B7C4B0022C3D4 /* MSCompress.swift */; };
188695A22C59DA5700B9B89F /* MSVoiceChatModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 188695A12C59DA5700B9B89F /* MSVoiceChatModel.swift */; };
18A2DCC92C40D94800A8731B /* AES.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DC4F2C40D94700A8731B /* AES.swift */; };
18A2DCCA2C40D94800A8731B /* ASN1Scanner.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DC512C40D94700A8731B /* ASN1Scanner.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 */; };
+ 18C21AA42D0005AF00F718A7 /* MSBleManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18C21AA32D0005AF00F718A7 /* MSBleManager.swift */; };
+ 18C21AA92D000C2700F718A7 /* NSBleManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18C21AA82D000C2700F718A7 /* NSBleManager.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 */; };
18314A9F2C3D37ED0028CE50 /* AudioTransform.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AudioTransform.h; sourceTree = "<group>"; };
18314AA02C3D37ED0028CE50 /* AudioTransform.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AudioTransform.m; sourceTree = "<group>"; };
186BFD482C57915500673D8C /* MSVoiceChat.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MSVoiceChat.swift; sourceTree = "<group>"; };
- 187C54632C6B7C4B0022C3D4 /* MSCompress.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MSCompress.swift; sourceTree = "<group>"; };
188695A12C59DA5700B9B89F /* MSVoiceChatModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MSVoiceChatModel.swift; sourceTree = "<group>"; };
18A2DC4F2C40D94700A8731B /* AES.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AES.swift; sourceTree = "<group>"; };
18A2DC512C40D94700A8731B /* ASN1Scanner.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ASN1Scanner.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>"; };
+ 18C21AA32D0005AF00F718A7 /* MSBleManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MSBleManager.swift; sourceTree = "<group>"; };
+ 18C21AA82D000C2700F718A7 /* NSBleManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSBleManager.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>"; };
18204BBE2C193E3D00F94E69 /* MSVoiceChatSdk.h */,
18C42C762C24241A000D8860 /* MSVoiceChatSdk-Bridging-Header.h */,
1811882E2C1A89E600C23FAB /* MSVoiceChatSdk.swift */,
- 18B1A38B2CE3547F00DD4A7C /* MSBle.swift */,
- 18AA046B2CE45E800008A345 /* MSBleInfo.swift */,
- 18AA04682CE444D40008A345 /* MSBleSender.swift */,
- 18204BC52C193E4B00F94E69 /* MSAuth.swift */,
- 18314A882C3CD55E0028CE50 /* MSAuthModel.swift */,
- 186BFD482C57915500673D8C /* MSVoiceChat.swift */,
- 188695A12C59DA5700B9B89F /* MSVoiceChatModel.swift */,
18204BEF2C19457600F94E69 /* MSHttpManager.swift */,
181188442C1AC35F00C23FAB /* MSError.swift */,
- 187C54632C6B7C4B0022C3D4 /* MSCompress.swift */,
+ 18C21AA52D000ADD00F718A7 /* ble */,
+ 18C21AA62D000AEE00F718A7 /* auth */,
+ 18C21AA72D000AFD00F718A7 /* chat */,
);
path = MixSdk;
sourceTree = "<group>";
path = CS_BigInt;
sourceTree = "<group>";
};
+ 18C21AA52D000ADD00F718A7 /* ble */ = {
+ isa = PBXGroup;
+ children = (
+ 18B1A38B2CE3547F00DD4A7C /* MSBle.swift */,
+ 18C21AA82D000C2700F718A7 /* NSBleManager.swift */,
+ 18C21AA32D0005AF00F718A7 /* MSBleManager.swift */,
+ 18AA046B2CE45E800008A345 /* MSBleInfo.swift */,
+ 18AA04682CE444D40008A345 /* MSBleSender.swift */,
+ );
+ path = ble;
+ sourceTree = "<group>";
+ };
+ 18C21AA62D000AEE00F718A7 /* auth */ = {
+ isa = PBXGroup;
+ children = (
+ 18204BC52C193E4B00F94E69 /* MSAuth.swift */,
+ 18314A882C3CD55E0028CE50 /* MSAuthModel.swift */,
+ );
+ path = auth;
+ sourceTree = "<group>";
+ };
+ 18C21AA72D000AFD00F718A7 /* chat */ = {
+ isa = PBXGroup;
+ children = (
+ 186BFD482C57915500673D8C /* MSVoiceChat.swift */,
+ 188695A12C59DA5700B9B89F /* MSVoiceChatModel.swift */,
+ );
+ path = chat;
+ sourceTree = "<group>";
+ };
18C42B5B2C242202000D8860 /* FFmpeg-iOS */ = {
isa = PBXGroup;
children = (
18A2DD1B2C40D94900A8731B /* CCM.swift in Sources */,
18A2DD2E2C40D94900A8731B /* PrimeTest.swift in Sources */,
18A2DCDF2C40D94800A8731B /* UInt16+Extension.swift in Sources */,
- 187C54642C6B7C4B0022C3D4 /* MSCompress.swift in Sources */,
18A2DD162C40D94900A8731B /* BlockModeOptions.swift in Sources */,
18314A912C3D13A60028CE50 /* AudioConvert.m in Sources */,
18A2DCF42C40D94800A8731B /* UInt8+Extension.swift in Sources */,
18204BFB2C19488900F94E69 /* Upload.swift in Sources */,
18A2DD2B2C40D94900A8731B /* WordsAndBits.swift in Sources */,
18204BFA2C19488900F94E69 /* Request.swift in Sources */,
+ 18C21AA92D000C2700F718A7 /* NSBleManager.swift in Sources */,
18314A862C3CD24E0028CE50 /* MSUtils.m in Sources */,
1811882F2C1A89E600C23FAB /* MSVoiceChatSdk.swift in Sources */,
18A2DCD32C40D94800A8731B /* HKDF.swift in Sources */,
18A2DD1F2C40D94900A8731B /* CipherModeWorker.swift in Sources */,
18A2DD122C40D94900A8731B /* PKCS7.swift in Sources */,
18314AA42C3D37EE0028CE50 /* AudioTransform.m in Sources */,
+ 18C21AA42D0005AF00F718A7 /* MSBleManager.swift in Sources */,
18A2DCE42C40D94800A8731B /* UInt128.swift in Sources */,
18A2DD052C40D94800A8731B /* Updatable.swift in Sources */,
18A2DD182C40D94900A8731B /* CFB.swift in Sources */,
+++ /dev/null
-////
-//// MSCompress.swift
-//// Example
-////
-//// Created by incar on 2024/8/13.
-////
-//
-//import UIKit
-//import SCompressLib
-//
-///// 压缩回调
-//public typealias MSCompressCallBack = (_ retData: [UInt8]) -> Void
-///// 解压回调
-//public typealias MSDeCompressCallBack = (_ image: UIImage?) -> Void
-//
-//public class MSCompress{
-// fileprivate static var compressCallBack:MSCompressCallBack?
-// fileprivate static var decompressCallBack:MSDeCompressCallBack?
-//
-// public static func compress(image:UIImage,finished: @escaping MSCompressCallBack){
-// compressCallBack = finished
-//
-// print("start = ",Date().timeIntervalSince1970)
-// let width = Int(image.size.width)
-// let height = Int(image.size.height)
-// print("width = \(width), height = \(height)")
-// let pixelFormat = image.cgImage?.pixelFormatInfo
-// print("pixelFormat = \(pixelFormat.debugDescription)")
-// var sParam = SCompressParam()
-// sParam.id = 15
-// sParam.width = Int32(width)
-// sParam.height = Int32(height)
-// sParam.stride = Int32(width)
-// sParam.modeRgb = 1
-// sParam.modeAlpha = 1
-// sParam.cmpMode = 0
-// sParam.tileWidth = 4
-// sParam.pixelFormat = 1
-//
-// let provider = image.cgImage!.dataProvider
-// let providerData = provider!.data
-// let data = CFDataGetBytePtr(providerData)
-//
-// for hei in 0...(height - 1) {
-// for wid in 0...(width - 1) {
-// let color = image[data!,providerData!,wid, hei]
-// sParam.redChannel.append(color.r)
-// sParam.greenChannel.append(color.g)
-// sParam.blueChannel.append(color.b)
-// sParam.alphaChannel.append(color.a)
-// }
-// }
-// print("red channel len = \(sParam.redChannel.count)")
-// let picApi = SPicZipApi()
-// SetZipEvent(picEvent: MyZipRets())
-// picApi.Compress(compressParam: sParam)
-// print("end = ",Date().timeIntervalSince1970)
-// }
-//
-// public static func deCompress(data:Data,finished: @escaping MSDeCompressCallBack){
-// decompressCallBack = finished
-// print("start = ",Date().timeIntervalSince1970)
-// let picApi = SPicZipApi()
-// var compressedFile : [UInt8] = []
-// for i in 0...data.count - 1 {
-// compressedFile.append(data[i])
-// }
-// picApi.Decompress(id: 39, fileBuffer: compressedFile)
-// print("end = ",Date().timeIntervalSince1970)
-// }
-//
-//}
-//
-//fileprivate func drawRect(decomRets: SCompressLib.SDecompressRets) ->CGImage?{
-// let width = decomRets.width
-// let height = decomRets.height
-// let boundingBox = CGRectMake(0, 0, CGFloat(width), CGFloat(height))
-// let context = createBitmapContext(pixelsWide: Int(width), Int(height))
-// let data = context!.data
-// var pixelIdx = 0
-// var chanIdx = 0
-// for i in 0...height - 1 {
-// for j in 0...width - 1 {
-// chanIdx = Int(i * width + j)
-// data?.storeBytes(of:decomRets.alphaChannel[chanIdx], toByteOffset: pixelIdx, as: UInt8.self)
-// pixelIdx += 1
-// data?.storeBytes(of:decomRets.redChannel[chanIdx], toByteOffset: pixelIdx, as: UInt8.self)
-// pixelIdx += 1
-// data?.storeBytes(of:decomRets.greenChannel[chanIdx], toByteOffset: pixelIdx, as: UInt8.self)
-// pixelIdx += 1
-// data?.storeBytes(of:decomRets.blueChannel[chanIdx], toByteOffset: pixelIdx, as: UInt8.self)
-// pixelIdx += 1
-// }
-// }
-// let decompressPic = context!.makeImage()
-// return decompressPic
-//}
-//
-//fileprivate func createBitmapContext(pixelsWide: Int, _ pixelsHigh: Int) -> CGContext? {
-// let bytesPerPixel = 4 // 生成包括ARGB四个通道的像素点,每个像素点需要4个字节
-// let bytesPerRow = bytesPerPixel * pixelsWide
-// let bitsPerComponent = 8
-// let byteCount = (bytesPerRow * pixelsHigh)
-// let colorSpace = CGColorSpaceCreateDeviceRGB()
-// let pixels = UnsafeMutablePointer<CUnsignedChar>.allocate(capacity:byteCount)
-// let bitmapInfo = CGImageAlphaInfo.premultipliedFirst.rawValue | CGBitmapInfo.byteOrder32Big.rawValue
-// let context = CGContext(data: pixels, width: pixelsWide, height: pixelsHigh, bitsPerComponent: bitsPerComponent, bytesPerRow: bytesPerRow, space: colorSpace, bitmapInfo: bitmapInfo)
-// return context
-//}
-//
-//fileprivate struct MyZipRets : PicZipEvent {
-// func DecompressRets(id: Int, state: Int, decomRets: SCompressLib.SDecompressRets) {
-// let imageWidth = Int(decomRets.width)
-// let imageHeight = Int(decomRets.height)
-// var cgImg = drawRect(decomRets: decomRets)
-// var img = (cgImg != nil) ? UIImage(cgImage: cgImg!):nil
-// MSCompress.decompressCallBack?(img)
-// print("decompress finish id ====== \(id)")
-// }
-//
-// func CompressRets(id: Int, state: Int, tileWidth: Int, retData: [UInt8]) {
-// MSCompress.compressCallBack?(retData)
-// }
-//}
-//
-//
-//fileprivate extension UIImage {
-// subscript (data:UnsafePointer<UInt8>,providerData:CFData,x: Int, y: Int) -> (r : UInt8, g : UInt8, b : UInt8, a : UInt8) {
-// if x < 0 || x > Int(size.width) || y < 0 || y > Int(size.height) {
-// return (0, 0, 0, 0)
-// }
-// let numberOfComponents = 4
-// let pixelData = ((Int(size.width) * y) + x) * numberOfComponents
-// let r = UInt8(data[pixelData])
-// let g = UInt8(data[pixelData + 1])
-// let b = UInt8(data[pixelData + 2])
-// let a = UInt8(data[pixelData + 3])
-// return (r, g, b, a)
-// }
-//}
// static let authBaseURL = "http://dmp.raisound.com/bz-api/"
// static let wsHeartBeatTime = 5.0
-
static func authUrl(api:String)->String{
return authBaseURL + api
}
public var logs: [String] { Logger.logs }
/// SDK 版本号
- public let version: String = "1.0.0"
+ public let version: String = "v2024120401"
/// 默认为 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()
import UIKit
import CommonCrypto
-class MSTool{
+public class MSTool{
static func dataToUtf8String(_ data:Data) ->String{
let text = String(data: data, encoding: .utf8)
return text ?? ""
}
return output
}
+
+ public static func hexStringToData(_ hexStr: String) -> Data {
+ let hexStrWithoutSpaces = hexStr.replacingOccurrences(of: " ", with: "")
+ let lowercasedHexStr = hexStrWithoutSpaces.lowercased()
+ let len = lowercasedHexStr.count
+ if len == 0 {
+ return Data()
+ }
+
+ let utf16Chars = lowercasedHexStr.utf16
+
+ var result = Data()
+ var bytes: UInt8
+ var str = [CChar](repeating: 0, count: 3)
+ for i in 0..<len / 2 {
+ str[0] = CChar(utf16Chars[utf16Chars.index(utf16Chars.startIndex, offsetBy: 2 * i)])
+ str[1] = CChar(utf16Chars[utf16Chars.index(utf16Chars.startIndex, offsetBy: 2 * i + 1)])
+ bytes = UInt8(strtoul(str, nil, 16))
+ result.append(&bytes, count: 1)
+ }
+ return result
+ }
}
/// 鉴权管理类
public class MSAuth {
+ /// 鉴权回调
+ public var authCallBack:(()->Void)?
+
/// 是否已经鉴权
public private(set) var authorized = false
self.authorized = true
self.deviceToken = dataDict["token"] as? String ?? ""
- MSVoiceChatSdk.shared.setupWebsoket()
completeHandler(self.deviceToken,nil)
}
else{
// Created by incar on 2024/11/12.
//
-import Foundation
-
+// 0xFF01 写通讯特征值
+// 0xFF02 读/通知特征值
-
-/// SDK 管理类
+import Foundation
public class MSBle {
- public var authCallBack:(()->Void)?
- public var didAuth:Bool{
- return MSVoiceChatSdk.shared.auth.authorized
- }//是否正在鉴权
-
var lastSender:MSBleSender?
var deviceInfo:MSBleInfo?
var isAuthing = false//是否正在鉴权
-
init() {
}
-
- /// sdk发送数据回调,app将此数据转发给蓝牙设备,写入设备成功后,需要调didWriteToDevice(characteristic:, error:)告知sdk是否写入成功
+
+ /// 连上蓝牙设备后必须调此方法通知sdk
+ public func didConnectBle(){
+ Logger.log("蓝牙连接" )
+ startAuth()
+ }
+ /// 断开蓝牙设备后必须调此方法通知sdk
+ public func didDisconnectBle(){
+ Logger.log("蓝牙已断开" )
+ }
+
+ /// sdk发送数据回调,app将此数据转发给蓝牙设备
public var sdkToDeviceCallback:((_ data: Data) -> Void)?
/// 收到蓝牙设备传过来的数据,转发给sdk处理
- public func sendToSdk(voiceData data: Data?){
+ public func sendToSdk(_ 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() + 2.0) {
-// self.startAuth()
-// }
-// return
-// }
- if(data.count > 16){
- MSAudioConvert.sbcTopcm(data){ pcm in
- MSVoiceChatSdk.shared.voiceChat.sendVoice(pcmData: pcm)
- }
+ Logger.log("[recv]" + data.joined(""))
+
+ if data.count < 12{
+ Logger.log("接收到的蓝牙数据异常")
+ return
}
+// receive(data)
+
+ // if let sender = lastSender{
+ // lastSender = nil
+ // sender.completion?(.response.code(.success).value(data))
+ // sender.remove()
+ //
+ // deinitTimer()
+ // }
}
- /// 收到蓝牙设备传过来的数据,转发给sdk处理
- public func sendToSdk(infoData data: Data?){
- guard let data = data else{
- Logger.log("[recv][info]")
- return
+}
+
+extension MSBle{
+ func receive(_ data:Data){
+ let payload = data.suffix(from: 12).map({ $0 })
+ let cmd = payload[0].int
+ let type = payload[1].int
+ Logger.log("cmd = \(cmd) type = \(type)")
+ switch cmd {
+ case 0x00:
+ Logger.log("获取设备信息")
+ receiveDeviceInfo(payload)
+ case 0x01:
+ Logger.log("设置设备数据")
+ receiveSetDeviceInfo(payload)
+ case 0x02:
+ Logger.log("AI语音对话")
+ receiveVoice(payload)
+ default:
+ Logger.log("未定义指令")
}
- Logger.log("[recv][info] " + data.joined("") + MSTool.dataToUtf8String(data))
- if data.joined("") == "7b2272737369223a2230227d"{
- return
+ }
+ func receiveDeviceInfo(_ list:[UInt8]){
+ let type = list[1].int
+ switch type {
+ case 0x00:
+ let value = list[2].int
+ Logger.log("电池电量 \(value)")
+
+ case 0x01:
+ let value = String(data: Data(list.suffix(from: 2)), encoding: .utf8) ?? ""
+ Logger.log("软件版本 \(value)")
+ case 0x02:
+ let value = String(data: Data(list.suffix(from: 2)), encoding: .utf8) ?? ""
+ Logger.log("MAC地址 \(value)")
+ case 0x03:
+ let value = String(data: Data(list.suffix(from: 2)), encoding: .utf8) ?? ""
+ Logger.log("产品密钥 \(value)")
+
+ case 0x04:
+ let value = String(data: Data(list.suffix(from: 2)), encoding: .utf8) ?? ""
+ Logger.log("产品密码 \(value)")
+ case 0x05:
+ let value = String(data: Data(list.suffix(from: 2)), encoding: .utf8) ?? ""
+ Logger.log("设备密码 \(value)")
+ default:
+ Logger.log("未定义指令")
}
- if let sender = lastSender{
- lastSender = nil
- sender.completion?(.response.code(.success).value(data))
- sender.remove()
+ }
+ func receiveSetDeviceInfo(_ list:[UInt8]){
+ let type = list[1].int
+ switch type {
+ case 0x00:
+ let value = list[2].int
+ Logger.log("密码设置 \(value)")
- deinitTimer()
+ case 0x01:
+ let value = list[2].int
+ Logger.log("品牌名称设置 \(value)")
+ default:
+ Logger.log("未定义指令")
+ }
+ }
+ func receiveVoice(_ list:[UInt8]){
+ let type = list[1].int
+ switch type {
+ case 0x00:
+ let value = list[2].int
+ Logger.log("开始录音 \(value)")
+
+ case 0x01:
+ let value = list[2].int
+ Logger.log("停止录音 \(value)")
+ case 0x02:
+ Logger.log("音频流 \(Data(list.suffix(from: 2)))")
+ default:
+ Logger.log("未定义指令")
}
}
- /// 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 {
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 requestDeviceId(finished: @escaping (String?) -> Void){
- let sender = dictionaryToSender(["cmd":"get_MAC"])
+ let cmd = [0.byte]
+ let type = [2.byte]
+ let data = Data(cmd + type)
+ let sender = MSBleSender()
+ sender.list.append(data)
sender.completion = { result in
Logger.log("\(result.description)")
if result.isSuccess,let dict = MSTool.dataToJson(result.data) ,let value = dict["MAC"] as? String{
sendToBle(sender)
}
func requestProductKey(finished: @escaping (String?) -> Void){
- let sender = dictionaryToSender(["cmd":"get_productKey"])
+ let cmd = [0.byte]
+ let type = [3.byte]
+ let data = Data(cmd + type)
+ let sender = MSBleSender()
+ sender.list.append(data)
sender.completion = { result in
Logger.log("\(result.description)")
if result.isSuccess,let dict = MSTool.dataToJson(result.data) ,let value = dict["productKey"] as? String{
sendToBle(sender)
}
func requestSecretKey(finished: @escaping (String?) -> Void){
- let sender = dictionaryToSender(["cmd":"get_secretKey"])
+ let cmd = [0.byte]
+ let type = [4.byte]
+ let data = Data(cmd + type)
+ let sender = MSBleSender()
+ sender.list.append(data)
sender.completion = { result in
Logger.log("\(result.description)")
if result.isSuccess,let dict = MSTool.dataToJson(result.data) ,let value = dict["secretKey"] as? String{
sendToBle(sender)
}
func requestDeviceSecret(finished: @escaping (String?) -> Void){
- let sender = dictionaryToSender(["cmd":"get_deviceSecret"])
+ let cmd = [0.byte]
+ let type = [5.byte]
+ let data = Data(cmd + type)
+ let sender = MSBleSender()
+ sender.list.append(data)
sender.completion = { result in
Logger.log("\(result.description)")
if result.isSuccess,let dict = MSTool.dataToJson(result.data) ,let value = dict["deviceSecret"] as? String{
startTimer()
for data in sender.list{
if let sendToBleBlock = self.sdkToDeviceCallback {
- sendToBleBlock(data)
+ Logger.log("[send]" + data.joined())
+// sendToBleBlock(data)
}
}
}
}
}
-
-extension MSBle{
- func dictionaryToSender(_ dict: [String: Any]) ->MSBleSender{
- let sender = MSBleSender()
- let str = MSTool.dataToUtf8String(MSTool.jsonToData(dict)!)
- 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) {
MSVoiceChatSdk.shared.auth.register(productId: deviceInfo!.productId, productKey: deviceInfo!.productKey, deviceId: deviceInfo!.deviceId){[weak self] password,error in
--- /dev/null
+//
+// MSBleManager.swift
+// MSVoiceChatSdk
+//
+// Created by incar on 2024/12/4.
+//
+
+import Foundation
+
+class MSBleManager{
+
+
+
+
+}
--- /dev/null
+////
+//// MSBle.swift
+//// MSVoiceChatSdk
+////
+//// Created by incar on 2024/11/12.
+////
+//
+//import Foundation
+//public class MSBle {
+// 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() + 2.0) {
+//// self.startAuth()
+//// }
+//// return
+//// }
+// if(data.count > 16){
+// MSAudioConvert.sbcTopcm(data){ pcm in
+// MSVoiceChatSdk.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("") + MSTool.dataToUtf8String(data))
+// if data.joined("") == "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}
+// 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 dict = MSTool.dataToJson(result.data) ,let value = dict["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 dict = MSTool.dataToJson(result.data) ,let value = dict["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 dict = MSTool.dataToJson(result.data) ,let value = dict["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 dict = MSTool.dataToJson(result.data) ,let value = dict["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 str = MSTool.dataToUtf8String(MSTool.jsonToData(dict)!)
+// 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) {
+// MSVoiceChatSdk.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
+// }
+//
+// MSVoiceChatSdk.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)
+// }
+// }
+// }
+//}