云端录音记录支持搜索
authorliangzhenying <867174124@qq.com>
Thu, 13 Nov 2025 09:05:36 +0000 (17:05 +0800)
committerliangzhenying <867174124@qq.com>
Thu, 13 Nov 2025 09:05:36 +0000 (17:05 +0800)
33 files changed:
ShenYin/JoyGemAI.framework/Headers/JoyGemAI.h
ShenYin/JoyGemAI.framework/Headers/JoyGemCallManager.h
ShenYin/JoyGemAI.framework/Headers/JoyGemChatManager.h
ShenYin/JoyGemAI.framework/Headers/JoyGemModel.h
ShenYin/JoyGemAI.framework/Headers/JoyGemParam.h
ShenYin/JoyGemAI.framework/Headers/JoyGemRecordManager.h
ShenYin/JoyGemAI.framework/Headers/JoyGemSIDeviceManager.h
ShenYin/JoyGemAI.framework/Headers/JoyGemSIManager.h
ShenYin/JoyGemAI.framework/Headers/JoyGemSubtitleManager.h [new file with mode: 0644]
ShenYin/JoyGemAI.framework/JoyGemAI
ShenYin/JoyGemAI.framework/_CodeSignature/CodeResources
ShenYin/ShenYin/Api/Config.swift
ShenYin/ShenYin/Api/HttpClient2.swift
ShenYin/ShenYin/Assets.xcassets/home/calendar_icon.imageset/Contents.json [new file with mode: 0644]
ShenYin/ShenYin/Assets.xcassets/home/calendar_icon.imageset/日历_(1)@2x.png [new file with mode: 0644]
ShenYin/ShenYin/Assets.xcassets/home/calendar_icon.imageset/日历_(1)@3x.png [new file with mode: 0644]
ShenYin/ShenYin/UI/Home/Chinese2English/Chinese2EnglishVC.swift
ShenYin/ShenYin/UI/Home/Dialog/DialogTranlateVC.swift
ShenYin/ShenYin/UI/Home/HistoryRecord/HistoryRecordCell.swift
ShenYin/ShenYin/UI/Home/HistoryRecord/RecordSearchBar.swift
ShenYin/ShenYin/UI/Home/HistoryRecord/VC/CloudRecordVC.swift
ShenYin/ShenYin/UI/Home/HistoryRecord/VC/CloudSearchVC.swift [new file with mode: 0644]
ShenYin/ShenYin/UI/Home/HistoryRecord/VC/HistoryRecordVC.swift
ShenYin/ShenYin/UI/Home/HistoryRecord/VC/HistorySearchVC.swift
ShenYin/ShenYin/UI/Home/RecordDetail/RecordDetaiContent.swift
ShenYin/ShenYin/UI/Home/RecordTranslateVC.swift
ShenYin/ShenYin/UI/Home/Scan/DeviceVM.swift
ShenYin/ShenYin/UI/Launch/AppLaunch.swift
ShenYin/ShenYin/UI/Launch/BleManager.swift
ShenYin/ShenYin/UI/Launch/JoySdkManager.swift
ShenYin/ShenYin/UI/Mine/ProtocolVC.swift
ShenYin/ShenYin/lan/AiSupportLanguage.swift
ShenYin/ShenYin/lan/LanConfig.swift

index 78d71ff..f16e0fe 100644 (file)
@@ -25,3 +25,4 @@ FOUNDATION_EXPORT const unsigned char JoyGemAIVersionString[];
 #import <JoyGemAI/JoyGemCallManager.h>
 #import <JoyGemAI/JoyGemEnum.h>
 #import <JoyGemAI/JoyGemDeviceManager.h>
+#import <JoyGemAI/JoyGemSubtitleManager.h>
index 86a2bac..5a4dd0f 100644 (file)
@@ -63,6 +63,13 @@ NS_ASSUME_NONNULL_BEGIN
 @required
 -(void)callManager:(JoyGemCallManager*)manager didUpdateMonoAsr:(JoyGemAsrModel*)model;
 
+/// 录音时的声波强度回调
+/// - Parameters:
+///   - list: 强度数组
+///   - timestamp: 时间戳(ms)
+@optional
+-(void)callManager:(JoyGemCallManager*)manager monoIntensity:(NSArray<NSNumber*>*)list timestamp:(NSTimeInterval)timestamp;
+
 @end
 
 @protocol JoyGemCallStereoDelegate <NSObject>
@@ -74,6 +81,13 @@ NS_ASSUME_NONNULL_BEGIN
 @required
 -(void)callManager:(JoyGemCallManager*)manager didUpdateStereoAsr:(JoyGemAsrModel*)model channel:(NSUInteger)index;
 
+/// 录音时的声波强度回调
+/// - Parameters:
+///   - lists: 强度数组(二维数组,0为左声道,1为右声道)
+///   - timestamp: 时间戳(ms)
+@optional
+-(void)callManager:(JoyGemCallManager*)manager stereoIntensity:(NSArray<NSArray<NSNumber*>*>*)lists timestamp:(NSTimeInterval)timestamp;
+
 
 @end
 
index b43fe4d..32c1303 100644 (file)
@@ -56,7 +56,7 @@ NS_ASSUME_NONNULL_BEGIN
 -(void)chatManager:(JoyGemChatManager*)manager onError:(JoyGemErrorCode)code;
 
 @optional
--(void)chatManagerStopped:(JoyGemChatManager*)manager DEPRECATED_MSG_ATTRIBUTE("Use isActive");
+-(void)chatManagerStopped:(JoyGemChatManager*)manager DEPRECATED_MSG_ATTRIBUTE("Use isActive instead");
 
 @end
 
@@ -81,12 +81,10 @@ NS_ASSUME_NONNULL_BEGIN
 
 /// 开启单次对话
 /// - Note: 如正在播报,需要提前关闭tts后再调用,否则开启无效
