From: liangzhenying <867174124@qq.com> Date: Thu, 13 Nov 2025 09:05:36 +0000 (+0800) Subject: 云端录音记录支持搜索 X-Git-Tag: 1.0.2~69 X-Git-Url: http://git.snicker.com.cn/?a=commitdiff_plain;h=4fa5a440945227637152e6ad505d3386e908f395;p=mirrors%2FShenYin.git 云端录音记录支持搜索 --- diff --git a/ShenYin/JoyGemAI.framework/Headers/JoyGemAI.h b/ShenYin/JoyGemAI.framework/Headers/JoyGemAI.h index 78d71ff..f16e0fe 100644 --- a/ShenYin/JoyGemAI.framework/Headers/JoyGemAI.h +++ b/ShenYin/JoyGemAI.framework/Headers/JoyGemAI.h @@ -25,3 +25,4 @@ FOUNDATION_EXPORT const unsigned char JoyGemAIVersionString[]; #import #import #import +#import diff --git a/ShenYin/JoyGemAI.framework/Headers/JoyGemCallManager.h b/ShenYin/JoyGemAI.framework/Headers/JoyGemCallManager.h index 86a2bac..5a4dd0f 100644 --- a/ShenYin/JoyGemAI.framework/Headers/JoyGemCallManager.h +++ b/ShenYin/JoyGemAI.framework/Headers/JoyGemCallManager.h @@ -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*)list timestamp:(NSTimeInterval)timestamp; + @end @protocol JoyGemCallStereoDelegate @@ -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*>*)lists timestamp:(NSTimeInterval)timestamp; + @end diff --git a/ShenYin/JoyGemAI.framework/Headers/JoyGemChatManager.h b/ShenYin/JoyGemAI.framework/Headers/JoyGemChatManager.h index b43fe4d..32c1303 100644 --- a/ShenYin/JoyGemAI.framework/Headers/JoyGemChatManager.h +++ b/ShenYin/JoyGemAI.framework/Headers/JoyGemChatManager.h @@ -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*))list; -///配置当前角色 +/// 配置当前角色 -(void)setSpeaker:(JoyGemSpeaker*)speaker; +/// 重置为默认角色 +-(void)resetSpearker; + +/// 进入英语口语陪练模式 +-(void)startSpoken; + +/// 退出英语口语陪练模式 +-(void)stopSpoken; + @end NS_ASSUME_NONNULL_END diff --git a/ShenYin/JoyGemAI.framework/Headers/JoyGemModel.h b/ShenYin/JoyGemAI.framework/Headers/JoyGemModel.h index bb4ae92..3a7cf57 100644 --- a/ShenYin/JoyGemAI.framework/Headers/JoyGemModel.h +++ b/ShenYin/JoyGemAI.framework/Headers/JoyGemModel.h @@ -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 diff --git a/ShenYin/JoyGemAI.framework/Headers/JoyGemParam.h b/ShenYin/JoyGemAI.framework/Headers/JoyGemParam.h index a0f4a81..b1f5ab6 100644 --- a/ShenYin/JoyGemAI.framework/Headers/JoyGemParam.h +++ b/ShenYin/JoyGemAI.framework/Headers/JoyGemParam.h @@ -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功能使用的蓝牙服务 diff --git a/ShenYin/JoyGemAI.framework/Headers/JoyGemRecordManager.h b/ShenYin/JoyGemAI.framework/Headers/JoyGemRecordManager.h index 99903a9..e95de63 100644 --- a/ShenYin/JoyGemAI.framework/Headers/JoyGemRecordManager.h +++ b/ShenYin/JoyGemAI.framework/Headers/JoyGemRecordManager.h @@ -12,6 +12,7 @@ NS_ASSUME_NONNULL_BEGIN @class JoyGemRecordManager; @class JoyGemAsrModel; +@class JoyGemRecordSpeaker; //现场录音的委托类 @protocol JoyGemRecordManagerDelegate @@ -48,6 +49,20 @@ NS_ASSUME_NONNULL_BEGIN @optional -(void)recordManagerCompleted:(JoyGemRecordManager*)manager; +/// 录音时的声波强度回调 +/// - Parameters: +/// - list: 强度数组 +/// - timestamp: 时间戳(ms) +@optional +-(void)recordManager:(JoyGemRecordManager*)manager intensity:(NSArray*)list timestamp:(NSTimeInterval)timestamp; + +/// 录音时的说话人区分 +/// - Parameters: +/// - list: 强度数组 +/// - timestamp: 时间戳(ms) +@optional +-(void)recordManager:(JoyGemRecordManager*)manager speakers:(NSArray*)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 diff --git a/ShenYin/JoyGemAI.framework/Headers/JoyGemSIDeviceManager.h b/ShenYin/JoyGemAI.framework/Headers/JoyGemSIDeviceManager.h index 00d2363..826b9c6 100644 --- a/ShenYin/JoyGemAI.framework/Headers/JoyGemSIDeviceManager.h +++ b/ShenYin/JoyGemAI.framework/Headers/JoyGemSIDeviceManager.h @@ -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: diff --git a/ShenYin/JoyGemAI.framework/Headers/JoyGemSIManager.h b/ShenYin/JoyGemAI.framework/Headers/JoyGemSIManager.h index 63d9ee0..bf64e1c 100644 --- a/ShenYin/JoyGemAI.framework/Headers/JoyGemSIManager.h +++ b/ShenYin/JoyGemAI.framework/Headers/JoyGemSIManager.h @@ -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 index 0000000..18f6bc5 --- /dev/null +++ b/ShenYin/JoyGemAI.framework/Headers/JoyGemSubtitleManager.h @@ -0,0 +1,79 @@ +// +// JoyGemSubtitleManager.h +// JoyGemAI +// +// Created by jjjs on 2025/10/17. +// + +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +@class JoyGemSubtitleManager; +@class JoyGemAsrModel; + +/// 字幕翻译的委托 +@protocol JoyGemSubtitleManagerDelegate + +///录音文字识别时 +/// - 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 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 diff --git a/ShenYin/JoyGemAI.framework/JoyGemAI b/ShenYin/JoyGemAI.framework/JoyGemAI index 39d3ea4..b8dcb56 100755 Binary files a/ShenYin/JoyGemAI.framework/JoyGemAI and b/ShenYin/JoyGemAI.framework/JoyGemAI differ diff --git a/ShenYin/JoyGemAI.framework/_CodeSignature/CodeResources b/ShenYin/JoyGemAI.framework/_CodeSignature/CodeResources index 0dc62d6..39407de 100644 --- a/ShenYin/JoyGemAI.framework/_CodeSignature/CodeResources +++ b/ShenYin/JoyGemAI.framework/_CodeSignature/CodeResources @@ -10,7 +10,7 @@ Headers/JoyGemAI.h - 8NnRIJGe/ATf7a6jdPAfly29UC4= + 7HO7XUA3NdXWlvtUWyiYdTaaZ4M= Headers/JoyGemAIManager.h @@ -18,11 +18,11 @@ Headers/JoyGemCallManager.h - BWJeSQyHYVE1TwiSgvXYhNdyJXI= + j3rnAsYkKzhbISebVqN00PCSeTk= Headers/JoyGemChatManager.h - I9SSrf6xzcSE2qKnMMmiSe9IV1I= + RmM4HQA4Ay7VLvTFWPw7s69sqBk= Headers/JoyGemDeviceManager.h @@ -34,23 +34,27 @@ Headers/JoyGemModel.h - ZQ5Ie1ErW3QF78XXxz/ydxsSQ5E= + WP0CE48Wvoq3FJX4ldz90bcNpTw= Headers/JoyGemParam.h - SzxTsmYo/XvsD3Uz2iqjnRDkRnA= + fq/eezzfuJFDMFtH+PXLOMYQQCs= Headers/JoyGemRecordManager.h - pKeMD6Xg3Br6ZOWB/BxyqAicCfc= + GzBtgXRPoBuyG0YwvFmoGA8Yg20= Headers/JoyGemSIDeviceManager.h - Cn0/thrZRr+pJ+skbuRbfJJirmA= + aH1rBy/dU7IVJkpw/BVW3BvfNtk= Headers/JoyGemSIManager.h - UiwORLtCgzF+QX/hXpK2w63Yx4Q= + 363bn6W4/w8F3xEYQrU0LEVD/XM= + + Headers/JoyGemSubtitleManager.h + + fDUdqGo5wSc78huSI4OUK8Qr8AU= Info.plist @@ -102,7 +106,7 @@ hash2 - nO8YFPNcoOIKUrpS9Pd4/T9V98f16ACbB7LFROxEPhQ= + AWVDZqJirRMWfVQc7FdxP1cjTTx2LWTSTwHDN0/ySRA= Headers/JoyGemAIManager.h @@ -116,14 +120,14 @@ hash2 - 4aqJC5wgayXjaD7XHafngtRL7PSKbqPdFujG27R7l4Q= + eKpRq8GaSD9YaI7Tfafc4BVLc/Vz2rS7cEcx8W6S+EI= Headers/JoyGemChatManager.h hash2 - v9CC1YyWez/AeMXpqpyouz+LNIkBqq147kBPoPYEVK8= + TrJNORF2yq5ZRf++PfcoxEwE0ic9W2VxQ+Nz7M7WKPM= Headers/JoyGemDeviceManager.h @@ -144,35 +148,42 @@ hash2 - rFoRM/fmnvDGWCISlMyWSioYrwvoetaKOU8WrgOCBNw= + 0kLzuK/HuYBvk9JPhWYbR2z7t1XSX83X5Am9ZD+vLXw= Headers/JoyGemParam.h hash2 - oXJjxfeaUmV4FnIP5DUB7MLED0QsP5hp0v0AqO8ttMs= + QKmLGG6IwVxT2QonEGJzea6rMbUX0PBNGoWpzqSNRbY= Headers/JoyGemRecordManager.h hash2 - C4rHebQlX6cKQWVmjh1cDetbe6cC+glcl2deSRF3r/o= + ep9EdHop9Qn0M9nCT7YwmHXxJh/XXCjBqtYjErKFd+8= Headers/JoyGemSIDeviceManager.h hash2 - bkQU7XO+s/wbDHlWf9VM2kvGxB5myT2n0bEp+ac6tHo= + x40L/ElpDJCMjpEBZZancy57toshn0DM6MOhkwnIMYQ= Headers/JoyGemSIManager.h hash2 - 5nObgPQx+QBZs8lbacCMnhBUJs629P/ZuVFJytUIV44= + NTKnVzoipmhtgLLzocc8J5kT4Q2qmUmVhZ3MgZUrbkA= + + + Headers/JoyGemSubtitleManager.h + + hash2 + + jGDopDnKVm0z/p6dEPEs54wWT6iwbetpqbTXTk8zoAQ= Modules/JoyGemAI.swiftmodule/Project/arm64-apple-ios.swiftsourceinfo diff --git a/ShenYin/ShenYin/Api/Config.swift b/ShenYin/ShenYin/Api/Config.swift index 14aec59..1a73781 100644 --- a/ShenYin/ShenYin/Api/Config.swift +++ b/ShenYin/ShenYin/Api/Config.swift @@ -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" + } diff --git a/ShenYin/ShenYin/Api/HttpClient2.swift b/ShenYin/ShenYin/Api/HttpClient2.swift index f445efd..850a5b0 100644 --- a/ShenYin/ShenYin/Api/HttpClient2.swift +++ b/ShenYin/ShenYin/Api/HttpClient2.swift @@ -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 index 0000000..a55920b --- /dev/null +++ b/ShenYin/ShenYin/Assets.xcassets/home/calendar_icon.imageset/Contents.json @@ -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/\346\227\245\345\216\206_(1)@2x.png" "b/ShenYin/ShenYin/Assets.xcassets/home/calendar_icon.imageset/\346\227\245\345\216\206_(1)@2x.png" new file mode 100644 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/\346\227\245\345\216\206_(1)@3x.png" "b/ShenYin/ShenYin/Assets.xcassets/home/calendar_icon.imageset/\346\227\245\345\216\206_(1)@3x.png" new file mode 100644 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 diff --git a/ShenYin/ShenYin/UI/Home/Chinese2English/Chinese2EnglishVC.swift b/ShenYin/ShenYin/UI/Home/Chinese2English/Chinese2EnglishVC.swift index d76fb7c..eb10389 100644 --- a/ShenYin/ShenYin/UI/Home/Chinese2English/Chinese2EnglishVC.swift +++ b/ShenYin/ShenYin/UI/Home/Chinese2English/Chinese2EnglishVC.swift @@ -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) { diff --git a/ShenYin/ShenYin/UI/Home/Dialog/DialogTranlateVC.swift b/ShenYin/ShenYin/UI/Home/Dialog/DialogTranlateVC.swift index b04aa71..64037e3 100644 --- a/ShenYin/ShenYin/UI/Home/Dialog/DialogTranlateVC.swift +++ b/ShenYin/ShenYin/UI/Home/Dialog/DialogTranlateVC.swift @@ -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) { diff --git a/ShenYin/ShenYin/UI/Home/HistoryRecord/HistoryRecordCell.swift b/ShenYin/ShenYin/UI/Home/HistoryRecord/HistoryRecordCell.swift index b3052f4..bfa218e 100644 --- a/ShenYin/ShenYin/UI/Home/HistoryRecord/HistoryRecordCell.swift +++ b/ShenYin/ShenYin/UI/Home/HistoryRecord/HistoryRecordCell.swift @@ -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) diff --git a/ShenYin/ShenYin/UI/Home/HistoryRecord/RecordSearchBar.swift b/ShenYin/ShenYin/UI/Home/HistoryRecord/RecordSearchBar.swift index b945972..0ccaadc 100644 --- a/ShenYin/ShenYin/UI/Home/HistoryRecord/RecordSearchBar.swift +++ b/ShenYin/ShenYin/UI/Home/HistoryRecord/RecordSearchBar.swift @@ -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 { diff --git a/ShenYin/ShenYin/UI/Home/HistoryRecord/VC/CloudRecordVC.swift b/ShenYin/ShenYin/UI/Home/HistoryRecord/VC/CloudRecordVC.swift index cdc1281..97a9650 100644 --- a/ShenYin/ShenYin/UI/Home/HistoryRecord/VC/CloudRecordVC.swift +++ b/ShenYin/ShenYin/UI/Home/HistoryRecord/VC/CloudRecordVC.swift @@ -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 index 0000000..25f2925 --- /dev/null +++ b/ShenYin/ShenYin/UI/Home/HistoryRecord/VC/CloudSearchVC.swift @@ -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() + } + + +} diff --git a/ShenYin/ShenYin/UI/Home/HistoryRecord/VC/HistoryRecordVC.swift b/ShenYin/ShenYin/UI/Home/HistoryRecord/VC/HistoryRecordVC.swift index 4835d11..2084ecb 100644 --- a/ShenYin/ShenYin/UI/Home/HistoryRecord/VC/HistoryRecordVC.swift +++ b/ShenYin/ShenYin/UI/Home/HistoryRecord/VC/HistoryRecordVC.swift @@ -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(){ diff --git a/ShenYin/ShenYin/UI/Home/HistoryRecord/VC/HistorySearchVC.swift b/ShenYin/ShenYin/UI/Home/HistoryRecord/VC/HistorySearchVC.swift index 72f1d37..0230635 100644 --- a/ShenYin/ShenYin/UI/Home/HistoryRecord/VC/HistorySearchVC.swift +++ b/ShenYin/ShenYin/UI/Home/HistoryRecord/VC/HistorySearchVC.swift @@ -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) { diff --git a/ShenYin/ShenYin/UI/Home/RecordDetail/RecordDetaiContent.swift b/ShenYin/ShenYin/UI/Home/RecordDetail/RecordDetaiContent.swift index 7ffa28d..9d278f0 100644 --- a/ShenYin/ShenYin/UI/Home/RecordDetail/RecordDetaiContent.swift +++ b/ShenYin/ShenYin/UI/Home/RecordDetail/RecordDetaiContent.swift @@ -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 diff --git a/ShenYin/ShenYin/UI/Home/RecordTranslateVC.swift b/ShenYin/ShenYin/UI/Home/RecordTranslateVC.swift index 4490938..5049bb6 100644 --- a/ShenYin/ShenYin/UI/Home/RecordTranslateVC.swift +++ b/ShenYin/ShenYin/UI/Home/RecordTranslateVC.swift @@ -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) { } diff --git a/ShenYin/ShenYin/UI/Home/Scan/DeviceVM.swift b/ShenYin/ShenYin/UI/Home/Scan/DeviceVM.swift index 6ea1f70..3b68c5e 100644 --- a/ShenYin/ShenYin/UI/Home/Scan/DeviceVM.swift +++ b/ShenYin/ShenYin/UI/Home/Scan/DeviceVM.swift @@ -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) } } diff --git a/ShenYin/ShenYin/UI/Launch/AppLaunch.swift b/ShenYin/ShenYin/UI/Launch/AppLaunch.swift index fcb749b..e751598 100644 --- a/ShenYin/ShenYin/UI/Launch/AppLaunch.swift +++ b/ShenYin/ShenYin/UI/Launch/AppLaunch.swift @@ -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)) } } diff --git a/ShenYin/ShenYin/UI/Launch/BleManager.swift b/ShenYin/ShenYin/UI/Launch/BleManager.swift index e38eefe..24c7179 100644 --- a/ShenYin/ShenYin/UI/Launch/BleManager.swift +++ b/ShenYin/ShenYin/UI/Launch/BleManager.swift @@ -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) { diff --git a/ShenYin/ShenYin/UI/Launch/JoySdkManager.swift b/ShenYin/ShenYin/UI/Launch/JoySdkManager.swift index 9271311..5f7cd03 100644 --- a/ShenYin/ShenYin/UI/Launch/JoySdkManager.swift +++ b/ShenYin/ShenYin/UI/Launch/JoySdkManager.swift @@ -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 { diff --git a/ShenYin/ShenYin/UI/Mine/ProtocolVC.swift b/ShenYin/ShenYin/UI/Mine/ProtocolVC.swift index cf53ecd..6f9cfe4 100644 --- a/ShenYin/ShenYin/UI/Mine/ProtocolVC.swift +++ b/ShenYin/ShenYin/UI/Mine/ProtocolVC.swift @@ -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){ diff --git a/ShenYin/ShenYin/lan/AiSupportLanguage.swift b/ShenYin/ShenYin/lan/AiSupportLanguage.swift index 93314c3..b5a2f12 100644 --- a/ShenYin/ShenYin/lan/AiSupportLanguage.swift +++ b/ShenYin/ShenYin/lan/AiSupportLanguage.swift @@ -76,7 +76,7 @@ enum AiSupportLanguage:String{ case .indonesian: return kJoyGemAILocalizationID case .russian: - return kJoyGemAILocalizationRU + return kJoyGemAILocalizationRU_Target case .malay: return kJoyGemAILocalizationMS } diff --git a/ShenYin/ShenYin/lan/LanConfig.swift b/ShenYin/ShenYin/lan/LanConfig.swift index d0610e3..3ac9da4 100644 --- a/ShenYin/ShenYin/lan/LanConfig.swift +++ b/ShenYin/ShenYin/lan/LanConfig.swift @@ -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: