泰凌微调试
authorliangzhenying <867174124@qq.com>
Thu, 22 Aug 2024 07:17:37 +0000 (15:17 +0800)
committerliangzhenying <867174124@qq.com>
Thu, 22 Aug 2024 07:17:37 +0000 (15:17 +0800)
17 files changed:
MouseSdk/AiMouseSdk/TlwMouseSdk/ADPCMConverter.h [new file with mode: 0644]
MouseSdk/AiMouseSdk/TlwMouseSdk/ADPCMConverter.m [new file with mode: 0644]
MouseSdk/AiMouseSdk/TlwMouseSdk/Aiaa.h [new file with mode: 0644]
MouseSdk/AiMouseSdk/TlwMouseSdk/TlwAudioManager.h [new file with mode: 0644]
MouseSdk/AiMouseSdk/TlwMouseSdk/TlwAudioManager.m [new file with mode: 0644]
MouseSdk/AiMouseSdk/TlwMouseSdk/TlwConnectManager.h [new file with mode: 0644]
MouseSdk/AiMouseSdk/TlwMouseSdk/TlwConnectManager.m [new file with mode: 0644]
MouseSdk/AiMouseSdk/TlwMouseSdk/TlwMouseSdk.h [new file with mode: 0644]
MouseSdk/AiMouseSdk/TlwMouseSdk/TlwMouseSdk.m [new file with mode: 0644]
MouseSdk/AiMouseSdk/TlwMouseSdk/TlwRecordManager.h [new file with mode: 0644]
MouseSdk/AiMouseSdk/TlwMouseSdk/TlwRecordManager.m [new file with mode: 0644]
MouseSdk/AiMouseSdk/TlwMouseSdk/TlwTools.h [new file with mode: 0644]
MouseSdk/AiMouseSdk/TlwMouseSdk/TlwTools.m [new file with mode: 0644]
MouseSdk/AiMouseSdk/TlwMouseSdk/adpcm.c [new file with mode: 0644]
MouseSdk/AiMouseSdk/TlwMouseSdk/adpcm.h [new file with mode: 0644]
MouseSdk/MouseSdk.xcodeproj/project.pbxproj
MouseSdk/MouseSdk/ViewController.m

diff --git a/MouseSdk/AiMouseSdk/TlwMouseSdk/ADPCMConverter.h b/MouseSdk/AiMouseSdk/TlwMouseSdk/ADPCMConverter.h
new file mode 100644 (file)
index 0000000..0558f1e
--- /dev/null
@@ -0,0 +1,18 @@
+//
+//  AiAdpcm2pcm.h
+//  AiMouseSdk
+//
+//  Created by incar on 2024/8/20.
+//
+
+#import <Foundation/Foundation.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface ADPCMConverter : NSObject
+
+- (NSData *)convertADPCMToPCM:(NSData *)adpcmData;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/MouseSdk/AiMouseSdk/TlwMouseSdk/ADPCMConverter.m b/MouseSdk/AiMouseSdk/TlwMouseSdk/ADPCMConverter.m
new file mode 100644 (file)
index 0000000..42e25f3
--- /dev/null
@@ -0,0 +1,92 @@
+
+#import "ADPCMConverter.h"
+#import <Foundation/Foundation.h>
+
+
+
+@implementation ADPCMConverter
+
+static const signed char idxtbl[] = { -1, -1, -1, -1, 2, 4, 6, 8, -1, -1, -1, -1, 2, 4, 6, 8};
+static const unsigned short steptbl[] = {
+    7,  8,  9,  10,  11,  12,  13,  14,  16,  17,
+    19,  21,  23,  25,  28,  31,  34,  37,  41,  45,
+    50,  55,  60,  66,  73,  80,  88,  97,  107, 118,
+    130, 143, 157, 173, 190, 209, 230, 253, 279, 307,
+    337, 371, 408, 449, 494, 544, 598, 658, 724, 796,
+    876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066,
+    2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358,
+    5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899,
+       15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767
+};
+
+#define NUM_OF_ORIG_SAMPLE 2
+
+- (NSData *)convertADPCMToPCM:(NSData *)adpcmData {
+    NSMutableData *pcmData = [NSMutableData data];
+    const signed short *source = (const signed short *)[adpcmData bytes];
+    signed short *destination = (signed short *)[pcmData mutableBytes];
+    int len = (int)[adpcmData length] / sizeof(signed short);
+
+    int i;
+    // 假设 source 中的前两个元素分别是 predict 和 predict_idx
+    int predict = source[0];
+    int predict_idx = source[1] & 0xff;
+    const unsigned char *pcode = (const unsigned char *)(source + NUM_OF_ORIG_SAMPLE);
+    unsigned char code;
+    code = *pcode++;
+
+    for (i = 0; i < len; i++) {
+        if (1) {
+            int step = steptbl[predict_idx];
+
+            int diffq = step >> 3;
+
+            if (code & 4) {
+                diffq = diffq + step;
+            }
+            step = step >> 1;
+            if (code & 2) {
+                diffq = diffq + step;
+            }
+            step = step >> 1;
+            if (code & 1) {
+                diffq = diffq + step;
+            }
+
+            if (code & 8) {
+                predict = predict - diffq;
+            } else {
+                predict = predict + diffq;
+            }
+
+            if (predict > 32767) {
+                predict = 32767;
+            } else if (predict < -32768) {
+                predict = -32768;
+            }
+
+            predict_idx = predict_idx + idxtbl[code & 15];
+
+            if (predict_idx < 0) {
+                predict_idx = 0;
+            } else if (predict_idx > 88) {
+                predict_idx = 88;
+            }
+
+            if (i & 1) {
+                code = *pcode++;
+            } else {
+                code = code >> 4;
+            }
+        }
+
+        if (0 && i < NUM_OF_ORIG_SAMPLE) {
+            destination[i] = source[i];
+        } else {
+            destination[i] = predict;
+        }
+    }
+    return pcmData;
+}
+
+@end
diff --git a/MouseSdk/AiMouseSdk/TlwMouseSdk/Aiaa.h b/MouseSdk/AiMouseSdk/TlwMouseSdk/Aiaa.h
new file mode 100644 (file)
index 0000000..f31d545
--- /dev/null
@@ -0,0 +1,21 @@
+//
+//  Aiaa.h
+//  AiMouseSdk
+//
+//  Created by incar on 2024/4/27.
+//
+
+#import <Foundation/Foundation.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface AiTrippleModel : NSObject
+@property (nonatomic,strong) NSString *productKey;//产品秘钥
+@property (nonatomic,strong) NSString *deviceName;//设备名称
+@property (nonatomic,strong) NSString *timeStamp;//时间戳
+@property (nonatomic,strong) NSString *signmethod;//签名方法
+@property (nonatomic,strong) NSString *sign; //签名
+@property (nonatomic,strong) NSString *sonce;//用于签名的随机盐值
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/MouseSdk/AiMouseSdk/TlwMouseSdk/TlwAudioManager.h b/MouseSdk/AiMouseSdk/TlwMouseSdk/TlwAudioManager.h
new file mode 100644 (file)
index 0000000..bcbc620
--- /dev/null
@@ -0,0 +1,20 @@
+//
+//  TlwAudioManager.h
+//  AiMouse
+//
+//  Created by incar on 2024/2/28.
+//
+
+#import <Foundation/Foundation.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface TlwAudioManager : NSObject
+@property (nonatomic,strong)void (^outputData)(NSData *data);
+@property (nonatomic,strong)void (^decodeCallBack)(NSData *data);
+@property (nonatomic,strong)void (^outputLog)(NSString *log);
+
+- (void)receiveData:(NSData *)data;
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/MouseSdk/AiMouseSdk/TlwMouseSdk/TlwAudioManager.m b/MouseSdk/AiMouseSdk/TlwMouseSdk/TlwAudioManager.m
new file mode 100644 (file)
index 0000000..460f163
--- /dev/null
@@ -0,0 +1,50 @@
+//
+//  TlwAudioManager.m
+//  AiMouse
+//
+//  Created by incar on 2024/2/28.
+//
+
+#import "TlwAudioManager.h"
+#import <AVFoundation/AVFoundation.h>
+#import "ADPCMConverter.h"
+#import "adpcm.h"
+
+@interface TlwAudioManager ()
+@end
+
+@implementation TlwAudioManager
+
+-(instancetype)init{
+    if (self = [super init]) {
+
+    }
+    return self;
+}
+
+- (void)receiveData:(NSData *)data{
+//    [self.audioDecoder decodeSBCData:data];
+    
+    NSData *pcmData = [self convertADPCMDataToPCMData:data];
+    if(self.decodeCallBack)self.decodeCallBack(pcmData);
+}
+- (NSData *)convertADPCMDataToPCMData:(NSData *)adpcmData {
+    const unsigned char *adpcmBytes = (const unsigned char *)[adpcmData bytes];
+    NSUInteger adpcmLength = [adpcmData length];
+
+    signed short *ps = (signed short *)malloc(adpcmLength);
+    memcpy(ps, adpcmBytes, adpcmLength);
+
+    signed short *pd = (signed short *)malloc(adpcmLength * 4);
+
+    adpcm_to_pcm(ps, pd, adpcmLength / sizeof(signed short)*2);
+
+    NSData *pcmData = [NSData dataWithBytes:pd length:adpcmLength * 4];
+
+    free(ps);
+    free(pd);
+
+    return pcmData;
+}
+
+@end
diff --git a/MouseSdk/AiMouseSdk/TlwMouseSdk/TlwConnectManager.h b/MouseSdk/AiMouseSdk/TlwMouseSdk/TlwConnectManager.h
new file mode 100644 (file)
index 0000000..8a76ef8
--- /dev/null
@@ -0,0 +1,35 @@
+//
+//  TlwConnectManager.h
+//  AiMouseSdk
+//
+//  Created by incar on 2024/5/28.
+//
+
+#import <Foundation/Foundation.h>
+
+NS_ASSUME_NONNULL_BEGIN
+@class TlwConnectManager;
+@protocol TlwConnectManagerDelegate <NSObject>
+
+- (void)TlwConnectManager:(TlwConnectManager *)manager connectStateDidChange:(BOOL)connected;
+
+- (void)TlwConnectManagerReSendData:(TlwConnectManager *)manager;
+
+- (void)TlwConnectManager:(TlwConnectManager *)manager outputLog:(NSString *)log;
+
+@end
+@interface TlwConnectManager : NSObject
+@property (nonatomic, weak) id<TlwConnectManagerDelegate> delegate;
+
+- (void)start;
+
+//重置
+- (void)resetTimeOut;
+
+/// 增加超时
+- (void)addTimeOut;
+
+- (void)disConnected;
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/MouseSdk/AiMouseSdk/TlwMouseSdk/TlwConnectManager.m b/MouseSdk/AiMouseSdk/TlwMouseSdk/TlwConnectManager.m
new file mode 100644 (file)
index 0000000..e81e7ce
--- /dev/null
@@ -0,0 +1,94 @@
+//
+//  TlwConnectManager.m
+//  AiMouseSdk
+//
+//  Created by incar on 2024/5/28.
+//
+
+#import "TlwConnectManager.h"
+
+@interface TlwConnectManager()
+@property (nonatomic, assign) NSInteger currentValue;//当前计数
+@property (nonatomic, assign) NSInteger timeOut;//超时次数
+@property (nonatomic, strong) NSTimer *timer;
+@property (nonatomic, assign) int connecteState;//连接状态0未知,1连上,2断开连接
+@end
+
+@implementation TlwConnectManager
+-(instancetype)init{
+    if (self = [super init]) {
+
+    }
+    return self;
+}
+
+- (void)start{
+    self.timeOut = 0;
+    self.connecteState = 0;
+    
+    [self addSendTimer];
+}
+
+- (void)resetTimeOut{
+    self.timeOut = 0;
+    self.currentValue = 0;
+    
+    if (self.connecteState != 1) {
+        [self showLog:@"连上设备。。。"];
+        [self.delegate TlwConnectManager:self connectStateDidChange:true];
+    }
+    self.connecteState = 1;
+}
+
+- (void)addTimeOut{
+    self.timeOut++;
+    
+    if (self.timeOut >= 3) {
+        if (self.connecteState != 2) {
+            [self showLog:@"设备断开连接!!!"];
+            [self.delegate TlwConnectManager:self connectStateDidChange:false];
+        }
+        self.connecteState = 2;
+    }
+}
+
+- (void)disConnected{
+    [self showLog:@"设备断开连接!!!"];
+    [self.delegate TlwConnectManager:self connectStateDidChange:false];
+    self.connecteState = 2;
+}
+#pragma mark - timer
+- (void)addSendTimer{
+    dispatch_async(dispatch_queue_create("timeoutQueue", DISPATCH_QUEUE_SERIAL), ^{
+        self.timer = [NSTimer timerWithTimeInterval:1000 target:self selector:@selector(timerAction) userInfo:nil repeats:true];
+        [[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];
+        [[NSRunLoop currentRunLoop] run];
+    });
+//    self.timer = [NSTimer timerWithTimeInterval:1 target:self selector:@selector(timerAction) userInfo:nil repeats:true];
+//    [[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];
+}
+
+//- (void)removeSendTimer{
+//    if (self.timer && [self.timer isValid]) {
+//        [self.timer invalidate];
+//    }
+//}
+
+- (void)timerAction{
+    self.currentValue++;
+//    [self showLog:[NSString stringWithFormat:@"time = %ld",self.currentValue]];
+    if (self.currentValue >= 3) {
+        self.currentValue = 0;
+        if (self.delegate && [self.delegate respondsToSelector:@selector(TlwConnectManagerReSendData:)]) {
+//            [self showLog:@"检查是否断开连接"];
+            [self.delegate TlwConnectManagerReSendData:self];
+        }
+    }
+}
+
+- (void)showLog:(NSString *)log{
+    if (self.delegate && [self.delegate respondsToSelector:@selector(TlwConnectManager:outputLog:)]) {
+        [self.delegate TlwConnectManager:self outputLog:log];
+    }
+}
+@end
diff --git a/MouseSdk/AiMouseSdk/TlwMouseSdk/TlwMouseSdk.h b/MouseSdk/AiMouseSdk/TlwMouseSdk/TlwMouseSdk.h
new file mode 100644 (file)
index 0000000..98376d5
--- /dev/null
@@ -0,0 +1,102 @@
+//
+//  AiMouseSdk.h
+//  AiMouseSdk
+//
+//  Created by incar on 2024/4/23.
+//
+
+#import <Foundation/Foundation.h>
+
+@interface TlwDeviceInfo : NSObject
+@property (nonatomic, assign) int DPI;/// DPI值 数值1 ~ 4 (分别对应 800 1200 1800 2400)
+@property (nonatomic, assign) int version;///软件版本
+@property (nonatomic, assign) int batteryState;/// 电池状态
+@property (nonatomic, assign) int batteryValue;/// 电池百分百电量,0-100
+@end
+
+/// 日志等级
+typedef NS_ENUM(NSInteger,TlwMouseLogLevel) {
+    TlwMouseLogLevel_none = 0,
+    TlwMouseLogLevel_error,
+    TlwMouseLogLevel_info,
+};
+
+typedef NS_ENUM(NSInteger,TlwConnectType) {
+    TlwConnectType_bluetooth = 0,///蓝牙
+    TlwConnectType_hid,///hid
+};
+
+@protocol TlwMouseDelegate <NSObject>
+
+/// 设备连接状态
+/// - Parameter connected: 是否连接
+- (void)aiMouseConnectStateDidChange:(BOOL)connected;
+
+/// 设备信息数据回调,需要将此数据通过设备信息特征转发给蓝牙设备
+/// - Parameter data: 数据
+- (void)tlwMouseSendDeviceInfoData:(NSData *)data;
+
+/// 鼠标按键指令回调
+/// - Parameter data: 数据。
+/// 前  单击0x06, 按下0x13(开始录音)抬起0x14(结束录音)
+/// 中  单击0x03, 按下0x11(开始录音)抬起0x12(结束录音)
+/// 后  第一次单击0x01, 第二次单击0x02(结束录音) , 按下0x09(开始录音)抬起0x10(结束录音)
+/// 其他未定义
+- (void)tlwMouseSendCmdData:(NSData *)data;
+
+/// 录音回调
+/// - Parameter data: pcm格式语音数据
+- (void)tlwMouseSendRecordData:(NSData *)data;
+
+@optional
+/// 输出sdk日志
+/// - Parameter log: 日志
+- (void)aiMouseAddLog:(NSString *)log;
+
+@end
+
+@interface TlwMouse : NSObject
+@property (nonatomic, weak) id<TlwMouseDelegate> delegate;/// 代理
+@property (nonatomic, readonly) TlwConnectType connectType;//连接类型
+@property (nonatomic, readonly) BOOL connected;//是否已连接
+
+/// 单利
++(TlwMouse *)shared;
+
+/// 设置日志等级
+/// - Parameter level: 等级
+- (void)setLogLevel:(TlwMouseLogLevel)level;
+
+/// 断开连接
+- (void)didDisconnect;
+
+/// 切换连接类型
+/// - Parameter type: 连接类型
+- (void)switchConnect:(TlwConnectType)type;
+
+/// 传输业务数据给sdk
+/// - Parameter data:ai特征传过来的数据
+- (void)sendAiDataToSdk:(NSData *)data;
+
+///  传输设备信息数据给sdk
+/// - Parameter data:设备信息特征传过来的数据
+- (void)sendDeviceDataToSdk:(NSData *)data;
+
+/// 获取设备信息
+/// - Parameter completation: 如果获取成功,则deviceInfo有值,否则为nil
+- (void)getDeviceInfo:(void (^)(TlwDeviceInfo *deviceInfo,NSError *error))complete;
+
+/// 设置DPI
+/// - Parameters:
+///   - value: 数值1 ~ 4 (分别对应 800 1200 1800 2400)
+///   - finished: 设置结果回调
+- (void)setDPI:(uint8_t)value completation:(void (^)(BOOL isSuccess,NSError *error))finished;
+
+/// 打开录音
+/// - finished: 执行结果回调
+- (void)openRecord:(void (^)(BOOL isSuccess,NSError *error))finished;
+
+/// 关闭录音
+/// - finished: 执行结果回调
+- (void)closeRecord:(void (^)(BOOL isSuccess,NSError *error))finished;
+@end
diff --git a/MouseSdk/AiMouseSdk/TlwMouseSdk/TlwMouseSdk.m b/MouseSdk/AiMouseSdk/TlwMouseSdk/TlwMouseSdk.m
new file mode 100644 (file)
index 0000000..17654f5
--- /dev/null
@@ -0,0 +1,492 @@
+//
+//  AiMouseSdk.m
+//  AiMouseSdk
+//
+//  Created by incar on 2024/4/23.
+//
+
+#import "TlwMouseSdk.h"
+#import "TlwAudioManager.h"
+#import "TlwTools.h"
+#import "TlwRecordManager.h"
+#import "TlwConnectManager.h"
+
+@interface TlwDeviceInfo()
+@end
+@implementation TlwDeviceInfo
+-(NSString *)description{
+    NSString *info = [NSString stringWithFormat:@"\n version = %d\n DPI = %d\n batteryState = %d\n batteryValue = %d\n",self.version,self.DPI,self.batteryState,self.batteryValue];
+    return info;
+}
+@end
+
+@interface TlwMouse()<TlwConnectManagerDelegate>
+
+@property (nonatomic, assign) TlwMouseLogLevel logLevel;
+@property (nonatomic, strong) TlwAudioManager *audioManager;
+@property (nonatomic, strong) TlwRecordManager *recordManager;
+@property (nonatomic, strong) TlwConnectManager *connectManager;
+
+@property (nonatomic,strong)void (^openRecord)(BOOL isSuccess,NSError *error);
+@property (nonatomic,strong)void (^closeRecord)(BOOL isSuccess,NSError *error);
+@property (nonatomic,strong)void (^setDpi)(BOOL isSuccess,NSError *error);
+@property (nonatomic,strong)void (^getMouseInfo)(TlwDeviceInfo *deviceInfo,NSError *error);
+@end
+
+@implementation TlwMouse
++(TlwMouse *)shared{
+    static TlwMouse *manager = nil;
+    static dispatch_once_t oncePredicate;
+    dispatch_once(&oncePredicate, ^{
+        manager = [[TlwMouse alloc]init];
+    });
+    return manager;
+}
+
+-(instancetype)init{
+    if (self = [super init]) {
+        _logLevel = TlwMouseLogLevel_none;
+        _connectType = TlwConnectType_bluetooth;
+        [self.connectManager start];
+        NSLog(@"[AiMouseSdk]%@",[NSString stringWithFormat:@"AiMouseSdk_v%@",self.version]);
+    }
+    return self;
+}
+
+-(NSString *)version{
+    return @"1.1.0";
+}
+
+-(TlwAudioManager *)audioManager{
+    if (!_audioManager) {
+        _audioManager = [[TlwAudioManager alloc] init];
+        __weak typeof(self) ws = self;
+        _audioManager.decodeCallBack = ^(NSData * _Nonnull data) {
+            [ws sendRecordData:data];
+        };
+        _audioManager.outputLog = ^(NSString * _Nonnull log) {
+            [ws showLog:log];
+        };
+    }
+    return _audioManager;
+}
+
+- (TlwRecordManager *)recordManager{
+    if (!_recordManager) {
+        _recordManager = [[TlwRecordManager alloc] init];
+        __weak typeof(self) ws = self;
+        _recordManager.didRecordData = ^(NSData * _Nonnull data) {
+            [ws showLog:[NSString stringWithFormat:@"%@",data]];
+            [ws sendRecordData:data];
+        };
+    }
+    return _recordManager;
+}
+
+-(TlwConnectManager *)connectManager{
+    if (!_connectManager) {
+        _connectManager = [[TlwConnectManager alloc] init];
+        _connectManager.delegate = self;
+    }
+    return _connectManager;
+}
+
+#pragma mark - pub Api
+- (void)setLogLevel:(TlwMouseLogLevel)level{
+    _logLevel = level;
+}
+- (void)didDisconnect{
+    [self showLog:@"系统断开连接"];
+    [self stopRecord];
+    [self.connectManager disConnected];
+}
+
+- (void)switchConnect:(TlwConnectType)type{
+    if(type != _connectType && self.connected){
+        [self.connectManager disConnected];
+    }
+    _connectType = type;
+    [self showLog:type == TlwConnectType_bluetooth?@"sdk切换到蓝牙模式":@"sdk切换到usb模式"];
+    
+
+    if (type == TlwConnectType_hid) {
+        [self.recordManager checkPermissions];
+    }
+}
+
+- (void)sendAiDataToSdk:(NSData *)data{
+    [self.connectManager resetTimeOut];
+    
+    [self showLog:[NSString stringWithFormat:@"[receive][Ai] %@",data]];
+    if(self.connectType == TlwConnectType_hid){
+////        0AFFFF12BC32
+//        if(data.length == 6){
+//            NSData *subData = [data subdataWithRange:NSMakeRange(5, 1)];
+//            [self sendCmdData:subData];
+//            uint8 cmd;
+//            [subData getBytes:&cmd length:1];
+////            [self showLog:[NSString stringWithFormat:@"cmd = %X",cmdInt]];
+//            if(cmd == 0x31 || cmd == 0x21 || cmd == 0x25 ||cmd == 0x27){
+//                self.recordManager.startCmd = subData;
+//                [self startRecord];
+//            }
+//            else if(cmd == 0x32 || cmd == 0x22 || cmd == 0x26 ||cmd == 0x28){
+//                [self stopRecord:false];
+//            }
+//        }
+//        else{
+//            [self showLog:@"未实现指令"];
+//        }
+    }
+    else if (self.connectType == TlwConnectType_bluetooth){
+        [self bleReceiveAiData:data];
+    }
+}
+
+-(void)sendDeviceDataToSdk:(NSData *)data{
+    [self showLog:[NSString stringWithFormat:@"[receive][info] %@",data]];
+    if(self.connectType == TlwConnectType_hid){
+        [self hidReceiveDeviceData:data];
+    }
+    else if (self.connectType == TlwConnectType_bluetooth){
+        [self bleReceiveDeviceData:data];
+    }
+}
+- (void)openRecord:(void (^)(BOOL isSuccess,NSError *error))finished{
+    if(self.connectType == TlwConnectType_hid){
+        self.openRecord = finished;
+        uint8_t cmd = 0x03;
+        uint8_t len = 0x00;
+        uint8_t tail = 0xEF;
+        NSMutableData *mData = NSMutableData.data;
+        [mData appendData:[self getHidHeader]];
+        [mData appendBytes:&cmd length:1];
+        [mData appendBytes:&len length:1];
+        [mData appendBytes:&tail length:1];
+        [self sendToDevice:mData];
+    }
+    else if (self.connectType == TlwConnectType_bluetooth){
+        self.openRecord = finished;
+        uint8_t cmd = 0x03;
+        uint8_t dir = 0x01;
+        uint8_t len = 0x00;
+        NSMutableData *mData = NSMutableData.data;
+        [mData appendBytes:&cmd length:1];
+        [mData appendBytes:&dir length:1];
+        [mData appendBytes:&len length:1];
+        [self sendToDevice:mData];
+    }
+}
+- (void)closeRecord:(void (^)(BOOL isSuccess,NSError *error))finished{
+    if(self.connectType == TlwConnectType_hid){
+        self.closeRecord = finished;
+        uint8_t cmd = 0x04;
+        uint8_t len = 0x00;
+        uint8_t tail = 0xEF;
+        NSMutableData *mData = NSMutableData.data;
+        [mData appendData:[self getHidHeader]];
+        [mData appendBytes:&cmd length:1];
+        [mData appendBytes:&len length:1];
+        [mData appendBytes:&tail length:1];
+        [self sendToDevice:mData];
+    }
+    else if (self.connectType == TlwConnectType_bluetooth){
+        self.closeRecord = finished;
+        uint8_t cmd = 0x04;
+        uint8_t dir = 0x01;
+        uint8_t len = 0x00;
+        NSMutableData *mData = NSMutableData.data;
+        [mData appendBytes:&cmd length:1];
+        [mData appendBytes:&dir length:1];
+        [mData appendBytes:&len length:1];
+        [self sendToDevice:mData];
+    }
+
+}
+
+- (void)setDPI:(uint8_t)value completation:(void (^)(BOOL isSuccess,NSError *error))finished{
+    if(self.connectType == TlwConnectType_hid){
+        self.setDpi  = finished;
+        uint8_t type = 0x06;
+        uint8_t len = 0x01;
+        uint8_t tail = 0xEF;
+        NSMutableData *mData = NSMutableData.data;
+        [mData appendData:[self getHidHeader]];
+        [mData appendBytes:&type length:1];
+        [mData appendBytes:&len length:1];
+        [mData appendBytes:&value length:1];
+        [mData appendBytes:&tail length:1];
+        [self sendToDevice:mData];
+    }
+    else if (self.connectType == TlwConnectType_bluetooth){
+        self.setDpi  = finished;
+        uint8_t cmd = 0x06;
+        uint8_t dir = 0x01;
+        uint8_t len = 0x01;
+        NSMutableData *mData = NSMutableData.data;
+        [mData appendBytes:&cmd length:1];
+        [mData appendBytes:&dir length:1];
+        [mData appendBytes:&len length:1];
+        [mData appendBytes:&value length:1];
+        [self sendToDevice:mData];
+    }
+    
+
+}
+- (void)getDeviceInfo:(void (^)(TlwDeviceInfo *deviceInfo,NSError *error))finished{
+    if(self.connectType == TlwConnectType_hid){
+        self.getMouseInfo = finished;
+//        0x05fff1fec09f0114ef
+        uint8_t cmd = 0x0c;
+        uint8_t len = 0x00;
+        uint8_t tail = 0xEF;
+        
+        NSMutableData *mData = NSMutableData.data;
+        [mData appendData:[self getHidHeader]];
+        [mData appendBytes:&cmd length:1];
+        [mData appendBytes:&len length:1];
+        [mData appendBytes:&tail length:1];
+        [self sendToDevice:mData];
+    }
+    else if (self.connectType == TlwConnectType_bluetooth){
+        self.getMouseInfo = finished;
+        uint8_t cmd = 0x0c;
+        uint8_t dir = 0x01;
+        uint8_t len = 0x00;
+        NSMutableData *mData = NSMutableData.data;
+        [mData appendBytes:&cmd length:1];
+        [mData appendBytes:&dir length:1];
+        [mData appendBytes:&len length:1];
+        [self sendToDevice:mData];
+    }
+}
+
+#pragma mark - ble
+- (void)bleReceiveAiData:(NSData *)data{
+    [self.connectManager resetTimeOut];
+    [self.audioManager receiveData:data];
+}
+
+-(void)bleReceiveDeviceData:(NSData *)data{
+    NSData *typeData = [data subdataWithRange:NSMakeRange(0, 1)];
+    uint8 type;
+    [typeData getBytes:&type length:1];
+    
+    NSData *stateData = [data subdataWithRange:NSMakeRange(1, 1)];
+    uint8 state;
+    [stateData getBytes:&state length:1];
+    BOOL success = state == 0x02;
+    
+    NSData *lenData = [data subdataWithRange:NSMakeRange(2, 1)];
+    uint8 len;
+    [lenData getBytes:&len length:1];
+    
+    switch (type) {
+        case 0x6A:
+            //语音开启回复
+            [self showLog:[NSString stringWithFormat:@"语音开启回复 %@",@(success)]];
+            self.openRecord(success, nil);
+            break;
+        case 0x6B:
+            //语音停止回复
+            [self showLog:[NSString stringWithFormat:@"语音停止回复 %@",@(success)]];
+            self.closeRecord(success, nil);
+            break;
+        case 0x96:
+            //设置DPI回复
+            [self showLog:[NSString stringWithFormat:@"设置DPI回复 %@",@(success)]];
+            self.setDpi(success, nil);
+            break;
+        case 0x9c:
+        { //鼠标状态查询回复
+            uint16_t version;
+            [[data subdataWithRange:NSMakeRange(3, 2)] getBytes:&version length:2];
+            
+            uint16_t DPI;
+            [[data subdataWithRange:NSMakeRange(5, 2)] getBytes:&DPI length:2];
+            
+            uint16_t batteryState;
+            [[data subdataWithRange:NSMakeRange(7, 1)] getBytes:&batteryState length:1];
+            
+            uint16_t batteryValue;
+            [[data subdataWithRange:NSMakeRange(8, 1)] getBytes:&batteryValue length:1];
+            
+            TlwDeviceInfo *info = [[TlwDeviceInfo alloc] init];
+            info.version = version;
+            info.DPI = DPI;
+            info.batteryState = batteryState;
+            info.batteryValue = batteryValue;
+            [self showLog:info.description];
+            self.getMouseInfo(info, nil);
+        }
+            break;
+        case 0x9f:
+        {
+            //鼠标按键上报
+            NSData *subData = [data subdataWithRange:NSMakeRange(3, 1)];
+            [self sendCmdData:subData];
+        }
+            break;
+        default:
+            break;
+    }
+}
+
+#pragma mark - hid
+- (void)hidReceiveDeviceData:(NSData *)data{
+//    0x05fff1fec09f0114ef
+    uint8 type;
+    [[data subdataWithRange:NSMakeRange(5, 1)] getBytes:&type length:1];
+    
+    NSData *lenData = [data subdataWithRange:NSMakeRange(6, 1)];
+    uint8 len;
+    [lenData getBytes:&len length:1];
+    
+    switch (type) {
+        case 0x6A:
+        {
+            //语音开启回复
+            uint8 state;
+            [[data subdataWithRange:NSMakeRange(6, 1)] getBytes:&state length:1];
+            BOOL success = state == 0x02;
+            [self showLog:[NSString stringWithFormat:@"语音开启回复 %@",@(success)]];
+            self.openRecord(success, nil);
+            [self startRecord];
+            break;
+        }
+
+        case 0x6B:
+        {
+            //语音停止回复
+            uint8 state;
+            [[data subdataWithRange:NSMakeRange(6, 1)] getBytes:&state length:1];
+            BOOL success = state == 0x02;
+
+            [self showLog:[NSString stringWithFormat:@"语音停止回复 %@",@(success)]];
+            [self stopRecord];
+            self.closeRecord(success, nil);
+            break;
+        }
+        case 0x96:
+        {
+            //设置DPI回复
+            uint8 state;
+            [[data subdataWithRange:NSMakeRange(6, 1)] getBytes:&state length:1];
+            BOOL success = state == 0x02;
+
+            [self showLog:[NSString stringWithFormat:@"设置DPI回复 %@",@(success)]];
+            self.setDpi(success, nil);
+            break;
+        }
+        case 0x9c:
+        { //鼠标状态查询回复
+            uint16_t version;
+            [[data subdataWithRange:NSMakeRange(3, 2)] getBytes:&version length:2];
+            
+            uint16_t DPI;
+            [[data subdataWithRange:NSMakeRange(5, 2)] getBytes:&DPI length:2];
+            
+            uint16_t batteryState;
+            [[data subdataWithRange:NSMakeRange(7, 1)] getBytes:&batteryState length:1];
+            
+            uint16_t batteryValue;
+            [[data subdataWithRange:NSMakeRange(8, 1)] getBytes:&batteryValue length:1];
+            
+            TlwDeviceInfo *info = [[TlwDeviceInfo alloc] init];
+            info.version = version;
+            info.DPI = DPI;
+            info.batteryState = batteryState;
+            info.batteryValue = batteryValue;
+            [self showLog:info.description];
+            self.getMouseInfo(info, nil);
+        }
+            break;
+        case 0x9f:
+        {
+            NSData *subData = [data subdataWithRange:NSMakeRange(7, 1)];
+            [self sendCmdData:subData];
+            
+            uint8 cmd;
+            [subData getBytes:&cmd length:1];
+
+            if(cmd == 0x13 || cmd == 0x11 || cmd == 0x01 ||cmd == 0x09){
+                self.recordManager.startCmd = subData;
+                [self startRecord];
+            }
+            else if(cmd == 0x14 || cmd == 0x12 || cmd == 0x02 ||cmd == 0x10){
+                [self stopRecord];
+            }
+        }
+            break;
+        default:
+            break;
+    }
+}
+- (NSData *)getHidHeader{
+    uint8_t head1 = 0x05;
+    uint16_t head2 = 0xFFF1;
+    uint8_t head3 = 0xfe;
+    uint8_t head4 = 0x00;
+
+    NSMutableData *mData = NSMutableData.data;
+    [mData appendBytes:&head1 length:1];
+    [mData appendBytes:&head2 length:2];
+    [mData appendBytes:&head3 length:1];
+    [mData appendBytes:&head4 length:1];
+    
+    return mData;
+}
+#pragma mark - other
+- (void)sendRecordData:(NSData *)data{
+    [self showLog:[NSString stringWithFormat:@"[send][audio] %@",data]];
+    if (self.delegate)  [self.delegate tlwMouseSendRecordData:data];
+}
+
+- (void)sendCmdData:(NSData *)data{
+    [self showLog:[NSString stringWithFormat:@"[send] %@",data]];
+    if (self.delegate) [self.delegate tlwMouseSendCmdData:data];
+}
+
+- (void)showLog:(NSString *)log{
+    if (self.delegate && [self.delegate respondsToSelector:@selector(aiMouseAddLog:)]) {
+        [self.delegate aiMouseAddLog:[NSString stringWithFormat:@"[AiMouseSdk]%@",log]];
+    }
+    if(self.logLevel == TlwMouseLogLevel_info){
+        NSLog(@"[AiMouseSdk]%@",log);
+    }
+}
+
+
+- (void)sendToDevice:(NSData *)data{
+    [self showLog:[NSString stringWithFormat:@"[send][info] %@",data]];
+    [self.delegate tlwMouseSendDeviceInfoData:data];
+}
+#pragma mark - deviceInfo
+- (void)startRecord{
+    [self.recordManager start];
+}
+
+- (void)stopRecord{
+    if (self.recordManager.recording ) {
+        [self.recordManager stop];
+    }
+}
+
+#pragma mark - TlwConnectManagerDelegate
+-(void)TlwConnectManager:(TlwConnectManager *)manager connectStateDidChange:(BOOL)connected{
+    _connected = connected;
+    if([self.delegate respondsToSelector:@selector(aiMouseConnectStateDidChange:)]) [self.delegate aiMouseConnectStateDidChange:connected];
+}
+-(void)TlwConnectManagerReSendData:(TlwConnectManager *)manager{
+//    __weak typeof(self) ws = self;
+//    [self.deviceInfoManager getVersion:^(NSString * _Nonnull version, NSError * _Nonnull error) {
+//        if (error.code == AiMouseErrorCode_timeOut) {
+//            [ws.connectManager addTimeOut];
+//        }
+//    }];
+}
+-(void)TlwConnectManager:(TlwConnectManager *)manager outputLog:(NSString *)log{
+    [self showLog:log];
+}
+@end
+
diff --git a/MouseSdk/AiMouseSdk/TlwMouseSdk/TlwRecordManager.h b/MouseSdk/AiMouseSdk/TlwMouseSdk/TlwRecordManager.h
new file mode 100644 (file)
index 0000000..3d499c6
--- /dev/null
@@ -0,0 +1,25 @@
+//
+//  TlwRecordManager.h
+//  AudioRecorder
+//
+//  Created by incar on 2024/3/29.
+//
+
+#import <Foundation/Foundation.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface TlwRecordManager : NSObject
+@property (nonatomic,assign)BOOL recording;
+@property (nonatomic,strong)NSData *startCmd;
+@property (nonatomic,readonly)NSData *stopCmd;
+@property (nonatomic,strong)void (^didRecordData)(NSData *data);
+
+- (void)checkPermissions;
+
+- (void)start;
+
+- (void)stop;
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/MouseSdk/AiMouseSdk/TlwMouseSdk/TlwRecordManager.m b/MouseSdk/AiMouseSdk/TlwMouseSdk/TlwRecordManager.m
new file mode 100644 (file)
index 0000000..7b3bf47
--- /dev/null
@@ -0,0 +1,194 @@
+//
+//  TlwRecordManager.m
+//  AudioRecorder
+//
+//  Created by incar on 2024/3/29.
+//
+
+#import "TlwRecordManager.h"
+#import <AVFoundation/AVFoundation.h>
+
+@interface TlwRecordManager()
+@property (nonatomic,assign)BOOL isAllowRecord;
+@property (nonatomic,strong)AVAudioEngine *audioEngine;
+@property (nonatomic,strong)AVAudioFile *outputFile;
+
+@end
+
+@implementation TlwRecordManager
+//+(TlwRecordManager *)shared{
+//    static TlwRecordManager *manager = nil;
+//    static dispatch_once_t oncePredicate;
+//    dispatch_once(&oncePredicate, ^{
+//        manager = [[TlwRecordManager alloc]init];
+//    });
+//    return manager;
+//}
+
+
+-(instancetype)init{
+    if (self = [super init ]) {
+        self.isAllowRecord = false;
+//        [self checkPermissions];
+    }
+    return self;
+}
+
+- (void)start{
+    if (self.isAllowRecord == false) {
+        return;
+    }
+    //    let audioSession = AVAudioSession.sharedInstance()
+    //    do {
+    //      try audioSession.setCategory(.playAndRecord, options: .defaultToSpeaker)
+    //      try audioSession.setActive(true, options: .notifyOthersOnDeactivation)
+    //    catch let error as NSError {
+    //      print("ERROR:", error)
+    //    }
+    self.recording = true;
+    
+    self.audioEngine = [[AVAudioEngine alloc] init];
+    AVAudioInputNode *input = self.audioEngine.inputNode;
+    AVAudioNodeBus bus = 0;
+    AVAudioFormat *inputFormat = [input inputFormatForBus:bus];
+    //    NSString *fullPath = [NSString stringWithFormat:@"%@1.pcm",NSTemporaryDirectory()];
+    //    NSURL *fileURL = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask].firstObject URLByAppendingPathComponent:@"record.caf"];
+    //    [self showLog:fileURL.absoluteString];
+    //    NSError *err0;
+    //    self.outputFile = [[AVAudioFile alloc] initForWriting:fileURL settings:inputFormat.settings commonFormat:inputFormat.commonFormat interleaved:inputFormat.isInterleaved error:&err0];
+    //    if (err0) {
+    //        [self showLog:[NSString stringWithFormat:@"设置写入路径 %@", err0.description]];
+    //    }
+    //    else{
+    //        [self showLog:@"写入路径设置成功"];
+    //    }
+    //    self.recordData = [[NSMutableData alloc] init];
+    __weak typeof(self) ws = self;
+    [input installTapOnBus:bus bufferSize:32 format:inputFormat block:^(AVAudioPCMBuffer * _Nonnull buffer, AVAudioTime * _Nonnull when) {
+        NSData *data = [ws pcm_downsample:buffer];
+        if (ws.didRecordData) {
+            ws.didRecordData(data);
+        }
+//        NSLog(@"%@",data);
+//        NSError *err0;
+        //        [ws.outputFile writeFromBuffer:buffer error:&err0];
+        //        if (err0) {
+        //            [self showLog:[NSString stringWithFormat:@"写入失败 %@", err0.description]];
+        //        }
+        //        else{
+        //            [self showLog:@"写入成功"];
+        //        }
+    }];
+    
+    NSError *err1;
+    [self.audioEngine prepare];
+    [self.audioEngine startAndReturnError:&err1];
+    if (err1) {
+        [self showLog:[NSString stringWithFormat:@"启动录音失败 %@", err1.description]];
+    }
+    else{
+        [self showLog:@"启动成功"];
+    }
+}
+
+- (void)stop{
+    [self.audioEngine stop];
+    self.recording = false;
+    //    [self finishRecored:self.recordData];
+}
+
+- (void)checkPermissions{
+    switch ([AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeAudio]) {
+        case AVAuthorizationStatusAuthorized:
+            self.isAllowRecord = true;
+            [self showLog:@"录音已经授权"];
+            break;
+        case AVAuthorizationStatusNotDetermined:
+        {
+            [self showLog:@"录音未授权"];
+            [AVCaptureDevice requestAccessForMediaType:AVMediaTypeAudio completionHandler:^(BOOL granted) {
+                if (granted) {
+                    self.isAllowRecord = true;
+                    [self showLog:@"录音已经授权"];
+                }
+                else{
+                    [self showLog:@"已拒绝"];
+                }
+            }];
+        }
+            break;
+        case AVAuthorizationStatusDenied:
+            [self showLog:@"已拒绝"];
+            [self openSettings];
+            break;
+        default:
+            [self showLog:@"已拒绝"];
+            [self openSettings];
+            break;
+    }
+}
+
+- (void)openSettings{
+//    NSURL *url = [NSURL URLWithString:@""];
+}
+
+- (void)showLog:(NSString *)log{
+//    NSLog(@"%@",log);
+}
+
+//将录音数据保存
+- (void)finishRecored:(NSData *)data{
+    NSString *doc = NSTemporaryDirectory();
+    NSString *fullPath = [NSString stringWithFormat:@"%@1.pcm",doc];
+    NSError *err;
+    [data writeToFile:fullPath options:NSDataWritingAtomic error:&err];
+    if (err) {
+        [self showLog:[NSString stringWithFormat:@"录音保存失败 %@",err.description]];
+    }
+    else{
+        [self showLog:[NSString stringWithFormat:@"录音保存成功 path = %@",fullPath]];
+    }
+}
+
+-(NSData*) pcm_downsample:(AVAudioPCMBuffer*)buffer {
+    //位深转换 float32 转 int16_t
+    NSData* pData = [[NSData alloc] initWithBytes:buffer.floatChannelData[0] length:buffer.frameLength * 4];
+    Byte*dataByte = (Byte*)[pData bytes];
+    NSUInteger srcSize = pData.length/4;
+    Float32 vf32[srcSize];
+    memcpy(vf32, &dataByte[0], pData.length);
+    int16_t bArr[srcSize];
+    int r;
+    for(size_t i =0; i < srcSize; ++i) {
+        float x = vf32[i];
+        float c;
+        c = ((x < -1) ? -1: ((x >1) ?1: x));
+        c = c +1;
+        r = (int)(c *32767.5f);
+        r = r -32768;
+        bArr[i] = (short)r;
+    }
+    //码率转换 44100 转 16000
+    uint32_t last_pos = (uint32_t)(srcSize -1);
+    uint32_t dstSize = (srcSize/44100.0)*16000;
+    int16_t bArr16[dstSize];
+    for(uint32_t idx =0; idx < dstSize; idx++) {
+        float index = ((float) idx *44100.0) / (16000.0);
+        uint32_t p1 = (uint32_t) index;
+        float coef = index - p1;
+        uint32_t p2 = (p1 == last_pos) ? last_pos : p1 +1;
+        bArr16[idx] = (int16_t) ((1.0f- coef) * bArr[p1] + coef * bArr[p2]);
+    }
+    NSMutableData *data1 = [[NSMutableData alloc] init];
+    for(NSUInteger i =0; i < dstSize; i++) {
+        unsigned char valChar[2];
+        valChar[0] =0xff& bArr16[i];
+        valChar[1] = (0xff00& bArr16[i]) >>8;
+        [data1 appendBytes:valChar length:2];
+    }
+    return data1;
+}
+
+
+
+@end
diff --git a/MouseSdk/AiMouseSdk/TlwMouseSdk/TlwTools.h b/MouseSdk/AiMouseSdk/TlwMouseSdk/TlwTools.h
new file mode 100644 (file)
index 0000000..4eb7bb4
--- /dev/null
@@ -0,0 +1,44 @@
+//
+//  TlwTools.h
+//  AiMouse
+//
+//  Created by incar on 2024/2/28.
+//
+
+#import <Foundation/Foundation.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface TlwTools : NSObject
++ (NSArray *)stringToArray:(NSString *)jsonString;
++ (NSString *)arrayToString:(NSArray *)arr;
+
++ (NSDictionary *)stringToDictionary:(NSString *)jsonString;
++ (NSString *)dictionaryToString:(NSDictionary *)dic;
+
++ (NSDictionary *)dataToDictionary:(NSData *)jsonData;
++ (NSData *)dictionaryToData:(NSDictionary *)dicJson;
+
+
++ (NSString *)dataToHexString:(NSData *)data;
+
++ (NSString *)dataToHexString_placeholder:(NSData *)data;
+
++ (NSData *)hexStringToData:(NSString *)hexStr ;
+
++ (NSString *)dataToUtf8String:(NSData *)data;
+
++ (NSString *)stringTomd5:(NSString *)str;
+
++ (NSString *)stringToHmacSha1:(NSString*)key text:(NSString*)text;
+
++ (NSString *)MD5ForUpper32:(NSString *)str;
+
++ (NSData *)dataWithHexString:(NSString *)hexStr;
+
++(NSString *)randomString:(NSInteger)length;
+
++(NSString *)getCurrentTime;
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/MouseSdk/AiMouseSdk/TlwMouseSdk/TlwTools.m b/MouseSdk/AiMouseSdk/TlwMouseSdk/TlwTools.m
new file mode 100644 (file)
index 0000000..be8f60a
--- /dev/null
@@ -0,0 +1,226 @@
+//
+//  TlwTools.m
+//  AiMouse
+//
+//  Created by incar on 2024/2/28.
+//
+
+#import "TlwTools.h"
+#import <CommonCrypto/CommonCryptor.h>
+#include <CommonCrypto/CommonDigest.h>
+#include <CommonCrypto/CommonHMAC.h>
+
+@implementation TlwTools
++ (NSArray *)stringToArray:(NSString *)jsonString{
+    NSData *jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
+    NSError *error;
+    NSArray *arr = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:&error];
+    return arr;
+}
++ (NSString *)arrayToString:(NSArray *)arr{
+    NSError *error;
+    NSData *jsonData = [NSJSONSerialization dataWithJSONObject:arr options:NSJSONWritingPrettyPrinted error:&error];
+    NSString * string = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
+    return  string;
+}
++ (NSDictionary *)stringToDictionary:(NSString *)jsonString{
+    NSData *jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
+    if (jsonData == nil) {
+        return nil;
+    }
+    NSError *error;
+    NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:&error];
+    return dict;
+}
++ (NSString *)dictionaryToString:(NSDictionary *)dic{
+    if ([dic isKindOfClass:[NSDictionary class]]) {
+        NSError *error;
+        NSData *jsonData = [NSJSONSerialization dataWithJSONObject:dic options:NSJSONWritingPrettyPrinted error:&error];
+        NSString * string = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
+        return  string;
+    }
+    else{
+        return @"";
+    }
+}
+
++ (NSDictionary *)dataToDictionary:(NSData *)jsonData{
+    if (![jsonData isKindOfClass:[NSData class]] || jsonData.length < 1) {
+        return nil;
+    }
+    id jsonObj = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingAllowFragments error:nil];
+    if (![jsonObj isKindOfClass:[NSDictionary class]]) {
+        return nil;
+    }
+    return [NSDictionary dictionaryWithDictionary:(NSDictionary *)jsonObj];
+}
+
+
++ (NSData *)dictionaryToData:(NSDictionary *)dicJson{
+    if (![dicJson isKindOfClass:[NSDictionary class]]) {
+        return nil;
+    }
+    NSData *jsonData = [NSJSONSerialization dataWithJSONObject:dicJson options:0 error:nil];
+    if (![jsonData isKindOfClass:[NSData class]]) {
+        return nil;
+    }
+    return jsonData;
+}
+
++ (NSString *)dataToHexString:(NSData *)data {
+    NSUInteger length = data.length;
+    NSMutableString *result = [NSMutableString stringWithCapacity:length * 2];
+    const unsigned char *byte = data.bytes;
+    for (int i = 0; i < length; i++, byte++) {
+        [result appendFormat:@"%02X", *byte];
+    }
+    return result;
+}
+
++ (NSString *)dataToHexString_placeholder:(NSData *)data {
+    NSUInteger length = data.length;
+    NSString *placeholder = @" ";
+    NSMutableString *result = [NSMutableString stringWithCapacity:length * 2];
+    const unsigned char *byte = data.bytes;
+    for (int i = 0; i < length; i++, byte++) {
+        [result appendFormat:@"%02X%@", *byte, placeholder];
+    }
+    return result;
+}
+
++ (NSData *)hexStringToData:(NSString *)hexStr {
+    hexStr = [hexStr stringByReplacingOccurrencesOfString:@" " withString:@""];
+    hexStr = [hexStr lowercaseString];
+    NSUInteger len = hexStr.length;
+    if (!len) return nil;
+    unichar *buf = malloc(sizeof(unichar) * len);
+    if (!buf) return nil;
+    [hexStr getCharacters:buf range:NSMakeRange(0, len)];
+    
+    NSMutableData *result = [NSMutableData data];
+    unsigned char bytes;
+    char str[3] = { '\0', '\0', '\0' };
+    int i;
+    for (i = 0; i < len / 2; i++) {
+        str[0] = buf[i * 2];
+        str[1] = buf[i * 2 + 1];
+        bytes = strtol(str, NULL, 16);
+        [result appendBytes:&bytes length:1];
+    }
+    free(buf);
+    return result;
+}
+
++ (NSString *)dataToUtf8String:(NSData *)data {
+    if (data.length > 0) {
+        return [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
+    }
+    return @"";
+}
+
+
++ (NSString *)stringTomd5:(NSString *)str {
+    if (str.length == 0) {
+        return nil;
+    }
+    // 初始化C字符数组
+    const char* original_str = (const char *)[[str dataUsingEncoding:NSUTF8StringEncoding] bytes];
+    // 盛放数字校验的字符数组(长度为16bytes)
+    unsigned char digist[CC_MD5_DIGEST_LENGTH]; //CC_MD5_DIGEST_LENGTH = 16
+    CC_MD5(original_str, (uint)strlen(original_str), digist);
+    //        NSMutableString* outPutStr = [NSMutableString stringWithCapacity:10];
+    NSMutableString* outPutStr = [NSMutableString new];
+    for(int  i =0; i<CC_MD5_DIGEST_LENGTH;i++){
+        [outPutStr appendFormat:@"%02X",digist[i]];//小写x表示输出的是小写MD5,大写X表示输出的是大写MD5
+    }
+    return outPutStr;
+}
+
+//HmacSHA1加密
++ (NSString *)stringToHmacSha1:(NSString*)key text:(NSString*)text{
+    const char *cKey  = [key cStringUsingEncoding:NSUTF8StringEncoding];
+    const char *cData = [text cStringUsingEncoding:NSUTF8StringEncoding];
+    uint8_t cHMAC[CC_SHA1_DIGEST_LENGTH];
+    CCHmac(kCCHmacAlgSHA1, cKey, strlen(cKey), cData, strlen(cData), cHMAC);
+    //NSData *HMAC = [[NSData alloc] initWithBytes:cHMAC length:CC_SHA1_DIGEST_LENGTH];
+    NSString *hash;
+    NSMutableString * output = [NSMutableString stringWithCapacity:CC_SHA1_DIGEST_LENGTH * 2];
+    for(int i = 0; i < CC_SHA1_DIGEST_LENGTH; i++)
+        [output appendFormat:@"%02X", cHMAC[i]];
+    hash = output;
+    return hash;
+}
+
++ (NSString *)MD5ForUpper32:(NSString *)str {
+    const char* input = [str UTF8String];
+    unsigned char result[CC_MD5_DIGEST_LENGTH];
+    CC_MD5(input, (CC_LONG)strlen(input), result);
+    // 'CC_MD5' is deprecated: first deprecated in iOS 13.0 - This function is cryptographically broken and should not be used in security contexts. Clients should migrate to SHA256 (or stronger).
+    NSMutableString *digest = [NSMutableString stringWithCapacity:CC_MD5_DIGEST_LENGTH * 2];
+    for (NSInteger i = 0; i < CC_MD5_DIGEST_LENGTH; i++) {
+        [digest appendFormat:@"%02X", result[i]];
+    }
+    
+    return digest;
+}
+
++ (NSData *)dataWithHexString:(NSString *)hexStr {
+    hexStr = [hexStr stringByReplacingOccurrencesOfString:@" " withString:@""];
+    hexStr = [hexStr lowercaseString];
+    NSUInteger len = hexStr.length;
+    if (!len) return nil;
+    unichar *buf = malloc(sizeof(unichar) * len);
+    if (!buf) return nil;
+    [hexStr getCharacters:buf range:NSMakeRange(0, len)];
+    
+    NSMutableData *result = [NSMutableData data];
+    unsigned char bytes;
+    char str[3] = { '\0', '\0', '\0' };
+    int i;
+    for (i = 0; i < len / 2; i++) {
+        str[0] = buf[i * 2];
+        str[1] = buf[i * 2 + 1];
+        bytes = strtol(str, NULL, 16);
+        [result appendBytes:&bytes length:1];
+    }
+    free(buf);
+    return result;
+}
+
++(NSString *)randomString:(NSInteger)length {
+    char letters[52] = "1234567891234567891234567891234567891234567891234567";
+//    char letters[10] = "012345678901234567890123456789012345678901234567890123456789";
+    char *output = malloc(sizeof(char) * (length + 1));
+    for (int i = 0; i < length; i++) {
+        int index = arc4random_uniform(52);
+        output[i] = letters[index];
+    }
+    output[length] = '\0';
+    
+    return [[NSString alloc] initWithBytesNoCopy:output length:length encoding:NSUTF8StringEncoding freeWhenDone:YES];
+}
+
++(NSString *)getCurrentTime{
+//    @"2024-08-05T10:07:39Z"
+    NSDate *date = [NSDate date];
+//    NSLog(@"当前零时区时间 %@", date);
+    
+    NSDateFormatter *dateFormaterA = [[NSDateFormatter alloc]init];
+    [dateFormaterA setTimeZone:[NSTimeZone timeZoneWithAbbreviation:@"GMT"]]; // 设置时区为格林尼治标准时区
+    [dateFormaterA setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
+    NSString *dateStrA = [dateFormaterA stringFromDate:date];
+    NSString *text1 = [[dateStrA componentsSeparatedByString:@" "] componentsJoinedByString:@"T"];
+    NSString *text2 = [text1 stringByAppendingString:@"Z"];
+//    NSLog(@"%@",text2);
+    
+    
+    // 如果你需要将这个UTC时间显示为格林尼治时间,‌你可以这样做:‌
+//    NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
+//    [dateFormatter setTimeZone:[NSTimeZone timeZoneWithAbbreviation:@"GMT"]]; // 设置时区为格林尼治标准时区
+//    NSString *gmTimeString = [dateFormatter stringFromDate:date]; // 将UTC日期转换为字符串表示形式,‌时区设置为GMT
+//    NSLog(@"%@ == ",gmTimeString);
+    
+    return  text2;
+}
+@end
+
diff --git a/MouseSdk/AiMouseSdk/TlwMouseSdk/adpcm.c b/MouseSdk/AiMouseSdk/TlwMouseSdk/adpcm.c
new file mode 100644 (file)
index 0000000..45fd010
--- /dev/null
@@ -0,0 +1,148 @@
+/********************************************************************************************************
+ * @file       adpcm.c
+ *
+ * @brief      This is the source file for BLE SDK
+ *
+ * @author     BLE GROUP
+ * @date       06,2020
+ *
+ * @par     Copyright (c) 2020, Telink Semiconductor (Shanghai) Co., Ltd. ("TELINK")
+ *          All rights reserved.
+ *
+ *          Redistribution and use in source and binary forms, with or without
+ *          modification, are permitted provided that the following conditions are met:
+ *
+ *              1. Redistributions of source code must retain the above copyright
+ *              notice, this list of conditions and the following disclaimer.
+ *
+ *              2. Unless for usage inside a TELINK integrated circuit, redistributions
+ *              in binary form must reproduce the above copyright notice, this list of
+ *              conditions and the following disclaimer in the documentation and/or other
+ *              materials provided with the distribution.
+ *
+ *              3. Neither the name of TELINK, nor the names of its contributors may be
+ *              used to endorse or promote products derived from this software without
+ *              specific prior written permission.
+ *
+ *              4. This software, with or without modification, must only be used with a
+ *              TELINK integrated circuit. All other usages are subject to written permission
+ *              from TELINK and different commercial license may apply.
+ *
+ *              5. Licensee shall be solely responsible for any claim to the extent arising out of or
+ *              relating to such deletion(s), modification(s) or alteration(s).
+ *
+ *          THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ *          ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ *          WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ *          DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER BE LIABLE FOR ANY
+ *          DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ *          (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ *          LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ *          ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *          (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ *          SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *******************************************************************************************************/
+
+#include       "adpcm.h"
+
+static const signed char idxtbl[] = { -1, -1, -1, -1, 2, 4, 6, 8, -1, -1, -1, -1, 2, 4, 6, 8};
+static const unsigned short steptbl[] = {
+ 7,  8,  9,  10,  11,  12,  13,  14,  16,  17,
+ 19,  21,  23,  25,  28,  31,  34,  37,  41,  45,
+ 50,  55,  60,  66,  73,  80,  88,  97,  107, 118,
+ 130, 143, 157, 173, 190, 209, 230, 253, 279, 307,
+ 337, 371, 408, 449, 494, 544, 598, 658, 724, 796,
+ 876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066,
+ 2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358,
+ 5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899,
+    15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767   };
+
+#define         NUM_OF_ORIG_SAMPLE                             2
+
+void adpcm_to_pcm (signed short *ps, signed short *pd, int len)
+{
+       int i;
+       //byte2,byte1: predict;  byte3: predict_idx; byte4:adpcm data len
+       int predict = ps[0];
+       int predict_idx = ps[1] & 0xff;
+//     int adpcm_len = (ps[1]>>8) & 0xff;
+       unsigned char *pcode = (unsigned char *) (ps + NUM_OF_ORIG_SAMPLE);
+       unsigned char code;
+       code = *pcode ++;
+
+       //byte5- byte128: 124 byte(62 sample) adpcm data
+       for (i=0; i<len; i++) {
+
+               if (1) {
+                       int step = steptbl[predict_idx];
+
+                       int diffq = step >> 3;
+
+                       if (code & 4) {
+                               diffq = diffq + step;
+                       }
+                       step = step >> 1;
+                       if (code & 2) {
+                               diffq = diffq + step;
+                       }
+                       step = step >> 1;
+                       if (code & 1) {
+                               diffq = diffq + step;
+                       }
+
+                       if (code & 8) {
+                               predict = predict - diffq;
+                       }
+                       else {
+                               predict = predict + diffq;
+                       }
+
+                       if (predict > 32767) {
+                               predict = 32767;
+                       }
+                       else if (predict < -32768) {
+                               predict = -32768;
+                       }
+
+                       predict_idx = predict_idx + idxtbl[code & 15];
+
+                       if(predict_idx < 0) {
+                               predict_idx = 0;
+                       }
+                       else if(predict_idx > 88) {
+                               predict_idx = 88;
+                       }
+
+                       if (i&1) {
+                               code = *pcode ++;
+                       }
+                       else {
+                               code = code >> 4;
+                       }
+               }
+
+               if (0 && i < NUM_OF_ORIG_SAMPLE) {
+                       *pd++ = ps[i];
+               }
+               else {
+                       *pd++ = predict;
+               }
+       }
+}
+
+//例子
+#if 0
+#define MIC_ADPCM_FRAME_SIZE           128                                             //一包 adpcm 的数据长度 128 byte
+#define MIC_SHORT_DEC_SIZE                     248
+#define DEC_BUFFER_SIZE                (MIC_SHORT_DEC_SIZE<<2)
+
+unsigned char          abuf_dec_wptr;                                                  // 0~3 , 4个缓冲 ,循环使用                               
+unsigned char          abuf_mic[MIC_ADPCM_FRAME_SIZE * 4];             //压缩的语音数据adpcm缓冲区 ,4个缓冲 ,每个缓冲区 128 byte , 循环使用
+signed short           abuf_dec[DEC_BUFFER_SIZE];                              //解压后存放的数据,注意数组类型是 16 bit,4个缓冲,循环使用
+
+//abuf_mic 源 ,  abuf_dec 目的 , 每次解压 128 byte adpcm 
+adpcm_to_pcm ((signed short *)(abuf_mic + (abuf_dec_wptr & 3) * MIC_ADPCM_FRAME_SIZE), abuf_dec + (abuf_dec_wptr & 3) * MIC_SHORT_DEC_SIZE, MIC_SHORT_DEC_SIZE );
+
+#endif
+
diff --git a/MouseSdk/AiMouseSdk/TlwMouseSdk/adpcm.h b/MouseSdk/AiMouseSdk/TlwMouseSdk/adpcm.h
new file mode 100644 (file)
index 0000000..0cb25a9
--- /dev/null
@@ -0,0 +1,51 @@
+/********************************************************************************************************
+ * @file       adpcm.h
+ *
+ * @brief      This is the header file for BLE SDK
+ *
+ * @author     BLE GROUP
+ * @date       06,2020
+ *
+ * @par     Copyright (c) 2020, Telink Semiconductor (Shanghai) Co., Ltd. ("TELINK")
+ *          All rights reserved.
+ *
+ *          Redistribution and use in source and binary forms, with or without
+ *          modification, are permitted provided that the following conditions are met:
+ *
+ *              1. Redistributions of source code must retain the above copyright
+ *              notice, this list of conditions and the following disclaimer.
+ *
+ *              2. Unless for usage inside a TELINK integrated circuit, redistributions
+ *              in binary form must reproduce the above copyright notice, this list of
+ *              conditions and the following disclaimer in the documentation and/or other
+ *              materials provided with the distribution.
+ *
+ *              3. Neither the name of TELINK, nor the names of its contributors may be
+ *              used to endorse or promote products derived from this software without
+ *              specific prior written permission.
+ *
+ *              4. This software, with or without modification, must only be used with a
+ *              TELINK integrated circuit. All other usages are subject to written permission
+ *              from TELINK and different commercial license may apply.
+ *
+ *              5. Licensee shall be solely responsible for any claim to the extent arising out of or
+ *              relating to such deletion(s), modification(s) or alteration(s).
+ *
+ *          THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ *          ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ *          WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ *          DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER BE LIABLE FOR ANY
+ *          DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ *          (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ *          LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ *          ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *          (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ *          SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *******************************************************************************************************/
+#ifndef ADPCM_H_
+#define ADPCM_H_
+
+void adpcm_to_pcm(signed short *ps, signed short *pd, int len);
+
+#endif /* ADPCM_H_ */
index 64f1164..65632ae 100644 (file)
@@ -3,7 +3,7 @@
        archiveVersion = 1;
        classes = {
        };
-       objectVersion = 56;
+       objectVersion = 55;
        objects = {
 
 /* Begin PBXBuildFile section */
                                };
                        };
                        buildConfigurationList = 18BF1DC52B81AB7C00B8D90F /* Build configuration list for PBXProject "MouseSdk" */;
-                       compatibilityVersion = "Xcode 14.0";
+                       compatibilityVersion = "Xcode 13.0";
                        developmentRegion = en;
                        hasScannedForEncodings = 0;
                        knownRegions = (
index af8900e..035fe32 100644 (file)
@@ -48,7 +48,6 @@
         btn.bordered = true;
         btn.title = @"选择指令";
         [btn setTarget:self];
-        [btn setAction:@selector(geviceInfoAction)];
         [btn setAction:@selector(showPopupMenu:)];
         
         btn.state = 1;