-/// - TODO: 待容错处理
 - (void)startSingleChat;
 
 /// 结束单次对话
 /// - Note:请勿在开启单次对话后立刻调用该方法,否则耳机可能不会处理该次请求
-/// - TODO: 待容错处理
 - (void)stopSingleChat;
 
 /// 开启连续对话
@@ -107,9 +105,18 @@ NS_ASSUME_NONNULL_BEGIN
 ///   - list: 角色列表
 -(void)getSpeakerList:(void (^)(NSArray<JoyGemSpeaker *>*))list;
 
-///配置当前角色
+/// 配置当前角色
 -(void)setSpeaker:(JoyGemSpeaker*)speaker;
 
+/// 重置为默认角色
+-(void)resetSpearker;
+
+/// 进入英语口语陪练模式
+-(void)startSpoken;
+
+/// 退出英语口语陪练模式
+-(void)stopSpoken;
+
 @end
 
 NS_ASSUME_NONNULL_END
index bb4ae92..3a7cf57 100644 (file)
@@ -17,8 +17,13 @@ NS_ASSUME_NONNULL_BEGIN
 @property (nonatomic, strong) NSString *type;/// 类型
 @property (nonatomic, strong) NSString *role;/// 角色
 
-@end
+///默认角色
++(JoyGemSpeaker*)defaultRole;
+
+///默认的口语陪练角色
++(JoyGemSpeaker*)defaultSpoken;
 
+@end
 
 ///ASR对象
 @interface JoyGemAsrModel : NSObject
@@ -36,4 +41,15 @@ NS_ASSUME_NONNULL_BEGIN
 
 @end
 
+///录音声纹识别
+@interface JoyGemRecordSpeaker : NSObject
+
+@property (nonatomic, copy, nullable) NSString *speakerId;     /// 说话人ID(字符串,如 "1")
+@property (nonatomic, assign) NSInteger startTime;             /// 起始时间(ms)
+@property (nonatomic, assign) NSInteger endTime;               /// 结束时间(ms)
+@property (nonatomic, copy, nullable) NSString *text;          /// 该段文本
+@property (nonatomic, assign) NSInteger index;                 /// 该段确定序号
+
+@end
+
 NS_ASSUME_NONNULL_END
index a0f4a81..b1f5ab6 100644 (file)
@@ -38,12 +38,32 @@ extern NSString* const kJoyGemAILocalizationPT;
 //印尼语 id-ID
 extern NSString* const kJoyGemAILocalizationID;
 
-//俄语 ru-RU
-extern NSString* const kJoyGemAILocalizationRU;
+//俄语(输入) ru-RU
+extern NSString* const kJoyGemAILocalizationRU_Original;
+
+//俄语(输出) ru
+extern NSString* const kJoyGemAILocalizationRU_Target;
 
 //马来语 ms-MY
 extern NSString* const kJoyGemAILocalizationMS;
 
+//德语 de-DE
+extern NSString* const kJoyGemAILocalizationDE;
+
+//菲律宾语 fil-PH
+extern NSString* const kJoyGemAILocalizationFIL;
+
+//泰语 th-TH
+extern NSString* const kJoyGemAILocalizationTH;
+
+//阿拉伯语 ar-SA
+extern NSString* const kJoyGemAILocalizationAR;
+
+//越南语 vi
+extern NSString* const kJoyGemAILocalizationVI;
+
+
+
 ///MARK: 蓝牙相关
 ///
 ///AI功能使用的蓝牙服务
index 99903a9..e95de63 100644 (file)
@@ -12,6 +12,7 @@ NS_ASSUME_NONNULL_BEGIN
 
 @class JoyGemRecordManager;
 @class JoyGemAsrModel;
+@class JoyGemRecordSpeaker;
 
 //现场录音的委托类
 @protocol JoyGemRecordManagerDelegate <NSObject>
@@ -48,6 +49,20 @@ NS_ASSUME_NONNULL_BEGIN
 @optional
 -(void)recordManagerCompleted:(JoyGemRecordManager*)manager;
 
+/// 录音时的声波强度回调
+/// - Parameters:
+///   - list: 强度数组
+///   - timestamp: 时间戳(ms)
+@optional
+-(void)recordManager:(JoyGemRecordManager*)manager intensity:(NSArray<NSNumber*>*)list timestamp:(NSTimeInterval)timestamp;
+
+/// 录音时的说话人区分
+/// - Parameters:
+///   - list: 强度数组
+///   - timestamp: 时间戳(ms)
+@optional
+-(void)recordManager:(JoyGemRecordManager*)manager speakers:(NSArray<JoyGemRecordSpeaker*>*)list;
+
 @end
 
 /// 现场录音的管理类
@@ -62,6 +77,9 @@ NS_ASSUME_NONNULL_BEGIN
 /// 是否正在暂停中
 @property(nonatomic,assign) BOOL isPause;
 
+/// 是否开启声纹识别
+@property(nonatomic,assign) BOOL useVpr;
+
 /// 实例对象
 + (instancetype)shared;
 
@@ -77,6 +95,8 @@ NS_ASSUME_NONNULL_BEGIN
 /// 恢复录音
 - (void)resume;
 
+
+
 @end
 
 NS_ASSUME_NONNULL_END
index 00d2363..826b9c6 100644 (file)
@@ -32,7 +32,7 @@ NS_ASSUME_NONNULL_BEGIN
 /// - Parameters:
 ///   - data: 流数据 pcm格式
 @required
--(void)siDeviceManager:(JoyGemSIDeviceManager*)manager didUpdateAudioData:(NSData*)data;
+-(void)siDeviceManager:(JoyGemSIDeviceManager*)manager didUpdateAudioData:(NSData*)data sampleRate:(NSUInteger)sampleRate;
 
 ///结束时的回调
 /// - Parameters:
index 63d9ee0..bf64e1c 100644 (file)
@@ -32,7 +32,7 @@ NS_ASSUME_NONNULL_BEGIN
 /// - Parameters:
 ///   - data: 流数据 pcm格式
 @required
--(void)siManager:(JoyGemSIManager*)manager didUpdateAudioData:(NSData*)data;
+-(void)siManager:(JoyGemSIManager*)manager didUpdateAudioData:(NSData*)data sampleRate:(NSUInteger)sampleRate;
 
 ///录音状态切换时
 -(void)siManager:(JoyGemSIManager*)manager stateChange:(JoyGemStateCode)code;
diff --git a/ShenYin/JoyGemAI.framework/Headers/JoyGemSubtitleManager.h b/ShenYin/JoyGemAI.framework/Headers/JoyGemSubtitleManager.h
new file mode 100644 (file)
index 0000000..18f6bc5
--- /dev/null
@@ -0,0 +1,79 @@
+//
+//  JoyGemSubtitleManager.h
+//  JoyGemAI
+//
+//  Created by jjjs on 2025/10/17.
+//
+
+#import <Foundation/Foundation.h>
+#import <JoyGemAI/JoyGemEnum.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+@class JoyGemSubtitleManager;
+@class JoyGemAsrModel;
+
+/// 字幕翻译的委托
+@protocol JoyGemSubtitleManagerDelegate <NSObject>
+
+///录音文字识别时
+/// - Parameters:
+///   - model: asr识别的对象
+@required
+-(void)subtitleManager:(JoyGemSubtitleManager*)manager didUpdateRaw:(JoyGemAsrModel*)model;
+
+///录音文字翻译时
+/// - Parameters:
+///   - model: asr识别的对象
+@required
+-(void)subtitleManager:(JoyGemSubtitleManager*)manager didUpdateTranslate:(JoyGemAsrModel*)model;
+
+///结束时的回调
+/// - Parameters:
+///   - code: 录音结束的原因
+-(void)subtitleManagerStopped:(JoyGemSubtitleManager*)manager;
+
+///录音状态回调
+/// - Parameters:
+///   - code: 状态变化
+-(void)subtitleManager:(JoyGemSubtitleManager*)manager stateChange:(JoyGemStateCode)code;
+
+/// 录音错误回调
+///  - Parameters:
+///   - mode:错误代码
+-(void)subtitleManager:(JoyGemSubtitleManager*)manager onError:(JoyGemErrorCode)code;
+
+@end
+
+//字幕翻译的管理类
+@interface JoyGemSubtitleManager : NSObject
+
+/// 委托类
+/// - Description: 字幕翻译状态委托
+@property(nonatomic, weak) id<JoyGemSubtitleManagerDelegate> delegate;
+
+/// 耳机识别语言
+@property(nonatomic, strong) NSString* originalLanguage;
+
+/// 耳机输出语言
+@property(nonatomic, strong) NSString* targetLanguage;
+
+/// 是否正在录音
+@property(nonatomic, assign) BOOL isRecording;
+
+/// 是否支持字幕翻译功能
+@property(nonatomic, assign) BOOL support;
+
+
+/// 实例对象
++ (instancetype)shared;
+
+/// 开始字幕翻译
+- (void)start;
+
+/// 结束字幕翻译
+- (void)stop;
+
+@end
+
+NS_ASSUME_NONNULL_END
index 39d3ea4..b8dcb56 100755 (executable)
Binary files a/ShenYin/JoyGemAI.framework/JoyGemAI and b/ShenYin/JoyGemAI.framework/JoyGemAI differ
index 0dc62d6..39407de 100644 (file)
@@ -10,7 +10,7 @@
                </data>
                <key>Headers/JoyGemAI.h</key>
                <data>
-               8NnRIJGe/ATf7a6jdPAfly29UC4=
+               7HO7XUA3NdXWlvtUWyiYdTaaZ4M=
                </data>
                <key>Headers/JoyGemAIManager.h</key>
                <data>
                </data>
                <key>Headers/JoyGemCallManager.h</key>
                <data>
-               BWJeSQyHYVE1TwiSgvXYhNdyJXI=
+               j3rnAsYkKzhbISebVqN00PCSeTk=
                </data>
                <key>Headers/JoyGemChatManager.h</key>
                <data>
-               I9SSrf6xzcSE2qKnMMmiSe9IV1I=
+               RmM4HQA4Ay7VLvTFWPw7s69sqBk=
                </data>
                <key>Headers/JoyGemDeviceManager.h</key>
                <data>
                </data>
                <key>Headers/JoyGemModel.h</key>
                <data>
-               ZQ5Ie1ErW3QF78XXxz/ydxsSQ5E=
+               WP0CE48Wvoq3FJX4ldz90bcNpTw=
                </data>
                <key>Headers/JoyGemParam.h</key>
                <data>
-               SzxTsmYo/XvsD3Uz2iqjnRDkRnA=
+               fq/eezzfuJFDMFtH+PXLOMYQQCs=
                </data>
                <key>Headers/JoyGemRecordManager.h</key>
                <data>
-               pKeMD6Xg3Br6ZOWB/BxyqAicCfc=
+               GzBtgXRPoBuyG0YwvFmoGA8Yg20=
                </data>
                <key>Headers/JoyGemSIDeviceManager.h</key>
                <data>
-               Cn0/thrZRr+pJ+skbuRbfJJirmA=
+               aH1rBy/dU7IVJkpw/BVW3BvfNtk=
                </data>
                <key>Headers/JoyGemSIManager.h</key>
                <data>
-               UiwORLtCgzF+QX/hXpK2w63Yx4Q=
+               363bn6W4/w8F3xEYQrU0LEVD/XM=
+               </data>
+               <key>Headers/JoyGemSubtitleManager.h</key>
+               <data>
+               fDUdqGo5wSc78huSI4OUK8Qr8AU=
                </data>
                <key>Info.plist</key>
                <data>
                <dict>
                        <key>hash2</key>
                        <data>
-                       nO8YFPNcoOIKUrpS9Pd4/T9V98f16ACbB7LFROxEPhQ=
+                       AWVDZqJirRMWfVQc7FdxP1cjTTx2LWTSTwHDN0/ySRA=
                        </data>
                </dict>
                <key>Headers/JoyGemAIManager.h</key>
                <dict>
                        <key>hash2</key>
                        <data>
-                       4aqJC5wgayXjaD7XHafngtRL7PSKbqPdFujG27R7l4Q=
+                       eKpRq8GaSD9YaI7Tfafc4BVLc/Vz2rS7cEcx8W6S+EI=
                        </data>
                </dict>
                <key>Headers/JoyGemChatManager.h</key>
                <dict>
                        <key>hash2</key>
                        <data>
-                       v9CC1YyWez/AeMXpqpyouz+LNIkBqq147kBPoPYEVK8=
+                       TrJNORF2yq5ZRf++PfcoxEwE0ic9W2VxQ+Nz7M7WKPM=
                        </data>
                </dict>
                <key>Headers/JoyGemDeviceManager.h</key>
                <dict>
                        <key>hash2</key>
                        <data>
-                       rFoRM/fmnvDGWCISlMyWSioYrwvoetaKOU8WrgOCBNw=
+                       0kLzuK/HuYBvk9JPhWYbR2z7t1XSX83X5Am9ZD+vLXw=
                        </data>
                </dict>
                <key>Headers/JoyGemParam.h</key>
                <dict>
                        <key>hash2</key>
                        <data>
-                       oXJjxfeaUmV4FnIP5DUB7MLED0QsP5hp0v0AqO8ttMs=
+                       QKmLGG6IwVxT2QonEGJzea6rMbUX0PBNGoWpzqSNRbY=
                        </data>
                </dict>
                <key>Headers/JoyGemRecordManager.h</key>
                <dict>
                        <key>hash2</key>
                        <data>
-                       C4rHebQlX6cKQWVmjh1cDetbe6cC+glcl2deSRF3r/o=
+                       ep9EdHop9Qn0M9nCT7YwmHXxJh/XXCjBqtYjErKFd+8=
                        </data>
                </dict>
                <key>Headers/JoyGemSIDeviceManager.h</key>
                <dict>
                        <key>hash2</key>
                        <data>
-                       bkQU7XO+s/wbDHlWf9VM2kvGxB5myT2n0bEp+ac6tHo=
+                       x40L/ElpDJCMjpEBZZancy57toshn0DM6MOhkwnIMYQ=
                        </data>
                </dict>
                <key>Headers/JoyGemSIManager.h</key>
                <dict>
                        <key>hash2</key>
                        <data>
-                       5nObgPQx+QBZs8lbacCMnhBUJs629P/ZuVFJytUIV44=
+                       NTKnVzoipmhtgLLzocc8J5kT4Q2qmUmVhZ3MgZUrbkA=
+                       </data>
+               </dict>
+               <key>Headers/JoyGemSubtitleManager.h</key>
+               <dict>
+                       <key>hash2</key>
+                       <data>
+                       jGDopDnKVm0z/p6dEPEs54wWT6iwbetpqbTXTk8zoAQ=
                        </data>
                </dict>
                <key>Modules/JoyGemAI.swiftmodule/Project/arm64-apple-ios.swiftsourceinfo</key>
index 14aec59..1a73781 100644 (file)
@@ -16,8 +16,14 @@ class DCConfig{
 //    https://dc-cloud-pub.tos-cn-guangzhou.volces.com/apple-app-site-association
     //    static let appStoreLink = "https://itunes.apple.com/cn/app/6738022284?mt=8"
     static let appStoreLink = "https://itunes.apple.com/cn/app/6752551504?mt=8"
-    
     /// 版本检测
     static let appStoreVersionURL = "https://itunes.apple.com/cn/lookup?id=6752551504"
+    
+    /// 服务器地址
+    static let baseURL = "http://118.145.178.188:32000"
+//    static let baseURL = "https://app-api.deepchords.com"
+    /// 用户隐私协议地址
+    static let yonghuBaseURL = "https://pub-tos.deepchords.com"
+    
 }
 
index f445efd..850a5b0 100644 (file)
@@ -129,8 +129,9 @@ extension CommomApi{
     {
         //        return URL(string: "https://app-api.raisound.com")!
         //        http://118.145.178.188:32000/uc/country/list
-        //        return URL(string: "https://app-api.deepchords.com")!
-        return "http://118.145.178.188:32000"
+//        return "https://app-api.deepchords.com"
+//        return "http://118.145.178.188:32000"
+        return DCConfig.baseURL
     }
     
     var url:String{
diff --git a/ShenYin/ShenYin/Assets.xcassets/home/calendar_icon.imageset/Contents.json b/ShenYin/ShenYin/Assets.xcassets/home/calendar_icon.imageset/Contents.json
new file mode 100644 (file)
index 0000000..a55920b
--- /dev/null
@@ -0,0 +1,22 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "scale" : "1x"
+    },
+    {
+      "filename" : "日历_(1)@2x.png",
+      "idiom" : "universal",
+      "scale" : "2x"
+    },
+    {
+      "filename" : "日历_(1)@3x.png",
+      "idiom" : "universal",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}
diff --git a/ShenYin/ShenYin/Assets.xcassets/home/calendar_icon.imageset/日历_(1)@2x.png b/ShenYin/ShenYin/Assets.xcassets/home/calendar_icon.imageset/日历_(1)@2x.png
new file mode 100644 (file)
index 0000000..d6c429b
Binary files /dev/null and "b/ShenYin/ShenYin/Assets.xcassets/home/calendar_icon.imageset/\346\227\245\345\216\206_(1)@2x.png" differ
diff --git a/ShenYin/ShenYin/Assets.xcassets/home/calendar_icon.imageset/日历_(1)@3x.png b/ShenYin/ShenYin/Assets.xcassets/home/calendar_icon.imageset/日历_(1)@3x.png
new file mode 100644 (file)
index 0000000..9999f4f
Binary files /dev/null and "b/ShenYin/ShenYin/Assets.xcassets/home/calendar_icon.imageset/\346\227\245\345\216\206_(1)@3x.png" differ
index d76fb7c..eb10389 100644 (file)
@@ -151,6 +151,8 @@ extension Chinese2EnglishVC:UITableViewDelegate,UITableViewDataSource {
 }
 
 extension Chinese2EnglishVC:JoyGemSIDeviceManagerDelegate{
+
+    
     func siDeviceManagerStopped(_ manager: JoyGemSIDeviceManager, reason code: JoyGemRecordStopCode) {
         Logger.debug("停止",code.rawValue)
         if code == .passively {
@@ -189,11 +191,14 @@ extension Chinese2EnglishVC:JoyGemSIDeviceManagerDelegate{
         }
         refreshData()
     }
-    
-    func siDeviceManager(_ manager: JoyGemSIDeviceManager, didUpdateAudioData data: Data) {
+    func siDeviceManager(_ manager: JoyGemSIDeviceManager, didUpdateAudioData data: Data, sampleRate: UInt) {
         pcmData.append(data)
         PcmSound.sharedInstance().feed(data)
     }
+//    func siDeviceManager(_ manager: JoyGemSIDeviceManager, didUpdateAudioData data: Data) {
+//        pcmData.append(data)
+//        PcmSound.sharedInstance().feed(data)
+//    }
     
     func siDeviceManager(_ manager: JoyGemSIDeviceManager, update mode: JoyGemRecordMode) {
         
index b04aa71..64037e3 100644 (file)
@@ -19,7 +19,10 @@ class DialogTranslateVC:BaseVC{
     var tmpList : [(String,UInt)] = []
     var tmpOtherList : [(String,UInt)] = []
     var otherPcm = Data()
-    
+    deinit {
+        PcmSound.sharedInstance().stopAudioUnit()
+        PcmSound.sharedInstance().cleanup()
+    }
     override func viewDidLoad() {
         super.viewDidLoad()
     }
@@ -205,6 +208,8 @@ extension DialogTranslateVC:DialogRecordBarDelegate{
 }
 
 extension DialogTranslateVC : JoyGemSIManagerDelegate {
+
+    
     func siManagerStopped(_ manager: JoyGemSIManager, reason code: JoyGemRecordStopCode) {
         if let model = dataList.last,model.send.isEmpty{
             dataList.removeLast()
@@ -270,10 +275,12 @@ extension DialogTranslateVC : JoyGemSIManagerDelegate {
     func siManagerStopped(_ manager: JoyGemSIManager) {
         
     }
-    
-    func siManager(_ manager: JoyGemSIManager, didUpdateAudioData data: Data) {
+    func siManager(_ manager: JoyGemSIManager, didUpdateAudioData data: Data, sampleRate: UInt) {
         PcmSound.sharedInstance().feed(data)
     }
+//    func siManager(_ manager: JoyGemSIManager, didUpdateAudioData data: Data) {
+//        PcmSound.sharedInstance().feed(data)
+//    }
     
     func siManager(_ manager: JoyGemSIManager, onError code: JoyGemErrorCode) {
         
@@ -281,6 +288,8 @@ extension DialogTranslateVC : JoyGemSIManagerDelegate {
 }
 
 extension DialogTranslateVC : JoyGemSIDeviceManagerDelegate {
+
+    
     
     func siDeviceManagerStopped(_ manager: JoyGemSIDeviceManager, reason code: JoyGemRecordStopCode) {
         if let model = dataList.last,model.send.isEmpty{
@@ -335,11 +344,13 @@ extension DialogTranslateVC : JoyGemSIDeviceManagerDelegate {
         }).joined(separator: "\n")
         refreshData()
     }
-    
-    func siDeviceManager(_ manager: JoyGemSIDeviceManager, didUpdateAudioData data: Data) {
-        //otherPcm.append(data)
+    func siDeviceManager(_ manager: JoyGemSIDeviceManager, didUpdateAudioData data: Data, sampleRate: UInt) {
         PcmSound.sharedInstance().feed(data)
     }
+//    func siDeviceManager(_ manager: JoyGemSIDeviceManager, didUpdateAudioData data: Data) {
+//        //otherPcm.append(data)
+//        PcmSound.sharedInstance().feed(data)
+//    }
     
     func siDeviceManager(_ manager: JoyGemSIDeviceManager, update mode: JoyGemRecordMode) {
         
index b3052f4..bfa218e 100644 (file)
@@ -15,6 +15,8 @@ class HistoryRecordCell: BaseTableViewCell {
     let dateLabel = UILabel()
     let typeLabel = UILabel()
     var longPressCallback:((_ cell:HistoryRecordCell)->Void)?
+    var collectStateChangeCallback:((_ cell:HistoryRecordCell)->Void)?
+    var supportLongPress = true
     
     var model:RecordHistoryModel?{
         didSet{
@@ -155,9 +157,13 @@ class HistoryRecordCell: BaseTableViewCell {
             self.localModel = localModel
             RecordCache.shared.add(localModel)
         }
+        collectStateChangeCallback?(self)
     }
     
     @objc func longPressAction(_ gesture: UILongPressGestureRecognizer) {
+        if supportLongPress == false {
+            return
+        }
         if gesture.state == .began {
             bgView.backgroundColor = .lightGray.withAlphaComponent(0.5)
             longPressCallback?(self)
index b945972..0ccaadc 100644 (file)
@@ -14,6 +14,7 @@ class RecordSearchBar:BaseView{
     let textField = UITextField()
     let icon = UIImageView(image: UIImage(named: "search_2"), )
     let cancelBtn = UIButton(type: .custom)
+    let calendarBtn = UIButton(type: .custom)
     
     weak var delegate:RecordSearchBarDelegate?
     override init(frame: CGRect) {
@@ -30,7 +31,11 @@ class RecordSearchBar:BaseView{
             v.addTarget(self, action: #selector(cancelAction), for: .touchUpInside)
 //            v.backgroundColor = .yellow
         }
-        
+        calendarBtn.do { v in
+            v.setImage(UIImage(named: "calendar_icon"), for: .normal)
+            v.addTarget(self, action: #selector(calendarAction), for: .touchUpInside)
+            //            v.backgroundColor = .yellow
+        }
         
         bgView.do { v in
             v.yc.setCornerRadius(8)
@@ -47,8 +52,7 @@ class RecordSearchBar:BaseView{
         }
         
         addSubviews([bgView,cancelBtn])
-        bgView.addSubview(icon)
-        bgView.addSubview(textField)
+        bgView.addSubviews([icon,textField,calendarBtn])
     }
     override func setupLayout() {
         cancelBtn.snp.makeConstraints { make in
@@ -68,10 +72,15 @@ class RecordSearchBar:BaseView{
             make.centerY.equalToSuperview()
             make.left.equalToSuperview().inset(12)
         }
+        calendarBtn.snp.makeConstraints { make in
+            make.right.equalToSuperview().inset(6)
+            make.centerY.equalToSuperview()
+            make.size.equalTo(18)
+        }
         textField.snp.makeConstraints { make in
             make.left.equalToSuperview().inset(34)
             make.top.bottom.equalToSuperview()
-            make.right.equalToSuperview()
+            make.right.equalTo(calendarBtn.snp.left).offset(-20)
         }
     }
     
@@ -86,7 +95,9 @@ class RecordSearchBar:BaseView{
     @objc func cancelAction(){
         App.shared.popPage(animated: true)
     }
-    
+    @objc func calendarAction(){
+        
+    }
 }
 extension RecordSearchBar:UITextFieldDelegate{
     func textFieldShouldReturn(_ textField: UITextField) -> Bool {
index cdc1281..97a9650 100644 (file)
@@ -24,8 +24,8 @@ class CloudRecordVC: BaseVC{
     override func setupViews() {
         navBar.do { v in
             v.title = "云存储空间".l10n
-//            let items = [NavigationRightItem(image: UIImage(named: "filter"),target: self, action: #selector(filterAction)),NavigationRightItem(image: UIImage(named: "search"), target: self,action: #selector(searchAction))]
-            let items = [NavigationRightItem(image: UIImage(named: "filter"),target: self, action: #selector(filterAction))]
+            let items = [NavigationRightItem(image: UIImage(named: "filter"),target: self, action: #selector(filterAction)),NavigationRightItem(image: UIImage(named: "search"), target: self,action: #selector(searchAction))]
+//            let items = [NavigationRightItem(image: UIImage(named: "filter"),target: self, action: #selector(filterAction))]
             v.setupRightViews(items)
         }
         
@@ -52,7 +52,18 @@ class CloudRecordVC: BaseVC{
         refreshAction()
     }
     @objc func searchAction(){
-        
+        let vc = CloudSearchVC(list: vm.list)
+        vc.deleteCallback = { [weak self] deleteList in
+            guard let self = self else{return}
+            for item in deleteList{
+                vm.list.removeAll(where: {$0.id == item.id})
+            }
+            tableView.reloadData()
+        }
+        vc.collectStateChangeCallback = { [weak self]  in
+            self?.tableView.reloadData()
+        }
+        App.shared.goToPage(vc)
     }
     @objc func filterAction(){
         let list = FiltterRecordType.alls.map({$0.title})
diff --git a/ShenYin/ShenYin/UI/Home/HistoryRecord/VC/CloudSearchVC.swift b/ShenYin/ShenYin/UI/Home/HistoryRecord/VC/CloudSearchVC.swift
new file mode 100644 (file)
index 0000000..25f2925
--- /dev/null
@@ -0,0 +1,135 @@
+//
+//  CloudSearchVC.swift
+//  ShenYin
+//
+//  Created by liangshi on 2025/11/13.
+//
+
+import UIKit
+class CloudSearchVC: BaseVC{
+    let searchBar = RecordSearchBar()
+    let tableView = UITableView(frame: .zero, style: .plain)
+    var orginList = [RecordHistoryModel]()
+    var searchList = [RecordHistoryModel]()
+    var deleteCallback:((_ list:[RecordHistoryModel])->Void)?
+    var collectStateChangeCallback:(()->Void)?
+    
+    init(list:[RecordHistoryModel]) {
+        orginList = list
+        searchList = orginList
+        super.init()
+    }
+
+    
+    override func viewDidLoad() {
+        super.viewDidLoad()
+        // Do any additional setup after loading the view.
+    }
+    override func viewWillAppear(_ animated: Bool) {
+        super.viewWillAppear(animated)
+        tableView.reloadData()
+    }
+    override func setupViews() {
+        navBar.addSubview(searchBar)
+        view.addSubviews([tableView])
+        
+        searchBar.do { v in
+            v.delegate = self
+        }
+        
+        tableView.do { v in
+            v.backgroundColor = .rgb(0xF7F7F7)
+            v.delegate = self
+            v.dataSource = self
+            v.estimatedRowHeight = 84
+            v.separatorStyle = .none
+            //            v.isEditing = true
+            v.register(HistoryRecordCell.self, forCellReuseIdentifier: HistoryRecordCell.reuseID)
+        }
+        
+    }
+    override func setupLayout() {
+        searchBar.snp.makeConstraints { make in
+            make.edges.equalToSuperview()
+        }
+        tableView.snp.makeConstraints { make in
+            make.left.right.equalToSuperview()
+            make.top.equalTo(kNavBtm)
+            make.bottom.equalToSuperview()
+        }
+    }
+    override func setupData() {
+
+    }
+
+}
+
+extension CloudSearchVC:UITableViewDelegate,UITableViewDataSource {
+    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
+        return searchList.count
+    }
+    
+    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
+        let cell = HistoryRecordCell.cell(withTableView: tableView, indexPath: indexPath)
+        cell.supportLongPress = false
+        cell.model = searchList[indexPath.row]
+        cell.collectStateChangeCallback = { [weak self] cell in
+            self?.collectStateChangeCallback?()
+        }
+        return cell
+    }
+    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
+        let model = searchList[indexPath.row]
+        let vc = RecordDetailVC(remoteData: model)
+        vc.delegateCallback = {[weak self] in
+            guard let self = self else{return}
+            deleteCallback?([model])
+            searchList.remove(at: indexPath.row)
+            orginList.removeAll(where: {$0 === model})
+            tableView.reloadData()
+        }
+        App.shared.goToPage(vc)
+    }
+    func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
+        if editingStyle == .delete {
+            CustomAlert.showWith(title: "您要删除这条录音吗?".l10n, messgage: "删除后,这些记录将无法恢复。".l10n, cancelCallback: {}) {[weak self] in
+                guard let self = self else{return}
+                let model = searchList[indexPath.row]
+                searchList.remove(at: indexPath.row)
+                orginList.removeAll(where: {$0 === model})
+                tableView.deleteRows(at: [indexPath], with:.fade)
+                HttpClient.request( CommomApi.deleteVoiceRecord(recordId: model.id)) {[weak self](result:ResponseBaseModel,res) in
+                    if result.isSuccess {
+                        self?.deleteCallback?([model])
+                    }
+                }
+            }
+        }
+    }
+    func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
+        return true
+    }
+    func tableView(_ tableView: UITableView, titleForDeleteConfirmationButtonForRowAt indexPath: IndexPath) -> String? {
+        return "删除".l10n
+    }
+    
+}
+extension CloudSearchVC:RecordSearchBarDelegate{
+    func searchBarTextDidChange(_ toolBar: RecordSearchBar, _ text: String) {
+        if text.isEmpty {
+            searchList = orginList
+        }
+        else{
+            var list =  [RecordHistoryModel]()
+            for item in orginList {
+                if item.name.uppercased() .contains(text.uppercased()) {
+                    list.append(item)
+                }
+            }
+            searchList = list
+        }
+        tableView.reloadData()
+    }
+    
+    
+}
index 4835d11..2084ecb 100644 (file)
@@ -70,6 +70,14 @@ class HistoryRecordVC: BaseVC{
             }
             tableView.reloadData()
         }
+        vc.collectStateChangeCallback = { [weak self] model in
+            guard let self = self else { return  }
+            guard let data = list.filter({$0.createTime == model.createTime}).first else{
+                return
+            }
+            data.favorite = model.favorite
+            tableView.reloadData()
+        }
         App.shared.goToPage(vc)
     }
     @objc func filterAction(){
index 72f1d37..0230635 100644 (file)
@@ -12,6 +12,7 @@ class HistorySearchVC: BaseVC{
     var orginList = [RecordDetailModel]()
     var searchList = [RecordDetailModel]()
     var deleteCallback:((_ list:[RecordDetailModel])->Void)?
+    var collectStateChangeCallback:((RecordDetailModel)->Void)?
 //    init(data:[RecordDetailModel]) {
 //        list = data
 //        super.init()
@@ -74,7 +75,11 @@ extension HistorySearchVC:UITableViewDelegate,UITableViewDataSource {
     
     func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
         let cell = HistoryRecordCell.cell(withTableView: tableView, indexPath: indexPath)
+        cell.supportLongPress = false
         cell.localModel = searchList[indexPath.row]
+        cell.collectStateChangeCallback = { [weak self] cell in
+            self?.collectStateChangeCallback?(cell.localModel!)
+        }
         return cell
     }
     func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
index 7ffa28d..9d278f0 100644 (file)
@@ -74,7 +74,7 @@ class RecordDetaiContent: BaseView {
         YLprogressHUD.showAutoHiden("已复制")
     }
     @objc func shareAction(){
-        if let text = textView.text{
+        if let text = textView.text,text.isNotEmpty{
             let vc = UIActivityViewController(activityItems: [text], applicationActivities: nil)
             // 对于iPad,设置弹出源
             //        vc.popoverPresentationController?.sourceView = self.view
index 4490938..5049bb6 100644 (file)
@@ -16,7 +16,10 @@ class RecordTranslateVC:BaseVC{
     var translateList : [(String,UInt)] = []
     var recordFilePath : String?
     
-
+    deinit {
+        PcmSound.sharedInstance().stopAudioUnit()
+        PcmSound.sharedInstance().cleanup()
+    }
     override func setupViews() {
         view.addSubviews([toolBar,textView,translateTextView])
         
@@ -166,6 +169,8 @@ extension RecordTranslateVC:RecordBarDelegate{
 }
 
 extension RecordTranslateVC : JoyGemSIManagerDelegate {
+
+    
     func siManagerStopped(_ manager: JoyGemSIManager, reason code: JoyGemRecordStopCode) {
         if (code == .actively) {
 //            YLprogressHUD.showAutoHiden("用户主动结束")
@@ -216,10 +221,12 @@ extension RecordTranslateVC : JoyGemSIManagerDelegate {
         debugPrint("code: \(code)")
     }
     
-    func siManager(_ manager: JoyGemSIManager, didUpdateAudioData data: Data) {
+//    func siManager(_ manager: JoyGemSIManager, didUpdateAudioData data: Data) {
+//        PcmSound.sharedInstance().feed(data)
+//    }
+    func siManager(_ manager: JoyGemSIManager, didUpdateAudioData data: Data, sampleRate: UInt) {
         PcmSound.sharedInstance().feed(data)
     }
-    
     func siManager(_ manager: JoyGemSIManager, onError code: JoyGemErrorCode) {
         
     }
index 6ea1f70..3b68c5e 100644 (file)
@@ -153,10 +153,14 @@ extension DeviceVM{
         }
     }
     static func getDN(finished:@escaping ((_ mac:String?)->Void)) {
+//        DispatchQueue.main.asyncAfter(deadline:.now() + 2) {
+//            print("延迟2秒后执行")
+//
+//        }
         JoyGemAIManager.shared().device.getDN { deviceName in
             finished(deviceName)
         } error: { error in
-            Logger.debug("\(error!.localizedDescription)")
+            Logger.debug("获取dn失败","\(error!.localizedDescription)")
             finished(nil)
         }
     }
index fcb749b..e751598 100644 (file)
@@ -12,7 +12,7 @@ class AppLaunch:NSObject{
     @MainActor static func start(){
 //        NFX.sharedInstance().start()
         
-            DCConfig.appLanguage = LanuageCache.get()
+        DCConfig.appLanguage = LanuageCache.get()
 
         
         IASignWithWeChat.shared.register()
@@ -22,7 +22,6 @@ class AppLaunch:NSObject{
         LoginVM.fetchCountryList {result in
 
         }
-        
 //        AppUpdateVM.shared.checkedUpdate()
     }
     
@@ -41,21 +40,5 @@ class AppLaunch:NSObject{
         else{
             App.shared.goToLogin()
         }
-        
-//        let list = DeviceCache.shared.getDeviceList()
-//        Logger.debug("缓存设备",list)
-//        if list.count > 0 {
-//            CacheManager.shared.currentDevice = list.first
-//            BleManager.shared.startScanDevice()
-//            App.shared.enter()
-//        }
-        
-//        let model = RecordDetailModel()
-//        model.type = .callingRecord
-//        model.origin = "深圳今天下雨吗?明天下雨吗,后天下雨吗,未来一周下雨吗"
-//        model.translate = ""
-//        model.path = ""
-//        model.name = ""
-//        App.shared.goToPage(RecordDetailVC(record: model))
     }
 }
index e38eefe..24c7179 100644 (file)
@@ -86,14 +86,16 @@ extension BleManager:CBCentralManagerDelegate{
     func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
         stopScanDevice()
         Logger.debug("蓝牙已连接 \(peripheral)")
-        DeviceCache.saveLastConnected(mac: connectPeripheral!.mac)
-        DeviceCache.shared.add(connectPeripheral!.device.identifier.uuidString, mac: connectPeripheral!.mac)
+
         peripheral.delegate = self
         peripheral.discoverServices([CBUUID(string: kJoyGemAIServiceUUID)]);
-        self.isConnetd = true
-        didConnectDeviceCallback?(connectPeripheral!)
-        NotificationManager.post(type: .deviceValidChange, objtect: nil)
-        DeviceVM.updateOtaVersion()
+        
+//        DeviceCache.saveLastConnected(mac: connectPeripheral!.mac)
+//        DeviceCache.shared.add(connectPeripheral!.device.identifier.uuidString, mac: connectPeripheral!.mac)
+//        self.isConnetd = true
+//        didConnectDeviceCallback?(connectPeripheral!)
+//        NotificationManager.post(type: .deviceValidChange, objtect: nil)
+//        DeviceVM.updateOtaVersion()
     }
     func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) {
         Logger.debug("peripheral: %@ failed to connect", peripheral)
@@ -245,6 +247,7 @@ extension BleManager : JoyGemAIManagerDelegate {
     }
     
     func joyGemAuthSuccessCallback() {
+        Logger.debug("鉴权成功")
         PcmSound.sharedInstance().setup(16000, withchannels: 1)
         let options: AVAudioSession.CategoryOptions = [.allowBluetooth]
         do {
@@ -253,6 +256,13 @@ extension BleManager : JoyGemAIManagerDelegate {
         } catch {
             Logger.debug(error)
         }
+        
+        DeviceCache.saveLastConnected(mac: connectPeripheral!.mac)
+        DeviceCache.shared.add(connectPeripheral!.device.identifier.uuidString, mac: connectPeripheral!.mac)
+        self.isConnetd = true
+        didConnectDeviceCallback?(connectPeripheral!)
+        NotificationManager.post(type: .deviceValidChange, objtect: nil)
+        DeviceVM.updateOtaVersion()
     }
     
     func joyGemAction(_ action: JoyGemActionMode) {
index 9271311..5f7cd03 100644 (file)
@@ -28,7 +28,6 @@ extension JoySdkManager {
         let fileManager = FileManager.default
         if let appSupportDirectory = fileManager.urls(for: .applicationSupportDirectory, in: .userDomainMask).first {
             let logDirectory = appSupportDirectory.appendingPathComponent("log")
-            
             // 检查 log 文件夹是否存在
             if !fileManager.fileExists(atPath: logDirectory.path) {
                 do {
index cf53ecd..6f9cfe4 100644 (file)
@@ -65,7 +65,9 @@ final class FileDownloadVC: BaseVC {
     
     override func setupData() {
         let lan = DCConfig.appLanguage.yonghuCode
-        let fileUrl = "https://dc-cloud-pub.tos-cn-guangzhou.volces.com/protocol/\(lan)/\(type.rawValue).pdf"
+//        let fileUrl = "https://dc-cloud-pub.tos-cn-guangzhou.volces.com/protocol/\(lan)/\(type.rawValue).pdf"
+        let fileUrl =  DCConfig.yonghuBaseURL + "/protocol/\(lan)/\(type.rawValue).pdf"
+        
         self.dowmload(url:fileUrl)
     }
     func dowmload(url:String){
index 93314c3..b5a2f12 100644 (file)
@@ -76,7 +76,7 @@ enum AiSupportLanguage:String{
         case .indonesian:
             return kJoyGemAILocalizationID
         case .russian:
-            return kJoyGemAILocalizationRU
+            return kJoyGemAILocalizationRU_Target
         case .malay:
             return kJoyGemAILocalizationMS
         }
index d0610e3..3ac9da4 100644 (file)
@@ -59,11 +59,11 @@ enum AppSupportLanguage{
     var lanCode:String{
         switch self {
         case .chinse:
-            return "zh-CN"
+            return "zh_CN"
         case .Traditional_Chinese:
-            return "zh-TW"
+            return "zh_TW"
         case .english:
-            return "en-US"
+            return "en_US"
         case .japanese:
             return "ja"
         case .korean: