From: liangzhenying <867174124@qq.com> Date: Mon, 29 Dec 2025 08:57:08 +0000 (+0800) Subject: 骨传导完善 X-Git-Tag: 1.0.2~15 X-Git-Url: http://git.snicker.com.cn/?a=commitdiff_plain;h=714c3e8bba533939048d0c38541b6130293e6fd9;p=mirrors%2FShenYin.git 骨传导完善 --- diff --git a/DeepChordsSdk_iOS/DeepChordsSdk/Example/DeepChordsSdk/DCBleManager.swift b/DeepChordsSdk_iOS/DeepChordsSdk/Example/DeepChordsSdk/DCBleManager.swift index 15b9722..21420f4 100644 --- a/DeepChordsSdk_iOS/DeepChordsSdk/Example/DeepChordsSdk/DCBleManager.swift +++ b/DeepChordsSdk_iOS/DeepChordsSdk/Example/DeepChordsSdk/DCBleManager.swift @@ -125,13 +125,13 @@ extension DCBleManager{ DCLogger.debug("打开AI对话") case 0x02: DCLogger.debug("关闭AI对话") - DCChat.shared.stopChat() +// DCChat.shared.stopChat() case 0x03: DCLogger.debug("打开耳机录音") - DCBleRecord.shared.start() +// DCBleRecord.shared.start(type: 0x00) case 0x04: DCLogger.debug("关闭耳机录音") - DCBleRecord.shared.stop() +// DCBleRecord.shared.stop() default: break } diff --git a/DeepChordsSdk_iOS/DeepChordsSdk/Example/DeepChordsSdk/DCBleRecord.swift b/DeepChordsSdk_iOS/DeepChordsSdk/Example/DeepChordsSdk/DCBleRecord.swift index 48ef04b..55820d3 100644 --- a/DeepChordsSdk_iOS/DeepChordsSdk/Example/DeepChordsSdk/DCBleRecord.swift +++ b/DeepChordsSdk_iOS/DeepChordsSdk/Example/DeepChordsSdk/DCBleRecord.swift @@ -94,8 +94,8 @@ public class DCBleRecord { } } - - public func start(){ + /// 0x00实时录音,0x0c通话录音 + public func start(type:Int){ asrList = [] asrText = "" @@ -104,8 +104,7 @@ public class DCBleRecord { setupRecord() let config = StarburstBleAsrConfig() - config.type = 0x0c/// 0x00实时录音,0x0c通话录音 - //开始录音 + config.type = type StarburstSdk.shared().startDeviceRecord(config) } diff --git a/DeepChordsSdk_iOS/DeepChordsSdk/Example/DeepChordsSdk/DCChat.swift b/DeepChordsSdk_iOS/DeepChordsSdk/Example/DeepChordsSdk/DCChat.swift index 34b409e..fdc5c88 100644 --- a/DeepChordsSdk_iOS/DeepChordsSdk/Example/DeepChordsSdk/DCChat.swift +++ b/DeepChordsSdk_iOS/DeepChordsSdk/Example/DeepChordsSdk/DCChat.swift @@ -16,6 +16,17 @@ public class DCRole{ public var type:String = "" /// 角色 public var role:String = "" + + public init(){ + + } + + public init(sid: String, name: String, type: String, role: String) { + self.sid = sid + self.name = name + self.type = type + self.role = role + } } public class DCChat{ diff --git a/DeepChordsSdk_iOS/DeepChordsSdk/Example/DeepChordsSdk/DCChinse2En.swift b/DeepChordsSdk_iOS/DeepChordsSdk/Example/DeepChordsSdk/DCChinse2En.swift new file mode 100644 index 0000000..ba8b359 --- /dev/null +++ b/DeepChordsSdk_iOS/DeepChordsSdk/Example/DeepChordsSdk/DCChinse2En.swift @@ -0,0 +1,87 @@ +// +// Untitled.swift +// Example +// +// Created by liangshi on 2025/12/29. +// + +import StarburstSdk + +public class DCChinse2En { + public static let shared = DCChinse2En() + /// 语⾔编码,默认为普通话(zh-CN),auto_lang自动识别 + public var fromLanguage:String = "zh-CN" + /// ⽬标语⾔编码,默认为英语(en-US),auto_lang自动识别 + public var toLanguage:String = "en-US" + /// 是否翻译,默认false。如果为true翻译源语言和目标语言不能是同一种语言 + public var isTranslate = false + /// 是否进⾏Tts,默认false + public var isTTs = false + /// 默认pcm ⽀持pcm / ogg_opus / mp3 + public var ttsFormat = "pcm" + /// 识别回调 + public var asrCallback:((String,Int)->Void)? + /// 翻译回调 + public var translateCallback:((String,Int)->Void)? + /// 翻译语音回调 + public var translateDataCallback:((Data)->Void)? + /// 状态码回调 + public var codeCallback:((Int)->Void)? + /// 打断 + public var interruptionCallBack:(()->Void)? + /// 录音文件回调 + public var filePathCallBack:((String?)->Void)? + + var asrList : [(String,Int)] = [] + var asrText = "" + + var translateList : [(String,Int)] = [] + var translateText = "" + + public func start(){ + let config = StarburstAsrConfig() + config.isTranslate = isTranslate + config.fromLanguage = fromLanguage + config.toLanguage = toLanguage + config.isTTs = isTTs + config.ttsFormat = ttsFormat + asrList = [] + asrText = "" + + translateList = [] + translateText = "" + + StarburstSdk.shared().startRecordAsr(config) {voiceData in + //DCLogger.debug("录音的音频流 = \(voiceData!)") + } recognizeText: {[weak self] model in + guard let self = self else {return} + guard let model = model else {return} + + if let text = model.textResult,!text.isEmpty{ +// DCLogger.debug("asr = \(text)") + asrCallback?(text,model.definiteIndex) + } + if let translate = model.translateResult,let text = translate.text,!text.isEmpty{ +// DCLogger.debug("translate = \(text)") + translateCallback?(text,Int(translate.definiteIndex)) + } + if let ttsData = model.ttsResult?.streamPiece { + if let data = Data(base64Encoded: ttsData){ + translateDataCallback?(data) + } + } + } stateChange: {[weak self] code in + DCLogger.debug("状态改变",code) + self?.codeCallback?(code.rawValue) + } interruption: { [weak self] in + self?.interruptionCallBack?() + } + } + public func stop(){ + StarburstSdk.shared().stopRecordAsr {[weak self] filePath in + DCLogger.debug("文件录音保存路径 \(filePath ?? "")") + self?.filePathCallBack?(filePath) + } + + } +} diff --git a/DeepChordsSdk_iOS/DeepChordsSdk/Example/DeepChordsSdk/DCPhoneRecord.swift b/DeepChordsSdk_iOS/DeepChordsSdk/Example/DeepChordsSdk/DCPhoneRecord.swift index 0d37199..6846441 100644 --- a/DeepChordsSdk_iOS/DeepChordsSdk/Example/DeepChordsSdk/DCPhoneRecord.swift +++ b/DeepChordsSdk_iOS/DeepChordsSdk/Example/DeepChordsSdk/DCPhoneRecord.swift @@ -17,11 +17,13 @@ public class DCPhoneRecord { /// 是否进⾏Tts,默认false public var isTTs = false /// 默认pcm ⽀持pcm / ogg_opus / mp3 - public var ttsFormat = "mp3" + public var ttsFormat = "pcm" /// 识别回调 public var asrCallback:((String)->Void)? - /// 识别回调 + /// 翻译回调 public var translateCallback:((String)->Void)? + /// 翻译语音回调 + public var translateDataCallback:((Data)->Void)? /// 状态码回调 public var codeCallback:((Int)->Void)? /// 打断 @@ -86,6 +88,11 @@ public class DCPhoneRecord { translateCallback?(translateText) } + if let ttsData = model.ttsResult?.streamPiece { + if let data = Data(base64Encoded: ttsData){ + translateDataCallback?(data) + } + } } stateChange: {[weak self] code in DCLogger.debug("状态改变",code) self?.codeCallback?(code.rawValue) diff --git a/DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/SceneDelegate.swift b/DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/SceneDelegate.swift index 5c01cf8..17a75bf 100644 --- a/DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/SceneDelegate.swift +++ b/DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/SceneDelegate.swift @@ -19,7 +19,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { guard let _ = (scene as? UIWindowScene) else { return } App.shared.window = window window?.rootViewController = UINavigationController(rootViewController: DeviceListVC()) - DCSdkManager.shared.initSdk() + DcSdkManager.shared.initSdk() } func sceneDidDisconnect(_ scene: UIScene) { diff --git a/DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/VC/ChatVC.swift b/DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/VC/ChatVC.swift index 096d47c..d7985be 100644 --- a/DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/VC/ChatVC.swift +++ b/DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/VC/ChatVC.swift @@ -55,14 +55,14 @@ class ChatVC: BaseVC { } } func setupSDk(){ - DCChatManager.shared.chatNeedReloadData = {[weak self] in + DcChatManager.shared.chatNeedReloadData = {[weak self] in guard let self = self else{return} tableView.reloadData() - if DCChatManager.shared.dataList.count > 0 { - tableView.scrollToRow(at: IndexPath(row: DCChatManager.shared.dataList.count - 1, section: 0), at: .none, animated: true) + if DcChatManager.shared.dataList.count > 0 { + tableView.scrollToRow(at: IndexPath(row: DcChatManager.shared.dataList.count - 1, section: 0), at: .none, animated: true) } } - DCChatManager.shared.codeCallback = { [weak self] code in + DcChatManager.shared.codeCallback = { [weak self] code in guard let self = self else{return} if code == 20001 { self.recordBtn.isSelected = true @@ -72,7 +72,7 @@ class ChatVC: BaseVC { } } - DCChatManager.shared.finishCallback = { [weak self] path in + DcChatManager.shared.finishCallback = { [weak self] path in guard let self = self else{return} // self.recordBtn.isSelected = false } @@ -83,19 +83,19 @@ class ChatVC: BaseVC { @objc func recordAction() { self.recordBtn.isSelected = !self.recordBtn.isSelected if self.recordBtn.isSelected { - DCChatManager.shared.startChat() + DcChatManager.shared.startChat() } else{ stopAction() } } func stopAction() { - DCChatManager.shared.stopChat() + DcChatManager.shared.stopChat() } @objc func roleAction(){ let vc = RoleVC() vc.didSelectedCallback = { role in - DCChatManager.shared.role = role + DcChatManager.shared.role = role } navigationController?.pushViewController(vc, animated: true) } @@ -103,12 +103,12 @@ class ChatVC: BaseVC { extension ChatVC:UITableViewDelegate,UITableViewDataSource { func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - return DCChatManager.shared.dataList.count + return DcChatManager.shared.dataList.count } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: ChatCell.reuseID, for: indexPath) as? ChatCell - cell!.data = DCChatManager.shared.dataList[indexPath.row] + cell!.data = DcChatManager.shared.dataList[indexPath.row] return cell! } func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { diff --git a/DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/VC/EarPhoneRecordVC.swift b/DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/VC/EarPhoneRecordVC.swift index ce25d9e..6890241 100644 --- a/DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/VC/EarPhoneRecordVC.swift +++ b/DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/VC/EarPhoneRecordVC.swift @@ -134,7 +134,7 @@ extension EarPhoneRecordVC{ DCBleRecord.shared.fromLanguage = fromLanguage DCBleRecord.shared.toLanguage = toLanguage DCBleRecord.shared.isTranslate = true - DCBleRecord.shared.start() + DCBleRecord.shared.start(type: 0x00) } func stopAction(){ diff --git a/DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/tools/DCChatManager.swift b/DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/tools/DCChatManager.swift index e2443d0..3e5b65c 100644 --- a/DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/tools/DCChatManager.swift +++ b/DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/tools/DCChatManager.swift @@ -1,5 +1,5 @@ // -// DCChatManager.swift +// DcChatManager.swift // Example // // Created by liangshi on 2025/12/15. @@ -41,8 +41,8 @@ public class ChatCellModel:NSObject,NSCoding { } -class DCChatManager:NSObject{ - static let shared = DCChatManager() +class DcChatManager:NSObject{ + static let shared = DcChatManager() private var isTtsPlaying = false private var audioData: Data? @@ -176,7 +176,7 @@ class DCChatManager:NSObject{ // } // } } -extension DCChatManager : PcmSoundDelegate { +extension DcChatManager : PcmSoundDelegate { func remaining(_ frames: UInt) { DCLogger.debug("[frame:\(frames)]"); if (frames == 0) { diff --git a/DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/tools/DCSdkManager.swift b/DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/tools/DCSdkManager.swift index 0e39f84..3a82873 100644 --- a/DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/tools/DCSdkManager.swift +++ b/DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/tools/DCSdkManager.swift @@ -1,19 +1,18 @@ // -// DCSdkManager.swift +// DcSdkManager.swift // Example // // Created by liangshi on 2025/12/19. // import DeepChordsSdk -class DCSdkManager{ - static let shared = DCSdkManager() +class DcSdkManager{ + static let shared = DcSdkManager() func initSdk(){ DeepChordsAiSdk.shared.initSDK() -// DCRecordManager.shared.setupRecord() - DCChatManager.shared.setupChat() + DcChatManager.shared.setupChat() DCDevice.shared.eventCallback = { event in switch event { diff --git a/ShenYin/ShenYin.xcodeproj/project.pbxproj b/ShenYin/ShenYin.xcodeproj/project.pbxproj index 1ea9a02..a2b618a 100644 --- a/ShenYin/ShenYin.xcodeproj/project.pbxproj +++ b/ShenYin/ShenYin.xcodeproj/project.pbxproj @@ -274,8 +274,10 @@ GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = ShenYin/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = DeepChords; + INFOPLIST_KEY_ITSAppUsesNonExemptEncryption = NO; INFOPLIST_KEY_NSBluetoothAlwaysUsageDescription = "扫描附近蓝牙设备需要获得权限"; INFOPLIST_KEY_NSCameraUsageDescription = "修改用户头像、提交意见反馈需要访问相册使用摄像头"; + INFOPLIST_KEY_NSLocationWhenInUseUsageDescription = "允许APP使用定位方便查找耳机"; INFOPLIST_KEY_NSMicrophoneUsageDescription = "录音需要使用麦克风"; INFOPLIST_KEY_NSPhotoLibraryUsageDescription = "修改用户头像、提交意见反馈需要访问相册"; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; @@ -283,13 +285,14 @@ INFOPLIST_KEY_UIMainStoryboardFile = Main; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = UIInterfaceOrientationPortrait; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = UIInterfaceOrientationPortrait; + INFOPLIST_KEY_UIUserInterfaceStyle = Light; IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); MARKETING_VERSION = 1.0.2; - PRODUCT_BUNDLE_IDENTIFIER = com.deepchords.app; + PRODUCT_BUNDLE_IDENTIFIER = com.deepchords.app1; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; @@ -324,8 +327,10 @@ GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = ShenYin/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = DeepChords; + INFOPLIST_KEY_ITSAppUsesNonExemptEncryption = NO; INFOPLIST_KEY_NSBluetoothAlwaysUsageDescription = "扫描附近蓝牙设备需要获得权限"; INFOPLIST_KEY_NSCameraUsageDescription = "修改用户头像、提交意见反馈需要访问相册使用摄像头"; + INFOPLIST_KEY_NSLocationWhenInUseUsageDescription = "允许APP使用定位方便查找耳机"; INFOPLIST_KEY_NSMicrophoneUsageDescription = "录音需要使用麦克风"; INFOPLIST_KEY_NSPhotoLibraryUsageDescription = "修改用户头像、提交意见反馈需要访问相册"; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; @@ -333,13 +338,14 @@ INFOPLIST_KEY_UIMainStoryboardFile = Main; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = UIInterfaceOrientationPortrait; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = UIInterfaceOrientationPortrait; + INFOPLIST_KEY_UIUserInterfaceStyle = Light; IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); MARKETING_VERSION = 1.0.2; - PRODUCT_BUNDLE_IDENTIFIER = com.deepchords.app; + PRODUCT_BUNDLE_IDENTIFIER = com.deepchords.app1; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; diff --git a/ShenYin/ShenYin/Info.plist b/ShenYin/ShenYin/Info.plist index 16b05a5..82a0f3a 100644 --- a/ShenYin/ShenYin/Info.plist +++ b/ShenYin/ShenYin/Info.plist @@ -51,11 +51,5 @@ bluetooth-central audio - NSLocationWhenInUseUsageDescription - 允许APP使用定位方便查找耳机 - ITSAppUsesNonExemptEncryption - - UIUserInterfaceStyle - Light diff --git a/ShenYin/ShenYin/Launch/AppLaunch.swift b/ShenYin/ShenYin/Launch/AppLaunch.swift index 3d52d87..a4b1540 100644 --- a/ShenYin/ShenYin/Launch/AppLaunch.swift +++ b/ShenYin/ShenYin/Launch/AppLaunch.swift @@ -15,7 +15,7 @@ class AppLaunch:NSObject{ IASignWithWeChat.shared.register() - + CCCall.shared.start() setupKeyboard() loadCache() LoginVM.fetchCountryList {result in @@ -39,6 +39,5 @@ class AppLaunch:NSObject{ else{ App.shared.goToLogin() } -// App.shared.window?.rootViewController = ChatVC() } } diff --git a/ShenYin/ShenYin/Launch/AppManager.swift b/ShenYin/ShenYin/Launch/AppManager.swift index f86626e..1f3ae04 100644 --- a/ShenYin/ShenYin/Launch/AppManager.swift +++ b/ShenYin/ShenYin/Launch/AppManager.swift @@ -9,8 +9,6 @@ class App{ static let shared = App() var window:UIWindow? var isInBackground = false - var isJoy = true - func topVC()->UIViewController!{ guard let vc = window?.rootViewController else{ @@ -19,20 +17,7 @@ class App{ } return getTopViewController(from: vc) } - func preparePlay(){ - PcmSound.sharedInstance().stopAudioUnit() - PcmSound.sharedInstance().cleanup() - PcmSound.sharedInstance().setup(16000, withchannels: 1) - - let options: AVAudioSession.CategoryOptions = [.allowBluetooth] - do { - try AVAudioSession.sharedInstance().setCategory(.playback, options: [options]) - try AVAudioSession.sharedInstance().setActive(true) - } catch { - Logger.debug(error) - } - } - + func willLogin(){ CacheManager.shared.user.tokenIsValid {[weak self] success in guard let self = self else{return} @@ -46,11 +31,11 @@ class App{ Logger.debug(DCFolder.user) Logger.debug(DCFolder.record) self.window?.rootViewController = BaseTC() - if App.shared.isJoy { + if CCAiManager.isJoy { JoySdkManager.shared.setupSdk() } else{ - DCSdkManager.shared.initSdk() + DcSdkManager.shared.initSdk() } } func goToLogin(){ diff --git a/ShenYin/ShenYin/Sdk/CCAiManager.swift b/ShenYin/ShenYin/Sdk/CCAiManager.swift new file mode 100644 index 0000000..518bb2d --- /dev/null +++ b/ShenYin/ShenYin/Sdk/CCAiManager.swift @@ -0,0 +1,62 @@ +// +// CCAiManager.swift +// ShenYin +// +// Created by liangshi on 2025/12/25. +// +import JoyGemAI +import DeepChordsSdk + +class CCAiManager { + static var isJoy = true + + static func translate(text:String,from:String,to:String,finished:@escaping ((stream:String,finish:Bool)?)->Void){ + + if isJoy { + JoyGemAIManager.shared().translate(text, from: from, to: to) { result, finish in + finished((result,finish)) + } error: { error in + finished(nil) + } + } + else{ + + } + } +} +extension CCAiManager{ + static func preparePlayBlueTooth(){ + PcmSound.sharedInstance().stopAudioUnit() + PcmSound.sharedInstance().cleanup() + PcmSound.sharedInstance().setup(16000, withchannels: 1) + + let options: AVAudioSession.CategoryOptions = [.allowBluetooth] + do { + try AVAudioSession.sharedInstance().setCategory(.playback, options: [options]) + try AVAudioSession.sharedInstance().setActive(true) + } catch { + Logger.debug(error) + } + } +// static func preparePlay(){ +// PcmSound.sharedInstance().stopAudioUnit() +// PcmSound.sharedInstance().cleanup() +// PcmSound.sharedInstance().setup(16000, withchannels: 1) +// +// do { +// try AVAudioSession.sharedInstance().setCategory(.playback) +// try AVAudioSession.sharedInstance().setActive(true) +// } catch { +// Logger.debug(error) +// } +// } + + static func stopPlay(){ + PcmSound.sharedInstance().stopAudioUnit() + PcmSound.sharedInstance().cleanup() + } +} + + + + diff --git a/ShenYin/ShenYin/Sdk/CCBleManager.swift b/ShenYin/ShenYin/Sdk/CCBleManager.swift index 4e13a15..1f62adc 100644 --- a/ShenYin/ShenYin/Sdk/CCBleManager.swift +++ b/ShenYin/ShenYin/Sdk/CCBleManager.swift @@ -71,13 +71,13 @@ extension CCBleManager:CBCentralManagerDelegate{ } Logger.debug(peripheral) - if App.shared.isJoy { + if CCAiManager.isJoy{ let mac = JoySdkManager.getMac(advertisementData) ?? "" devices.append(DCPeripheral(device: peripheral,mac: mac)) discoverDeviceCallback?(devices) } else{ - let mac = DCSdkManager.getMac(advertisementData) ?? "" + let mac = DcSdkManager.getMac(advertisementData) ?? "" devices.append(DCPeripheral(device: peripheral,mac: mac)) discoverDeviceCallback?(devices) } @@ -87,7 +87,7 @@ extension CCBleManager:CBCentralManagerDelegate{ stopScanDevice() Logger.debug("蓝牙已连接 \(peripheral)") peripheral.delegate = self - if App.shared.isJoy { + if CCAiManager.isJoy{ peripheral.discoverServices([CBUUID(string: JoySdkManager.serviceUUID),CBUUID(string: OtaMananger.otaServiceUUId)]); } else{ @@ -101,8 +101,9 @@ extension CCBleManager:CBCentralManagerDelegate{ } func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) { Logger.debug("蓝牙已断开 \(peripheral)") + YLprogressHUD.showAutoHiden("蓝牙已断开".l10n) self.isConnetd = false - if App.shared.isJoy { + if CCAiManager.isJoy{ DCBleManager.shared.centralManager(central, didDisconnectPeripheral: peripheral, error: nil) } NotificationManager.post(type: .deviceValidChange, objtect: nil) @@ -121,7 +122,7 @@ extension CCBleManager: CBPeripheralDelegate { } func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) { - if App.shared.isJoy { + if CCAiManager.isJoy{ if (service.uuid.uuidString == JoySdkManager.serviceUUID) { JoySdkManager.shared.peripheral(peripheral, didDiscoverCharacteristicsFor: service, error: error) } @@ -135,7 +136,7 @@ extension CCBleManager: CBPeripheralDelegate { } func peripheral(_ peripheral: CBPeripheral, didUpdateNotificationStateFor characteristic: CBCharacteristic, error: (any Error)?) { - if App.shared.isJoy { + if CCAiManager.isJoy{ if (characteristic.isNotifying == true && characteristic.service?.uuid.uuidString == JoySdkManager.serviceUUID && characteristic.uuid.uuidString == JoySdkManager.notifyUUID) { Logger.debug("特征使能成功") JoySdkManager.shared.bleConnected() @@ -148,7 +149,7 @@ extension CCBleManager: CBPeripheralDelegate { func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) { // Logger.debug("收到数据 ===uUId ",characteristic.uuid) - if App.shared.isJoy { + if CCAiManager.isJoy{ if OtaMananger.isOtadata(characteristic){ if let _ = characteristic.value,error == nil { OtaMananger.shared.otaManager.peripheral(peripheral, didUpdateValueFor: characteristic) @@ -189,7 +190,7 @@ extension CCBleManager{ // devices.append(DCPeripheral(device: item,mac: mac)) // } // } - if App.shared.isJoy { + if CCAiManager.isJoy{ centralManager.scanForPeripherals(withServices: [CBUUID(string: JoySdkManager.serviceUUID)],options: [CBCentralManagerScanOptionAllowDuplicatesKey : false]) } else{ diff --git a/ShenYin/ShenYin/Sdk/CCCall.swift b/ShenYin/ShenYin/Sdk/CCCall.swift new file mode 100644 index 0000000..fc9d089 --- /dev/null +++ b/ShenYin/ShenYin/Sdk/CCCall.swift @@ -0,0 +1,54 @@ +// +// CCCall.swift +// ShenYin +// +// Created by liangshi on 2025/12/29. +// +import CallKit +import CoreTelephony + +class CCCall: NSObject, CXCallObserverDelegate { + static let shared = CCCall() + + let callObserver = CXCallObserver() + var isInCall = false + + override init() { + super.init() + checkInitialCallState() + start() + } + + func start() { + callObserver.setDelegate(self, queue: nil) + } + + func callObserver(_ callObserver: CXCallObserver, callChanged call: CXCall) { + if call.hasEnded { + print("通话结束") + isInCall = false + } else if call.isOnHold { + print("通话保持中") + isInCall = true + } else if call.isOutgoing { + print("拨出电话") + isInCall = true + } else if call.hasConnected { + print("通话已接通") + isInCall = true + } + } + + private func checkInitialCallState() { + let callCenter = CTCallCenter() + if let calls = callCenter.currentCalls { + for call in calls { + if call.callState == CTCallStateDialing || call.callState == CTCallStateIncoming || call.callState == CTCallStateConnected { + print("正在听话。。。") + isInCall = true + return + } + } + } + } +} diff --git a/ShenYin/ShenYin/Sdk/CCPage.swift b/ShenYin/ShenYin/Sdk/CCPage.swift new file mode 100644 index 0000000..75cc017 --- /dev/null +++ b/ShenYin/ShenYin/Sdk/CCPage.swift @@ -0,0 +1,67 @@ +// +// CCVCManager.swift +// ShenYin +// +// Created by liangshi on 2025/12/26. +// + +class CCPage{ + static func gotoChat(){ + if CCAiManager.isJoy { + App.shared.goToPage(JoyChatVC()) + } + else{ + App.shared.goToPage(DcChatVC()) + } + } + static func gotoFieldRecord(){ + if CCAiManager.isJoy { + App.shared.goToPage(JoyXclyVC()) + } + else{ + App.shared.goToPage(DcXclyVC()) + } + } + static func gotoCallRecord(){ + if CCAiManager.isJoy { + if JoySdkManager.shared.callRecord.supportStereo{ + App.shared.goToPage(JoyStereoCallVC()) + } + else{ + App.shared.goToPage(JoyCallingRecordVC()) + } + } + else{ +// if JoySdkManager.shared.callRecord.supportStereo{ +// App.shared.goToPage(JoyStereoCallVC()) +// } +// else{ + App.shared.goToPage(DcCallingRecordVC()) +// } + } + } + static func gotoChinese2Enlgish(){ + if CCAiManager.isJoy { + App.shared.goToPage(JoyChinese2EnglishVC()) + } + else{ + App.shared.goToPage(DcChinese2EnglishVC()) + } + } + static func gotoTcty(){ + if CCAiManager.isJoy { + App.shared.goToPage(JoyTctyVC()) + } + else{ + App.shared.goToPage(DcTctyVC()) + } + } + static func gotoDialogTranslate(){ + if CCAiManager.isJoy { + App.shared.goToPage(JoyDialogTranslateVC()) + } + else{ + App.shared.goToPage(DcDialogTranslateVC()) + } + } +} diff --git a/ShenYin/ShenYin/Sdk/CCRole.swift b/ShenYin/ShenYin/Sdk/CCRole.swift index 953f1b9..a11f84d 100644 --- a/ShenYin/ShenYin/Sdk/CCRole.swift +++ b/ShenYin/ShenYin/Sdk/CCRole.swift @@ -14,4 +14,23 @@ public class CCRole{ public var type:String = "" /// 角色 public var role:String = "" + + public init(){ + + } + public init(sid: String, name: String, type: String, role: String) { + self.sid = sid + self.name = name + self.type = type + self.role = role + } + + static var xiaoYin:CCRole{ + let model = CCRole() + model.sid = "zh_female_tianmeitaozi_mars_bigtts" + model.name = "小音" + model.type = "default" + model.role = "test_xiaoyin" + return model + } } diff --git a/ShenYin/ShenYin/Sdk/CCSdkManager.swift b/ShenYin/ShenYin/Sdk/CCSdkManager.swift deleted file mode 100644 index fe99743..0000000 --- a/ShenYin/ShenYin/Sdk/CCSdkManager.swift +++ /dev/null @@ -1,12 +0,0 @@ -// -// CCSdkManager.swift -// ShenYin -// -// Created by liangshi on 2025/12/25. -// - -class CCSdkManager { - - - -} diff --git a/ShenYin/ShenYin/Sdk/DcSdk/DCChatVC.swift b/ShenYin/ShenYin/Sdk/DcSdk/DCChatVC.swift deleted file mode 100644 index 51d58a7..0000000 --- a/ShenYin/ShenYin/Sdk/DcSdk/DCChatVC.swift +++ /dev/null @@ -1,218 +0,0 @@ -//// -//// DCChatVC.swift -//// ShenYin -//// -//// Created by liangshi on 2025/12/26. -//// -// -//import UIKit -//import AVFAudio -//import DeepChordsSdk -//class DCChatVC:BaseVC{ -// let rightItem = ChatNavigationBarItem(frame: CGRect(x: 0, y: 0, width: 80, height: 44)) -// let roleItem = ChatRoleItem(frame: CGRect(x: 0, y: 0, width: 100, height: 44)) -// -// let toolBar = ChatRecordBar() -// let tableView = UITableView(frame: CGRectZero, style: .plain) -// let headerView = ChatHeaderView(frame: CGRect(x: 0, y: 0, width: kScreenWid, height: 260)) -// let tipsLabel = UILabel() -// -// var roleList:[DCRole] = [] -// -// deinit { -// PcmSound.sharedInstance().stopAudioUnit() -// PcmSound.sharedInstance().cleanup() -// } -// -// override func viewDidLoad() { -// super.viewDidLoad() -// } -// override func setupViews() { -// -// navBar.rightView.isHidden = true -// navBar.addSubviews([roleItem,rightItem]) -// roleItem.yc.addTap(target: self, action: #selector(roleBtnAction)) -// rightItem.yc.addTap(target: self, action: #selector(rightBtnAction)) -// -// tableView.backgroundColor = .clear -// tableView.delegate = self -// tableView.dataSource = self -// tableView.estimatedRowHeight = 60 -// tableView.separatorStyle = .none -// tableView.register(ChatCell.self, forCellReuseIdentifier: ChatCell.reuseID) -// tableView.tableHeaderView = headerView -// -// view.addSubviews([tableView,toolBar,tipsLabel]) -// toolBar.delegate = self -// -// tipsLabel.do { v in -// v.text = "内容由AI生成,请仔细甄别".l10n -// v.font = .regular(10) -// v.textColor = .hexString("#919191") -// } -// } -// override func setupLayout() { -// rightItem.snp.makeConstraints { (m) in -// m.top.equalTo(kNavTop) -// m.bottom.equalToSuperview() -// m.width.greaterThanOrEqualTo(80) -// m.right.equalToSuperview() -// } -// roleItem.snp.makeConstraints { (m) in -// m.top.equalTo(kNavTop) -// m.bottom.equalToSuperview() -// m.width.greaterThanOrEqualTo(80) -// m.left.equalToSuperview().inset(48) -// } -// toolBar.snp.makeConstraints { make in -// make.width.centerX.equalToSuperview() -// make.height.equalTo(52) -// make.bottom.equalToSuperview().inset(35) -// } -// tipsLabel.snp.makeConstraints { make in -// make.top.equalTo(toolBar.snp.bottom).offset(4) -// make.centerX.equalToSuperview() -// } -// tableView.snp.makeConstraints { make in -// make.top.equalToSuperview().inset(kNavBtm) -// make.left.right.equalToSuperview() -// make.bottom.equalTo(toolBar.snp.top) -// } -// } -// override func setupData() { -// DCChatManager.shared.chatNeedReloadData = {[weak self] in -// guard let self = self else{return} -// tableView.reloadData() -// if DCChatManager.shared.dataList.count > 0 { -// tableView.scrollToRow(at: IndexPath(row: DCChatManager.shared.dataList.count - 1, section: 0), at: .none, animated: true) -// } -// } -// DCChatManager.shared.codeCallback = { [weak self] code in -// guard let self = self else{return} -// if code == 20001 { -// toolBar.recordBtn.state = .start -// } -// else if code == 20002 || code == 20003{ -// toolBar.recordBtn.state = .finish -// } -// -// } -// DCChatManager.shared.finishCallback = { [weak self] path in -// guard let self = self else{return} -// // self.recordBtn.isSelected = false -// } -// -// -// refreshData() -// loadRoleList() -// } -// override func viewWillDisappear(_ animated: Bool) { -// super.viewWillDisappear(animated) -// -// ///结束对话,但并不会终止正在播放的录音 -// DCChatManager.shared.stopChat() -// -// } -// override func viewDidAppear(_ animated: Bool) { -// super.viewDidAppear(animated) -// refreshData() -// } -// override func viewDidDisappear(_ animated: Bool) { -// super.viewDidDisappear(animated) -// ChatCache.shared.saveList(JoySdkManager.shared.chat.dataList) -// } -// -// @objc func rightBtnAction() { -// App.shared.goToPage(IntelligentVC()) -// } -// @objc func roleBtnAction(){ -// if roleList.count == 0 { -// loadRoleList() -// return -// } -// let vc = RoleListVC(list: roleList, selectedModel: roleItem.role) {[weak self] model in -// guard let self = self else{return} -// roleItem.role = model -// headerView.update(name: model.name, role: model.role) -// JoyGemChatManager.shared().setSpeaker(model) -// } -// App.shared.goToPage(vc) -// } -// func refreshData(){ -// tableView.reloadData() -// if JoySdkManager.shared.chat.dataList.count > 0 { -// tableView.scrollToRow(at: IndexPath(row: JoySdkManager.shared.chat.dataList.count - 1, section: 0), at: .none, animated: true) -// } -// } -// func loadRoleList(){ -// DCChatManager.shared.getVcSpeekerList {[weak self] speakers in -// guard let self = self,speakers.count > 0 else{return} -// self.roleList = speakers -// self.roleList.insert(xiaoYinRole(), at: 0) -// -// var currentRole = roleList.first -// if let cacheRole = RoleCache.get(){ -// for item in roleList{ -// if item.role == cacheRole { -// currentRole = item -// break -// } -// } -// } -// self.roleItem.role = currentRole -// self.headerView.update(name: self.roleItem.role.name, role: self.roleItem.role.role) -// JoyGemChatManager.shared().setSpeaker(self.roleItem.role) -// } -// } -// func xiaoYinRole()->JoyGemSpeaker{ -// let model = JoyGemSpeaker() -// model.sid = "zh_female_tianmeitaozi_mars_bigtts" -// model.name = "小音" -// model.type = "default" -// model.role = "test_xiaoyin" -// return model -// } -//} -// -//extension DCChatVC:ChatRecordBarDelegate{ -// func recordBarCancel(_ toolBar: ChatRecordBar) { -// // JoyGemChatManager.shared().stopChat() -// JoyGemChatManager.shared().stopSingleChat() -// if let item = JoySdkManager.shared.chat.dataList.last,item.content.isEmpty{ -// JoySdkManager.shared.chat.dataList.removeLast() -// refreshData() -// } -// } -// -// func recordBarStart(_ toolBar: ChatRecordBar) { -// // let model = ChatCellModel() -// // model.type = 1 -// // model.content = "" -// // model.id = -1 -// // JoySdkManager.shared.dataList.append(model) -// // refreshData() -// -// // JoyGemChatManager.shared().stopChat() -// JoyGemChatManager.shared().startSingleChat() -// DCChatManager.shared.startChat() -// } -// -// func recordBarStop(_ toolBar: ChatRecordBar) { -// JoyGemChatManager.shared().stopSingleChat() -// } -//} -// -//extension DCChatVC:UITableViewDelegate,UITableViewDataSource { -// func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { -// return JoySdkManager.shared.chat.dataList.count -// } -// -// func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { -// let cell = ChatCell.cell(withTableView: tableView, indexPath: indexPath) -// cell.data = JoySdkManager.shared.chat.dataList[indexPath.row] -// return cell -// } -// func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { -// -// } -//} diff --git a/ShenYin/ShenYin/Sdk/DcSdk/BleManager.swift b/ShenYin/ShenYin/Sdk/DcSdk/DcBleManager.swift similarity index 85% rename from ShenYin/ShenYin/Sdk/DcSdk/BleManager.swift rename to ShenYin/ShenYin/Sdk/DcSdk/DcBleManager.swift index 3952660..dd6d19d 100644 --- a/ShenYin/ShenYin/Sdk/DcSdk/BleManager.swift +++ b/ShenYin/ShenYin/Sdk/DcSdk/DcBleManager.swift @@ -1,5 +1,5 @@ //// -//// BleManager.swift +//// DcBleManager.swift //// Example //// //// Created by liangshi on 2025/11/24. @@ -16,7 +16,7 @@ //import CoreBluetooth //import DeepChordsSdk // -//class BleManager: NSObject { +//class DcBleManager: NSObject { // var discoverDeviceCallback: (([CBPeripheral]) -> Void)? // var didConnectDeviceCallback: ((CBPeripheral) -> Void)? // var disConnectDeviceCallback: ((CBPeripheral) -> Void)? @@ -30,7 +30,7 @@ // centralManager = CBCentralManager(delegate: self, queue: nil) // } // -// static let shared = BleManager() +// static let shared = DcBleManager() // // func stopScanDevice() { // centralManager.stopScan() @@ -60,7 +60,7 @@ //// } // } //} -//extension BleManager:CBCentralManagerDelegate{ +//extension DcBleManager:CBCentralManagerDelegate{ // func centralManagerDidUpdateState(_ central: CBCentralManager) { // DCLogger.debug("蓝牙状态",central.state.rawValue) // switch central.state { @@ -100,28 +100,28 @@ // // peripheral.delegate = self // peripheral.discoverServices(nil); -// DCBleManager.shared.centralManager(central, didConnect: peripheral) +// DCDcBleManager.shared.centralManager(central, didConnect: peripheral) // self.didConnectDeviceCallback?(peripheral) // App.shared.window?.makeToast("蓝牙已连接") // } // func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) { -// DCBleManager.shared.centralManager(central, didFailToConnect: peripheral, error: error) +// DCDcBleManager.shared.centralManager(central, didFailToConnect: peripheral, error: error) // } // func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) { // disConnectDeviceCallback?(peripheral) -// DCBleManager.shared.centralManager(central, didDisconnectPeripheral: peripheral, error: nil) +// DCDcBleManager.shared.centralManager(central, didDisconnectPeripheral: peripheral, error: nil) // App.shared.window?.makeToast("蓝牙已断开连接") // App.shared.window?.rootViewController = UINavigationController(rootViewController: DeviceListVC()) // } //} // -//extension BleManager: CBPeripheralDelegate { +//extension DcBleManager: CBPeripheralDelegate { // func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) { -// DCBleManager.shared.peripheral(peripheral, didDiscoverServices: error) +// DCDcBleManager.shared.peripheral(peripheral, didDiscoverServices: error) // } // // func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) { -// DCBleManager.shared.peripheral(peripheral, didDiscoverCharacteristicsFor: service, error: error) +// DCDcBleManager.shared.peripheral(peripheral, didDiscoverCharacteristicsFor: service, error: error) // } // // func peripheral(_ peripheral: CBPeripheral, didUpdateNotificationStateFor characteristic: CBCharacteristic, error: (any Error)?) { @@ -129,7 +129,7 @@ // } // // func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) { -// DCBleManager.shared.peripheral(peripheral, didUpdateValueFor: characteristic, error: error) +// DCDcBleManager.shared.peripheral(peripheral, didUpdateValueFor: characteristic, error: error) // // } // diff --git a/ShenYin/ShenYin/Sdk/DcSdk/DCChatManager.swift b/ShenYin/ShenYin/Sdk/DcSdk/DcChatManager.swift similarity index 86% rename from ShenYin/ShenYin/Sdk/DcSdk/DCChatManager.swift rename to ShenYin/ShenYin/Sdk/DcSdk/DcChatManager.swift index 005b2e8..ad5e4c4 100644 --- a/ShenYin/ShenYin/Sdk/DcSdk/DCChatManager.swift +++ b/ShenYin/ShenYin/Sdk/DcSdk/DcChatManager.swift @@ -1,5 +1,5 @@ // -// DCChatManager.swift +// DcChatManager.swift // Example // // Created by liangshi on 2025/12/15. @@ -20,8 +20,8 @@ import DeepChordsSdk //}; -class DCChatManager:NSObject{ - static let shared = DCChatManager() +class DcChatManager:NSObject{ + static let shared = DcChatManager() private var isTtsPlaying = false private var audioData: Data? @@ -44,25 +44,20 @@ class DCChatManager:NSObject{ public func startChat(){ DCChat.shared.startChat() } - public func getVcSpeekerList(finished:@escaping ([DCRole]?,Bool)->Void){ - DCChat.shared.getVcSpeekerList(finished: finished) - } - - func preparePlay(){ - PcmSound.sharedInstance().cleanup() - PcmSound.sharedInstance().setup(16000, withchannels: 1) - - var options: AVAudioSession.CategoryOptions = [.allowBluetooth] - do { - try AVAudioSession.sharedInstance().setCategory(.playback, options: [options]) - try AVAudioSession.sharedInstance().setActive(true) - } catch { - DCLogger.debug(error) + public func getVcSpeekerList(finished:@escaping ([DCRole]?)->Void){ + DCChat.shared.getVcSpeekerList { list, success in + if success { + finished(list) + } + else{ + finished(nil) + } } } + func setupChat(){ - preparePlay() + CCAiManager.preparePlayBlueTooth() DCChat.shared.role = role DCChat.shared.asrCallback = {[weak self] (text,index) in guard let self = self else{return} @@ -124,7 +119,7 @@ class DCChatManager:NSObject{ } } DCChat.shared.finishCallback = {[weak self] path in - self?.preparePlay() + CCAiManager.preparePlayBlueTooth() self?.finishCallback?(path) } @@ -155,7 +150,7 @@ class DCChatManager:NSObject{ // } // } } -extension DCChatManager : PcmSoundDelegate { +extension DcChatManager : PcmSoundDelegate { func remaining(_ frames: UInt) { DCLogger.debug("[frame:\(frames)]"); if (frames == 0) { diff --git a/ShenYin/ShenYin/Sdk/DcSdk/DcChinese2Enligsh.swift b/ShenYin/ShenYin/Sdk/DcSdk/DcChinese2Enligsh.swift new file mode 100644 index 0000000..157223b --- /dev/null +++ b/ShenYin/ShenYin/Sdk/DcSdk/DcChinese2Enligsh.swift @@ -0,0 +1,92 @@ +// +// DcChinese2Enligsh.swift +// ShenYin +// +// Created by liangshi on 2025/12/29. +// + +import DeepChordsSdk + +class DcChinese2Enligsh:NSObject{ + var dataList:[(Chinese2EnglishCellModel,Int)] = [] + var startIndex = 0//记录开始录音的次数,用于区分返回的数据 + + var stopCallback:(()->Void)? + var refreshDataCallback:(()->Void)? + override init(){ + super.init() + + } + + func start(){ + setupSdk() + startIndex += 100000 + + DCChinse2En.shared.fromLanguage = "auto_lang" + DCChinse2En.shared.toLanguage = "auto_lang" + DCChinse2En.shared.isTranslate = true + DCChinse2En.shared.isTTs = true + DCChinse2En.shared.start() + } + + func stop(){ + DCChinse2En.shared.stop() + } + + func setupSdk(){ + DCChinse2En.shared.asrCallback = { [weak self] (text,index) in + guard let self = self else{return} + Logger.debug("asr: \(text),index: \(index)") + let msg = dataList.firstIndex (where: { (str,idx) in + return idx == Int(index) + self.startIndex + }) + if (msg == nil) { + dataList.append((Chinese2EnglishCellModel(send: text),Int(index) + startIndex)) + } else { + let (item,_) = dataList[msg!] + item.send = text + } + // + // ///监听到一句话结束,自动结束当前录音 + // if (model.definite == true) { + // stopEarRecord() + // } + refreshDataCallback?() + } + DCChinse2En.shared.translateCallback = { [weak self] (text,index) in + guard let self = self else{return} + Logger.debug("translate: \(text),index: \(index)") + let msg = dataList.firstIndex (where: { (str,idx) in + return idx == Int(index) + self.startIndex + }) + if (msg == nil) { + dataList.append((Chinese2EnglishCellModel(response: text),Int(index) + startIndex)) + } else { + let (item,_) = dataList[msg!] + item.response = text + } + refreshDataCallback?() + } + DCChinse2En.shared.translateDataCallback = { data in + PcmSound.sharedInstance().feed(data) + } + DCChinse2En.shared.codeCallback = { [weak self] code in + guard let self = self else{return} + if code == 20001 { + } + else if code == 20002 || code == 20003{ + stopCallback?() + } + } + DCChinse2En.shared.filePathCallBack = { [weak self] path in + guard let self = self else{return} + } + } + +} + +extension DcChinese2Enligsh:PcmSoundDelegate{ + func remaining(_ frames: UInt) { + + } +} diff --git a/ShenYin/ShenYin/Sdk/DcSdk/DCSdkManager.swift b/ShenYin/ShenYin/Sdk/DcSdk/DcSdkManager.swift similarity index 56% rename from ShenYin/ShenYin/Sdk/DcSdk/DCSdkManager.swift rename to ShenYin/ShenYin/Sdk/DcSdk/DcSdkManager.swift index 0ca4e8a..a0b4ff9 100644 --- a/ShenYin/ShenYin/Sdk/DcSdk/DCSdkManager.swift +++ b/ShenYin/ShenYin/Sdk/DcSdk/DcSdkManager.swift @@ -1,13 +1,13 @@ // -// DCSdkManager.swift +// DcSdkManager.swift // Example // // Created by liangshi on 2025/12/19. // import DeepChordsSdk -class DCSdkManager{ - static let shared = DCSdkManager() +class DcSdkManager{ + static let shared = DcSdkManager() public var authCallback:((_ success:Bool)->Void)? @@ -17,57 +17,80 @@ class DCSdkManager{ DCLogger.debug(code,msg) guard let self = self else{return} if code == 200{ - joyGemAuthSuccessCallback() + authSuccessCallback() } else{ - joyGemAuthFailedCallback() + authFailedCallback() } } -// DCRecordManager.shared.setupRecord() - DCChatManager.shared.setupChat() + DcChatManager.shared.setupChat() + /// ai事件回调 + /// 0x01(打开AI对话) + // 0x02(关闭AI对话) + // 0x03(打开耳机录音) + // 0x04(关闭耳机录音) DCDevice.shared.eventCallback = { event in switch event { case 0x01: - if let vc = App.shared.topVC() as? ChatVC { +// 0x01(打开AI对话) + if let vc = App.shared.topVC() as? DcChatVC { // vc.startRecord() }else{ - App.shared.goToPage(ChatVC()) + App.shared.goToPage(DcChatVC()) } break case 0x02: +// 0x02(关闭AI对话) // if let vc = App.shared.topVC() as? ChatVC { // // vc.startRecord() // }else{ // App.shared.goToPage(ChatVC()) // } + DcChatManager.shared.stopChat() break case 0x03: -// if let vc = App.shared.topVC() as? EarPhoneRecordVC { -// // vc.startRecord() -// }else{ -// App.shared.goToPage(EarPhoneRecordVC()) -// } + //0x03(打开耳机录音) + if CCCall.shared.isInCall { + if let vc = App.shared.topVC() as? DcStereoCallVC { + vc.startRecord() + } + else{ + App.shared.goToPage(DcStereoCallVC(autoStart: true)) + DcXcly.shared.recordStart() + } + } + else{ + if let vc = App.shared.topVC() as? DcXclyVC { + vc.startRecord() + } + else{ + App.shared.goToPage(DcXclyVC(autoStart: true)) + DcXcly.shared.recordStart() + } + } break case 0x04: -// if let vc = App.shared.topVC() as? EarPhoneRecordVC { -// // vc.startRecord() -// }else{ -// App.shared.goToPage(EarPhoneRecordVC()) -// } + // 0x04(关闭耳机录音) + if let topVC = App.shared.topVC(), let vc = topVC as? DcXclyVC { + vc.stopRecord() + } + if let topVC = App.shared.topVC(), let vc = topVC as? DcCallingRecordVC { + vc.stopRecord() + } break default: break } } } - func joyGemAuthFailedCallback() { + func authFailedCallback() { Logger.debug("鉴权失败") YLprogressHUD.showAutoHiden("鉴权失败") } - func joyGemAuthSuccessCallback() { + func authSuccessCallback() { Logger.debug("鉴权成功") - App.shared.preparePlay() + CCAiManager.preparePlayBlueTooth() DeviceCache.saveLastConnected(mac: CCBleManager.shared.connectPeripheral!.mac) DeviceCache.shared.add(CCBleManager.shared.connectPeripheral!.device.identifier.uuidString, mac: CCBleManager.shared.connectPeripheral!.mac) @@ -77,7 +100,7 @@ class DCSdkManager{ DeviceVM.updateOtaVersion() } } -extension DCSdkManager{ +extension DcSdkManager{ static func getMac(_ dict:[String:Any])->String?{ if let advMsd = dict[CBAdvertisementDataManufacturerDataKey] as? Data { diff --git a/ShenYin/ShenYin/Sdk/DcSdk/DcTcty.swift b/ShenYin/ShenYin/Sdk/DcSdk/DcTcty.swift new file mode 100644 index 0000000..cbf25be --- /dev/null +++ b/ShenYin/ShenYin/Sdk/DcSdk/DcTcty.swift @@ -0,0 +1,93 @@ +// +// Untitled.swift +// ShenYin +// +// Created by liangshi on 2025/12/29. +// + +import DeepChordsSdk + +class DcTcty:NSObject{ + var messageList : [(String,UInt)] = [] + var translateList : [(String,UInt)] = [] + var recordFilePath : String? + + + var stopCallback:(()->Void)? + var refreshOriginCallback:((String)->Void)? + var refreshTranslateCallback:((String)->Void)? + var filePathCallback:((String)->Void)? + + override init(){ + super.init() + + + } + + func setupSdk(){ + DCPhoneRecord.shared.asrCallback = { [weak self] text in + guard let self = self else{return} + refreshOriginCallback?(text) + } + DCPhoneRecord.shared.translateCallback = { [weak self] text in + guard let self = self else{return} + refreshTranslateCallback?(text) + } + DCPhoneRecord.shared.translateDataCallback = { data in + PcmSound.sharedInstance().feed(data) + } + DCPhoneRecord.shared.codeCallback = { [weak self] code in + guard let self = self else{return} + if code == 20001 { + } + else if code == 20002 || code == 20003{ + stopCallback?() + } + } + DCPhoneRecord.shared.filePathCallBack = { [weak self] path in + guard let self = self else{return} + recordFilePath = path + filePathCallback?(path ?? "") + } + } + func start(from:String,to:String){ + setupSdk() + messageList.removeAll() + translateList.removeAll() + + if Thread.isMainThread { + do { + try AVAudioSession.sharedInstance().setCategory(.playAndRecord, mode: .default, options: []) + try AVAudioSession.sharedInstance().setActive(true) + } catch let error as NSError { + Logger.debug("An error occurred setting the audio session category: \(error.localizedDescription)") + } + } else { + DispatchQueue.main.sync { + do { + try AVAudioSession.sharedInstance().setCategory(.playAndRecord, mode: .default, options: []) + try AVAudioSession.sharedInstance().setActive(true) + } catch let error as NSError { + Logger.debug("An error occurred setting the audio session category: \(error.localizedDescription)") + } + } + } + + DCPhoneRecord.shared.fromLanguage = from + DCPhoneRecord.shared.toLanguage = to + DCPhoneRecord.shared.isTranslate = true + DCPhoneRecord.shared.isTTs = true + DCPhoneRecord.shared.start() + } + + func stop(){ + DCPhoneRecord.shared.stop() + } + +} + +extension DcTcty : PcmSoundDelegate { + func remaining(_ frames: UInt) { + Logger.debug("[frame:\(frames)]"); + } +} diff --git a/ShenYin/ShenYin/Sdk/DcSdk/DcThly.swift b/ShenYin/ShenYin/Sdk/DcSdk/DcThly.swift new file mode 100644 index 0000000..47106fc --- /dev/null +++ b/ShenYin/ShenYin/Sdk/DcSdk/DcThly.swift @@ -0,0 +1,79 @@ +// +// DcThly.swift +// ShenYin +// +// Created by liangshi on 2025/12/29. +// + +import UIKit +import DeepChordsSdk + +class DcThly:NSObject{ + static let shared = DcThly() + + var recordFinish:((String)->Void)? + var recordNeedReloadData:((String)->Void)? + var recordList : [(String,UInt)] = [] + var recordText = "" + /// 状态码回调 + public var codeCallback:((Int)->Void)? + public var callRecordNotOnCall:(()->Void)? + + + override init() { + super.init() + setupSdk() + } + func setupSdk(){ + DCBleRecord.shared.asrCallback = { [weak self] text in + guard let self = self else{return} + recordText = text + recordNeedReloadData?(text) + } + // DCBleRecord.shared.translateCallback = { [weak self] text in + // guard let self = self else{return} + // self.toTextView.text = text + // let range = NSMakeRange(toTextView.text.count - 1, 1) + // self.toTextView.scrollRangeToVisible(range) + // } + DCBleRecord.shared.codeCallback = { [weak self] code in + guard let self = self else{return} + codeCallback?(code) + } + DCBleRecord.shared.filePathCallBack = { [weak self] path in + guard let self = self else{return} + // self.recordBtn.isSelected = false + if recordText.isEmpty{ + return + } + guard let path = path,path.isNotEmpty else{ + return + } + guard let wavPath = DCFolder.recordFileResave(srcPath: path) else{return} + recordFinish?(wavPath) + } + } + func setRecordLanage(_ language:CCAiLanguage){ + DCBleRecord.shared.fromLanguage = language.value + } + func recordStart(){ + recordList.removeAll() + recordText = "" + setupSdk() + if !CCCall.shared.isInCall { + callRecordNotOnCall?() + return + } + Logger.debug("开启通话录音") + DCBleRecord.shared.start(type: 0x00 ) + } + func recordResume(){ + // JoyGemRecordManager.shared().resume() + } + func recordPause(){ + // JoyGemRecordManager.shared().pause() + } + func recordStop(){ + DCBleRecord.shared.stop() + } +} diff --git a/ShenYin/ShenYin/Sdk/DcSdk/DcXcly.swift b/ShenYin/ShenYin/Sdk/DcSdk/DcXcly.swift new file mode 100644 index 0000000..22e90e9 --- /dev/null +++ b/ShenYin/ShenYin/Sdk/DcSdk/DcXcly.swift @@ -0,0 +1,67 @@ +// +// DcXcly.swift +// ShenYin +// +// Created by liangshi on 2025/12/26. +// + +import UIKit +import DeepChordsSdk + +class DcXcly:NSObject{ + static let shared = DcXcly() + + var recordFinish:((String)->Void)? + var recordNeedReloadData:((String)->Void)? + var recordList : [(String,UInt)] = [] + var recordText = "" + /// 状态码回调 + public var codeCallback:((Int)->Void)? + + override init() { + super.init() + setupSdk() + } + func setupSdk(){ + DCBleRecord.shared.asrCallback = { [weak self] text in + guard let self = self else{return} + recordText = text + recordNeedReloadData?(text) + } + DCBleRecord.shared.codeCallback = { [weak self] code in + guard let self = self else{return} + codeCallback?(code) + } + DCBleRecord.shared.filePathCallBack = { [weak self] path in + guard let self = self else{return} + // self.recordBtn.isSelected = false + if recordText.isEmpty{ + return + } + guard let path = path,path.isNotEmpty else{ + return + } + guard let wavPath = DCFolder.recordFileResave(srcPath: path) else{return} + recordFinish?(wavPath) + } + } + func setRecoordLanage(_ language:CCAiLanguage){ + DCBleRecord.shared.fromLanguage = language.value + } + func recordStart(){ + Logger.debug("开启录音") + recordList.removeAll() + recordText = "" + setupSdk() + DCBleRecord.shared.start(type: 0x0c ) + } + func recordResume(){ +// JoyGemRecordManager.shared().resume() + } + func recordPause(){ +// JoyGemRecordManager.shared().pause() + } + func recordStop(){ + DCBleRecord.shared.stop() + } +} diff --git a/ShenYin/ShenYin/Sdk/DcSdk/VC/DcCallingRecordVC.swift b/ShenYin/ShenYin/Sdk/DcSdk/VC/DcCallingRecordVC.swift new file mode 100644 index 0000000..536e48f --- /dev/null +++ b/ShenYin/ShenYin/Sdk/DcSdk/VC/DcCallingRecordVC.swift @@ -0,0 +1,177 @@ + +// +// DcCallingRecordVC.swift +// ShenYin +// +// Created by liangshi on 2025/8/6. +// + +import UIKit +import AVFAudio +class DcCallingRecordVC:BaseVC{ + let toolBar = FieldRecordBar() + let textView = UITextView() + let rhythView = RhythView() + + var autoStart = false + var needSave = true + var bgEnter = false//是否是APP在后台进入这个页面的 + + init(autoStart :Bool = false) { + self.autoStart = autoStart + super.init() + bgEnter = App.shared.isInBackground + setupSdk() + } + + override func viewDidLoad() { + super.viewDidLoad() + } + override func setupViews() { + navBar.title = "通话录音".l10n + navBar.backgroundColor = .white + + toolBar.do { v in + v.delegate = self + v.hasRightItem = false + v.leftItem.language = AiLanguageCache.get(type: .thly) ?? .chinse + } + + view.backgroundColor = .white + + textView.do { v in + v.backgroundColor = .white + v.isEditable = false + v.font = .regular(14) + v.text = "" + v.textColor = .hexString("#121212") + v.contentInset = UIEdgeInsets(top: 0, left: 18, bottom: 0, right: 18) + } + view.addSubviews([rhythView,textView,toolBar]) + + } + override func setupLayout() { + rhythView.snp.makeConstraints { make in + make.width.centerX.equalToSuperview() + make.top.equalToSuperview().inset(kNavBtm) + make.height.equalTo(110) + } + toolBar.snp.makeConstraints { make in + make.width.centerX.equalToSuperview() + make.height.equalTo(120) + make.bottom.equalToSuperview().inset(20) + } + textView.snp.makeConstraints { make in + make.top.equalTo(rhythView.snp.bottom) + make.left.right.equalToSuperview() + make.bottom.equalTo(toolBar.snp.top) + } + } + override func setupData() { + if bgEnter { + textView.text = DcThly.shared.recordText + } + } + func setupSdk(){ + DcThly.shared.recordFinish = {[weak self] path in + guard let self = self else { return } + if !needSave { + return + } + let v = PopTextField() + v.show() + v.cancelTap = { + App.shared.popPage() + } + v.okTap = { [weak self] text in + guard let self = self else{return} + + let model = RecordDetailModel() + model.type = .phone + model.from = toolBar.leftItem.language + model.to = .english + model.name = text + model.path = path + model.duration = YLToos.getDuration(model.path) + model.origin = textView.text + model.translate = "" + App.shared.popPage(animated: false) + App.shared.goToPage(RecordDetailVC(record: model)) + } + } + DcThly.shared.recordNeedReloadData = {[weak self] text in + guard let self = self else { return } + textView.text = text + + let range = NSMakeRange(textView.text.count - 1, 1) + textView.scrollRangeToVisible(range) + } + //需要完善 +// DcThly.shared.callRecordAutoFinish = {[weak self] in +// guard let self = self else { return } +// stopAction() +// } + DcThly.shared.callRecordNotOnCall = {[weak self] in + guard let self = self else { return } + YLprogressHUD.showAutoHiden("未处于通话状态".l10n) + rhythView.reset() + toolBar.recordBtn.isSelected = false + toolBar.leftItem.alpha = 1 + } + + if autoStart{ + startRecord() + } + } + + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + navigationController?.fd_fullscreenPopGestureRecognizer?.isEnabled = false + } + override func viewDidDisappear(_ animated: Bool) { + super.viewDidDisappear(animated) + navigationController?.fd_fullscreenPopGestureRecognizer?.isEnabled = true + } + override func navleftViewTapped() { + if toolBar.recordBtn.isSelected { + CustomAlert.showWith(title: "提示".l10n, messgage: "当前正在录音,确定放弃当前录音吗?".l10n, cancelCallback: {}) {[unowned self] in + self.rhythView.stopTimer() + self.needSave = false + DcThly.shared.recordStop() + self.navigationController?.popViewController(animated: true) + } + } + else{ + super.navleftViewTapped() + } + } + func startRecord(){ + toolBar.recordBtnAction() + } + func stopRecord(){ + toolBar.recordBtnAction() + } +} + +extension DcCallingRecordVC:FieldRecordBarDelegate{ + func fieldRecordBar(_ bar: FieldRecordBar, languageChange language: CCAiLanguage) { + DcThly.shared.setRecordLanage(language) + AiLanguageCache.save(type: .thly, lan: language) + } + + func fieldRecordBar(_ bar: FieldRecordBar, stateChange state: RecordSate) { + if state == .start { + textView.text = "" + rhythView.startTimer() + DcThly.shared.recordStart() + } + else if state == .stop{ + stopAction() + } + } + func stopAction(){ + rhythView.stopTimer() + DcThly.shared.recordStop() + toolBar.startAnimating() + } +} diff --git a/ShenYin/ShenYin/Sdk/DcSdk/VC/DcChatVC.swift b/ShenYin/ShenYin/Sdk/DcSdk/VC/DcChatVC.swift new file mode 100644 index 0000000..5b4b3f2 --- /dev/null +++ b/ShenYin/ShenYin/Sdk/DcSdk/VC/DcChatVC.swift @@ -0,0 +1,201 @@ +//// +//// DcChatVC.swift +//// ShenYin +//// +//// Created by liangshi on 2025/12/26. +//// +// +import UIKit +import AVFAudio +import DeepChordsSdk +class DcChatVC:BaseVC{ + let rightItem = ChatNavigationBarItem(frame: CGRect(x: 0, y: 0, width: 80, height: 44)) + let roleItem = ChatRoleItem(frame: CGRect(x: 0, y: 0, width: 100, height: 44)) + + let toolBar = ChatRecordBar() + let tableView = UITableView(frame: CGRectZero, style: .plain) + let headerView = ChatHeaderView(frame: CGRect(x: 0, y: 0, width: kScreenWid, height: 260)) + let tipsLabel = UILabel() + + var roleList:[CCRole] = [] + + deinit { + CCAiManager.stopPlay() + } + + override func viewDidLoad() { + super.viewDidLoad() + } + override func setupViews() { + + navBar.rightView.isHidden = true + navBar.addSubviews([roleItem,rightItem]) + roleItem.yc.addTap(target: self, action: #selector(roleBtnAction)) + rightItem.yc.addTap(target: self, action: #selector(rightBtnAction)) + + tableView.backgroundColor = .clear + tableView.delegate = self + tableView.dataSource = self + tableView.estimatedRowHeight = 60 + tableView.separatorStyle = .none + tableView.register(ChatCell.self, forCellReuseIdentifier: ChatCell.reuseID) + tableView.tableHeaderView = headerView + + view.addSubviews([tableView,toolBar,tipsLabel]) + toolBar.delegate = self + + tipsLabel.do { v in + v.text = "内容由AI生成,请仔细甄别".l10n + v.font = .regular(10) + v.textColor = .hexString("#919191") + } + } + override func setupLayout() { + rightItem.snp.makeConstraints { (m) in + m.top.equalTo(kNavTop) + m.bottom.equalToSuperview() + m.width.greaterThanOrEqualTo(80) + m.right.equalToSuperview() + } + roleItem.snp.makeConstraints { (m) in + m.top.equalTo(kNavTop) + m.bottom.equalToSuperview() + m.width.greaterThanOrEqualTo(80) + m.left.equalToSuperview().inset(48) + } + toolBar.snp.makeConstraints { make in + make.width.centerX.equalToSuperview() + make.height.equalTo(52) + make.bottom.equalToSuperview().inset(35) + } + tipsLabel.snp.makeConstraints { make in + make.top.equalTo(toolBar.snp.bottom).offset(4) + make.centerX.equalToSuperview() + } + tableView.snp.makeConstraints { make in + make.top.equalToSuperview().inset(kNavBtm) + make.left.right.equalToSuperview() + make.bottom.equalTo(toolBar.snp.top) + } + } + override func setupData() { + DcChatManager.shared.chatNeedReloadData = {[weak self] in + guard let self = self else{return} + tableView.reloadData() + if DcChatManager.shared.dataList.count > 0 { + tableView.scrollToRow(at: IndexPath(row: DcChatManager.shared.dataList.count - 1, section: 0), at: .none, animated: true) + } + } + DcChatManager.shared.codeCallback = { [weak self] code in + guard let self = self else{return} + if code == 20001 { + toolBar.recordBtn.state = .start + } + else if code == 20002 || code == 20003{ + toolBar.recordBtn.state = .finish + } + + } + DcChatManager.shared.finishCallback = { [weak self] path in + guard let self = self else{return} + toolBar.recordBtn.state = .finish + } + + + refreshData() + loadRoleList() + } + override func viewWillDisappear(_ animated: Bool) { + super.viewWillDisappear(animated) + DcChatManager.shared.stopChat() + } + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + refreshData() + } + override func viewDidDisappear(_ animated: Bool) { + super.viewDidDisappear(animated) + ChatCache.shared.saveList(DcChatManager.shared.dataList) + } + + @objc func rightBtnAction() { + App.shared.goToPage(IntelligentVC()) + } + @objc func roleBtnAction(){ + if roleList.count == 0 { + loadRoleList() + return + } + let vc = RoleListVC(list: roleList, selectedModel: roleItem.role) {[weak self] model in + guard let self = self else{return} + roleItem.role = model + headerView.update(name: model.name, role: model.role) + DcChatManager.shared.role = DCRole(sid: model.sid, name: model.name, type: model.type, role: model.role) + } + App.shared.goToPage(vc) + } + func refreshData(){ + tableView.reloadData() + if DcChatManager.shared.dataList.count > 0 { + tableView.scrollToRow(at: IndexPath(row: DcChatManager.shared.dataList.count - 1, section: 0), at: .none, animated: true) + } + } + func loadRoleList(){ + DcChatManager.shared.getVcSpeekerList {[weak self] speakers in + guard let self = self,let speakers = speakers,speakers.count > 0 else{return} + self.roleList = speakers.map{CCRole(sid: $0.sid, name: $0.name, type: $0.type, role: $0.role)} + self.roleList.insert(CCRole.xiaoYin, at: 0) + + var currentRole = roleList.first + if let cacheRole = RoleCache.get(){ + for item in roleList{ + if item.role == cacheRole { + currentRole = item + break + } + } + } + guard let currentRole = currentRole else{return} + self.roleItem.role = currentRole + self.headerView.update(name: self.roleItem.role.name, role: self.roleItem.role.role) + DcChatManager.shared.role = DCRole(sid: currentRole.sid, name: currentRole.name, type: currentRole.type, role: currentRole.role) + } + } + +} + +extension DcChatVC:ChatRecordBarDelegate{ + func recordBarCancel(_ toolBar: ChatRecordBar) { +// JoyGemChatManager.shared().stopSingleChat() + DcChatManager.shared.stopChat() + if let item = DcChatManager.shared.dataList.last,item.content.isEmpty{ + DcChatManager.shared.dataList.removeLast() + refreshData() + } + } + + func recordBarStart(_ toolBar: ChatRecordBar) { +// JoyGemChatManager.shared().startSingleChat() + DcChatManager.shared.startChat() + } + + func recordBarStop(_ toolBar: ChatRecordBar) { +// JoyGemChatManager.shared().stopSingleChat() + DcChatManager.shared.stopChat() + } +} + +extension DcChatVC:UITableViewDelegate,UITableViewDataSource { + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return DcChatManager.shared.dataList.count + } + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cell = ChatCell.cell(withTableView: tableView, indexPath: indexPath) + cell.data = DcChatManager.shared.dataList[indexPath.row] + return cell + } + func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + + } +} diff --git a/ShenYin/ShenYin/Sdk/DcSdk/VC/DcChinese2EnglishVC.swift b/ShenYin/ShenYin/Sdk/DcSdk/VC/DcChinese2EnglishVC.swift new file mode 100644 index 0000000..0406d2f --- /dev/null +++ b/ShenYin/ShenYin/Sdk/DcSdk/VC/DcChinese2EnglishVC.swift @@ -0,0 +1,142 @@ +// +// Chinese2EnglishVC.swift +// ShenYin +// +// Created by liangshi on 2025/8/6. +// + +import UIKit +import AVFAudio +class DcChinese2EnglishVC:BaseVC{ + let toolBar = RecordBar() + let tableView = UITableView(frame: CGRectZero, style: .plain) + let bg = UIImageView() + let sdk = DcChinese2Enligsh() + var dataList:[(Chinese2EnglishCellModel,Int)] = [] + + // deinit { + // PcmSound.sharedInstance().stopAudioUnit() + // PcmSound.sharedInstance().cleanup() + // } + + override func setupViews() { + navBar.title = "中英互译".l10n + navBar.rightView.isHidden = false + navBar.rightView.setImage(UIImage(named: "help_icon"), for: .normal) + view.addSubview(tableView) + view.addSubview(toolBar) + view.addSubview(bg) + + toolBar.delegate = self + toolBar.isChienseEnglish = true + toolBar.leftItem.valueLabel.text = "左耳".l10n + toolBar.rightItem.valueLabel.text = "右耳".l10n + + tableView.backgroundColor = .clear + tableView.delegate = self + tableView.dataSource = self + tableView.estimatedRowHeight = 60 + tableView.separatorStyle = .none + tableView.register(Chinese2EnglishCell.self, forCellReuseIdentifier: Chinese2EnglishCell.reuseID) + + bg.image = UIImage(named: "translate_bg") + } + override func setupLayout() { + toolBar.snp.makeConstraints { make in + make.width.centerX.equalToSuperview() + make.height.equalTo(120) + make.bottom.equalToSuperview().inset(20) + } + tableView.snp.makeConstraints { make in + make.top.equalToSuperview().inset(80) + make.left.right.equalToSuperview() + make.bottom.equalTo(toolBar.snp.top) + } + bg.snp.makeConstraints { make in + make.size.equalTo(90) + make.center.equalTo(tableView) + } + } + override func setupData() { + if TipsCache.needShow(type: .chinese2english) { + RecordTipsView(type: .chinese2english).show() + } + sdk.stopCallback = {[weak self] in + guard let self = self else{return} + toolBar.recordBtn.isSelected = false + } + sdk.refreshDataCallback = {[weak self] in + guard let self = self else{return} + dataList = sdk.dataList + refreshData() + } + } + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + navigationController?.fd_fullscreenPopGestureRecognizer?.isEnabled = false + + PcmSound.sharedInstance().delegate = sdk + CCAiManager.preparePlayBlueTooth() + } + override func viewDidDisappear(_ animated: Bool) { + super.viewDidDisappear(animated) + navigationController?.fd_fullscreenPopGestureRecognizer?.isEnabled = true + CCAiManager.preparePlayBlueTooth() + } + override func navleftViewTapped() { + if toolBar.recordBtn.isSelected { + self.sdk.stop() + // CustomAlert.showWith(title: "提示".l10n, messgage: "当前正在录音,确定放弃当前录音吗".l10n, cancelCallback: {}) { + // self.sdk.stop() + //// PcmSound.sharedInstance().stopAudioUnit() + //// PcmSound.sharedInstance().cleanup() + // super.navleftViewTapped() + // } + } + else{ + super.navleftViewTapped() + } + } + override func navRightViewTapped() { + let tip = RecordTipsView(type: .chinese2english) + tip.show() + } + func refreshData(){ + tableView.reloadData() + if dataList.count > 0 { + tableView.scrollToRow(at: IndexPath(row: dataList.count - 1, section: 0), at: .none, animated: true) + bg.isHidden = true + } + else{ + bg.isHidden = false + } + } +} +extension DcChinese2EnglishVC:RecordBarDelegate{ + func recordBarLanguageChange(_ bar: RecordBar) { + + } + func recordBar(_ bar: RecordBar, stateChange state: RecordBarSate) { + if state == .start { + sdk.start() + } + else if state == .stop{ + sdk.stop() + } + } +} +extension DcChinese2EnglishVC:UITableViewDelegate,UITableViewDataSource { + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return dataList.count + } + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cell = Chinese2EnglishCell.cell(withTableView: tableView, indexPath: indexPath) + let (vallue,_) = dataList[indexPath.row] + cell.data = vallue + return cell + } + func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + + } +} diff --git a/ShenYin/ShenYin/UI/Home/Dialog/DialogTranlateVC.swift b/ShenYin/ShenYin/Sdk/DcSdk/VC/DcDialogTranslateVC.swift similarity index 94% rename from ShenYin/ShenYin/UI/Home/Dialog/DialogTranlateVC.swift rename to ShenYin/ShenYin/Sdk/DcSdk/VC/DcDialogTranslateVC.swift index 06ce7d9..fe50b07 100644 --- a/ShenYin/ShenYin/UI/Home/Dialog/DialogTranlateVC.swift +++ b/ShenYin/ShenYin/Sdk/DcSdk/VC/DcDialogTranslateVC.swift @@ -1,14 +1,14 @@ // -// DialogTranslateVC.swift +// Untitled.swift // ShenYin // -// Created by liangshi on 2025/8/6. +// Created by liangshi on 2025/12/29. // import UIKit import JoyGemAI import AVFoundation -class DialogTranslateVC:BaseVC{ +class DcDialogTranslateVC:BaseVC{ let toolBar = DialogRecordBar() var recordFilePath : String? @@ -20,8 +20,7 @@ class DialogTranslateVC:BaseVC{ var tmpOtherList : [(String,UInt)] = [] var otherPcm = Data() deinit { - PcmSound.sharedInstance().stopAudioUnit() - PcmSound.sharedInstance().cleanup() + CCAiManager.stopPlay() } override func viewDidLoad() { super.viewDidLoad() @@ -34,7 +33,7 @@ class DialogTranslateVC:BaseVC{ navBar.rightView.setImage(UIImage(named: "help_icon"), for: .normal) toolBar.do { v in -// v.backgroundColor = .clear + // v.backgroundColor = .clear v.delegate = self v.navBar = navBar } @@ -98,8 +97,7 @@ class DialogTranslateVC:BaseVC{ override func navleftViewTapped() { JoyGemSIManager.shared().stop() JoyGemSIDeviceManager.shared().stop() - PcmSound.sharedInstance().stopAudioUnit() - PcmSound.sharedInstance().cleanup() + CCAiManager.stopPlay() super.navleftViewTapped() } @@ -112,7 +110,7 @@ class DialogTranslateVC:BaseVC{ } } -extension DialogTranslateVC:UITableViewDelegate,UITableViewDataSource { +extension DcDialogTranslateVC:UITableViewDelegate,UITableViewDataSource { func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return dataList.count } @@ -127,7 +125,7 @@ extension DialogTranslateVC:UITableViewDelegate,UITableViewDataSource { } } -extension DialogTranslateVC:DialogRecordBarDelegate{ +extension DcDialogTranslateVC:DialogRecordBarDelegate{ func recordBar(_ bar: DialogRecordBar, layoutChange: DialogRecordLayoutType) { if layoutChange == .spolit{ splitView.isHidden = false @@ -201,8 +199,8 @@ extension DialogTranslateVC:DialogRecordBarDelegate{ } } -extension DialogTranslateVC : JoyGemSIManagerDelegate { - +extension DcDialogTranslateVC : JoyGemSIManagerDelegate { + func siManagerStopped(_ manager: JoyGemSIManager, reason code: JoyGemRecordStopCode) { if let model = dataList.last,model.send.isEmpty{ @@ -277,8 +275,8 @@ extension DialogTranslateVC : JoyGemSIManagerDelegate { } } -extension DialogTranslateVC : JoyGemSIDeviceManagerDelegate { - +extension DcDialogTranslateVC : JoyGemSIDeviceManagerDelegate { + func siDeviceManagerStopped(_ manager: JoyGemSIDeviceManager, reason code: JoyGemRecordStopCode) { @@ -355,7 +353,7 @@ extension DialogTranslateVC : JoyGemSIDeviceManagerDelegate { } -extension DialogTranslateVC : PcmSoundDelegate { +extension DcDialogTranslateVC : PcmSoundDelegate { func remaining(_ frames: UInt) { Logger.debug("[frame:\(frames)]"); } diff --git a/ShenYin/ShenYin/Sdk/DcSdk/VC/DcStereoCallVC.swift b/ShenYin/ShenYin/Sdk/DcSdk/VC/DcStereoCallVC.swift new file mode 100644 index 0000000..6fa9e3f --- /dev/null +++ b/ShenYin/ShenYin/Sdk/DcSdk/VC/DcStereoCallVC.swift @@ -0,0 +1,264 @@ +// +// Untitled 2.swift +// ShenYin +// +// Created by liangshi on 2025/12/29. +// + +import UIKit +import AVFAudio +class DcStereoCallVC:BaseVC{ + let toolBar = FieldRecordBar() + let line = UIView() + let selfTextView = UITextView() + let otherTextView = UITextView() + private let selfLabel = DotLabel() + private let otherLabel = DotLabel() + let rhythView = RhythView() + + + var autoStart = false + var needSave = true + var bgEnter = false//是否是APP在后台进入这个页面的 + + init(autoStart :Bool = false) { + self.autoStart = autoStart + super.init() + bgEnter = App.shared.isInBackground + setupSdk() + } + override func viewDidLoad() { + super.viewDidLoad() + } + override func setupViews() { + navBar.title = "通话录音".l10n + navBar.backgroundColor = .white + + + toolBar.do { v in + v.delegate = self + v.hasRightItem = false + v.leftItem.language = AiLanguageCache.get(type: .thly) ?? .chinse + // v.backgroundColor = .red + } + + + view.backgroundColor = .white + // rhythView.backgroundColor = .blue + selfTextView.do { v in + v.backgroundColor = .white + v.isEditable = false + v.font = .regular(14) + v.text = "" + v.textColor = .hexString("#121212") + v.contentInset = UIEdgeInsets(top: 0, left: 13, bottom: 0, right: 13) + // v.backgroundColor = .green + } + otherTextView.do { v in + v.backgroundColor = .white + v.isEditable = false + v.font = .regular(14) + v.text = "" + v.textColor = .hexString("#121212") + v.contentInset = UIEdgeInsets(top: 0, left: 13, bottom: 0, right: 13) + // v.backgroundColor = .yellow + } + line.do { v in + v.backgroundColor = .hexString("#E9E9E9") + } + selfLabel.do { v in + v.label.text = "你:" + v.label.textColor = .hexString("#B9B9B9") + v.dot.backgroundColor = v.label.textColor + // v.backgroundColor = .red + } + otherLabel.do { v in + v.label.text = "对方:" + v.label.textColor = .hexString("#0092E1") + v.dot.backgroundColor = v.label.textColor + // v.backgroundColor = .red + } + view.addSubviews([rhythView,selfTextView,otherTextView,toolBar,line,selfLabel,otherLabel]) + + } + override func setupLayout() { + rhythView.snp.makeConstraints { make in + make.width.centerX.equalToSuperview() + make.top.equalToSuperview().inset(kNavBtm) + make.height.equalTo(110) + } + toolBar.snp.makeConstraints { make in + make.width.centerX.equalToSuperview() + make.height.equalTo(120) + make.bottom.equalToSuperview().inset(20) + } + selfLabel.snp.makeConstraints { make in + make.height.equalTo(16) + make.left.right.equalToSuperview() + make.top.equalToSuperview().inset(kNavBtm + 120) + } + selfTextView.snp.makeConstraints { make in + make.top.equalTo(selfLabel.snp.bottom) + make.left.right.equalToSuperview() + make.height.equalTo((kScreenHei - kNavBtm - 120 - 140 - 25 - 32)/2) + } + line.snp.makeConstraints { make in + make.left.right.equalToSuperview().inset(36) + make.height.equalTo(1) + make.top.equalTo(selfTextView.snp.bottom).offset(12) + } + otherLabel.snp.makeConstraints { make in + make.height.equalTo(16) + make.left.right.equalToSuperview() + make.top.equalTo(line.snp.bottom).offset(12) + } + otherTextView.snp.makeConstraints { make in + make.height.equalTo(selfTextView) + make.left.right.equalToSuperview() + make.top.equalTo(otherLabel.snp.bottom) + } + + } + override func setupData() { + if bgEnter { + selfTextView.text = JoySdkManager.shared.callRecord.selfText + otherTextView.text = JoySdkManager.shared.callRecord.otherText + } + } + + func setupSdk(){ + JoySdkManager.shared.callRecord.recordFinish = {[weak self] path in + guard let self = self else { return } + if !needSave { + return + } + let v = PopTextField() + v.show() + v.cancelTap = { + App.shared.popPage() + } + v.okTap = { [weak self] text in + guard let self = self else{return} + gotoDetail(fileName: text, wavPath: path, language: toolBar.leftItem.language) + } + } + JoySdkManager.shared.callRecord.stereoNeedReloadData = {[weak self] (text,index) in + guard let self = self else { return } + if (index == 1) { + selfTextView.text = text + selfTextView.scrollToBottom() + } else if (index == 2) { + otherTextView.text = text + otherTextView.scrollToBottom() + } + } + JoySdkManager.shared.callRecord.callRecordAutoFinish = {[weak self] in + guard let self = self else { return } + stopAction() + } + JoySdkManager.shared.callRecord.callRecordNotOnCall = {[weak self] in + guard let self = self else { return } + YLprogressHUD.showAutoHiden("未处于通话状态".l10n) + rhythView.reset() + toolBar.recordBtn.isSelected = false + toolBar.leftItem.alpha = 1 + } + if autoStart{ + startRecord() + } + } + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + navigationController?.fd_fullscreenPopGestureRecognizer?.isEnabled = false + } + override func viewDidDisappear(_ animated: Bool) { + super.viewDidDisappear(animated) + navigationController?.fd_fullscreenPopGestureRecognizer?.isEnabled = true + } + override func navleftViewTapped() { + if toolBar.recordBtn.isSelected { + CustomAlert.showWith(title: "提示".l10n, messgage: "当前正在录音,确定放弃当前录音吗?".l10n, cancelCallback: {}) {[unowned self] in + self.rhythView.stopTimer() + self.needSave = false + JoySdkManager.shared.callRecord.callRecordStop() + self.navigationController?.popViewController(animated: true) + } + } + else{ + super.navleftViewTapped() + } + } + func startRecord(){ + toolBar.recordBtnAction() + } + func stopRecord(){ + toolBar.recordBtnAction() + } +} + +extension DcStereoCallVC:FieldRecordBarDelegate{ + func fieldRecordBar(_ bar: FieldRecordBar, languageChange language: CCAiLanguage) { + JoySdkManager.shared.callRecord.setCallRecoordLanage(language) + AiLanguageCache.save(type: .thly, lan: language) + } + + func fieldRecordBar(_ bar: FieldRecordBar, stateChange state: RecordSate) { + if state == .start { + // selfMessageList.removeAll() + // otherMessageList.removeAll() + selfTextView.text = nil + otherTextView.text = nil + JoySdkManager.shared.callRecord.callRecordStart() + rhythView.startTimer() + } + else if state == .stop{ + stopAction() + } + } + func stopAction(){ + rhythView.stopTimer() + JoySdkManager.shared.callRecord.callRecordStop() + toolBar.startAnimating() + } + func gotoDetail( fileName:String,wavPath:String,language:CCAiLanguage){ + let model = RecordDetailModel() + model.type = .phone + model.from = language + model.to = .english + model.name = fileName + model.path = wavPath + model.duration = YLToos.getDuration(model.path) + model.origin = "你:".l10n + "\n" + selfTextView.text + "\n\n" + "对方:".l10n + "\n" + otherTextView.text + model.translate = "" + App.shared.popPage(animated: false) + App.shared.goToPage(RecordDetailVC(record: model)) + } +} + +fileprivate class DotLabel:BaseView{ + let dot = UIView() + let label = UILabel() + + override func setupViews() { + addSubviews([dot,label]) + dot.do { v in + v.yc.setCornerRadius(3) + v.backgroundColor = .hexString("#B9B9B9") + } + label.do { v in + v.font = .regular(12) + } + } + override func setupLayout() { + dot.snp.makeConstraints { make in + make.size.equalTo(6) + make.left.equalToSuperview().inset(18) + make.centerY.equalToSuperview() + } + label.snp.makeConstraints { make in + make.left.equalToSuperview().inset(28) + make.centerY.equalToSuperview() + make.right.equalToSuperview().inset(36) + } + } +} diff --git a/ShenYin/ShenYin/Sdk/DcSdk/VC/DcTctyVC.swift b/ShenYin/ShenYin/Sdk/DcSdk/VC/DcTctyVC.swift new file mode 100644 index 0000000..43fafb7 --- /dev/null +++ b/ShenYin/ShenYin/Sdk/DcSdk/VC/DcTctyVC.swift @@ -0,0 +1,155 @@ +// +// Untitled.swift +// ShenYin +// +// Created by liangshi on 2025/12/29. +// + +import UIKit +import AVFoundation +class DcTctyVC:BaseVC{ + let toolBar = RecordBar() + let textView = PlaceHolderTextView() + let translateTextView = PlaceHolderTextView() + let sdk = DcTcty() + + deinit { + CCAiManager.stopPlay() + } + override func setupViews() { + view.addSubviews([toolBar,textView,translateTextView]) + navBar.title = "同传听译".l10n + navBar.rightView.isHidden = false + navBar.rightView.setImage(UIImage(named: "help_icon"), for: .normal) + + toolBar.do { v in + v.backgroundColor = .clear + v.delegate = self + v.recordBtn.activeView.backgroundColor = .hexString("#F7F7F7") + v.leftItem.valueLabel.text = "手机".l10n + v.leftItem.language = AiLanguageCache.get(type: .tscy_left) ?? .chinse + v.rightItem.language = AiLanguageCache.get(type: .tscy_right) ?? .english + } + view.backgroundColor = .hexString("#F7F7F7") + + textView.do { v in + v.placeHolde = "此处将显示原文".l10n + v.textView.textColor = .hexString("#666666") + } + translateTextView.do { v in + v.textView.textColor = .hexString("#121212") + v.placeHolde = "此处将显示译文".l10n + v.bgView.yc.addgradientLayer(colors: [.hexString("#0092E1"),.hexString("#F7F7F7")], direction: .top2down,rect: CGRect(x: 0, y: 0, width: kScreenWid, height: (kScreenHei - kNavBtm - 120 - 10)/2)) + v.bgView.alpha = 0.1 + v.yc.setCorners(withRoundedRect: CGRect(x: 0, y: 0, width: kScreenWid, height: (kScreenHei - kNavBtm - 120 - 10)/2), roundingCorners: [.topLeft,.topRight], cornerRadii: CGSize(width: 20, height: 20)) + } + } + override func setupLayout() { + toolBar.snp.makeConstraints { make in + make.width.centerX.equalToSuperview() + make.height.equalTo(120) + make.bottom.equalToSuperview().inset(20) + } + textView.snp.makeConstraints { make in + make.top.equalToSuperview().inset(80) + make.left.right.equalToSuperview() + make.height.equalTo((kScreenHei - kNavBtm - 120 - 10)/2) + } + translateTextView.snp.makeConstraints { make in + make.top.equalTo(textView.snp.bottom).offset(10) + make.left.right.equalToSuperview() + make.bottom.equalTo(toolBar.snp.top) + } + } + override func setupData() { + if TipsCache.needShow(type: .tongchuan) { + RecordTipsView(type: .tongchuan).show() + } + + sdk.stopCallback = {[weak self] in + guard let self = self else{return} + toolBar.recordBtn.isSelected = false + } + sdk.refreshOriginCallback = {[weak self] text in + guard let self = self else{return} + textView.text = text + textView.textView.scrollToBottom() + } + sdk.refreshTranslateCallback = {[weak self] text in + guard let self = self else{return} + translateTextView.text = text + translateTextView.textView.scrollToBottom() + } + sdk.filePathCallback = { [weak self] path in + guard let self = self else{return} + guard let wavPath = DCFolder.recordFileResave(srcPath: path) else{return} + let v = PopTextField() + v.show() + v.cancelTap = { + App.shared.popPage() + } + v.okTap = { [weak self] text in + guard let self = self else{return} + + let model = RecordDetailModel() + model.type = .translate + model.from = toolBar.leftItem.language + model.to = toolBar.rightItem.language + model.name = text + model.path = wavPath + model.duration = YLToos.getDuration(model.path) + model.origin = textView.text ?? "" + model.translate = translateTextView.text ?? "" + App.shared.popPage(animated: false) + App.shared.goToPage(RecordDetailVC(record: model)) + } + } + } + override func navRightViewTapped() { + let tip = RecordTipsView(type: .tongchuan) + tip.show() + } + + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + navigationController?.fd_fullscreenPopGestureRecognizer?.isEnabled = false + + PcmSound.sharedInstance().delegate = sdk + CCAiManager.preparePlayBlueTooth() + } + override func viewDidDisappear(_ animated: Bool) { + super.viewDidDisappear(animated) + navigationController?.fd_fullscreenPopGestureRecognizer?.isEnabled = true + CCAiManager.stopPlay() + } + override func navleftViewTapped() { + if toolBar.recordBtn.isSelected { + CustomAlert.showWith(title: "提示".l10n, messgage: "当前正在录音,确定放弃当前录音吗?".l10n, cancelCallback: {}) { + self.sdk.stop() + CCAiManager.stopPlay() + super.navleftViewTapped() + } + } + else{ + super.navleftViewTapped() + } + } +} + +extension DcTctyVC:RecordBarDelegate{ + func recordBarLanguageChange(_ bar:RecordBar){ + AiLanguageCache.save(type: .tscy_left, lan: bar.leftItem.language) + AiLanguageCache.save(type: .tscy_right, lan: bar.rightItem.language) + } + func recordBar(_ bar: RecordBar, stateChange state: RecordBarSate) { + if state == .start { + textView.text = nil + translateTextView.text = nil + sdk.start(from: toolBar.leftItem.language.value, to: toolBar.rightItem.language.value) + } + else if state == .stop{ + sdk.stop() + bar.startAnimating() + } + } +} diff --git a/ShenYin/ShenYin/Sdk/DcSdk/VC/DcXclyVC.swift b/ShenYin/ShenYin/Sdk/DcSdk/VC/DcXclyVC.swift new file mode 100644 index 0000000..ae9c8e1 --- /dev/null +++ b/ShenYin/ShenYin/Sdk/DcSdk/VC/DcXclyVC.swift @@ -0,0 +1,176 @@ +// +// Untitled.swift +// ShenYin +// +// Created by liangshi on 2025/12/26. +// + +// +// FieldRecordingVC.swift +// ShenYin +// +// Created by liangshi on 2025/8/6. +// + +import UIKit +import AVFAudio +class DcXclyVC:BaseVC{ + let toolBar = FieldRecordBar() + let textView = UITextView() + let rhythView = RhythView() + + var autoStart = false + var needSave = true + var bgEnter = false//是否是APP在后台进入这个页面的 + + init(autoStart :Bool = false) { + self.autoStart = autoStart + super.init() + bgEnter = App.shared.isInBackground + setupSdk() + } + + override func setupViews() { + navBar.title = "现场录音".l10n + navBar.backgroundColor = .white + toolBar.do { v in + v.delegate = self + v.leftItem.language = AiLanguageCache.get(type: .xcly) ?? .chinse + } + view.backgroundColor = .white + textView.do { v in + v.isEditable = false + v.font = .regular(14) + v.text = "" + v.backgroundColor = .white + v.textColor = .hexString("#121212") + v.contentInset = UIEdgeInsets(top: 0, left: 18, bottom: 0, right: 18) + } + view.addSubviews([rhythView,textView,toolBar]) + } + override func setupLayout() { + rhythView.snp.makeConstraints { make in + make.width.centerX.equalToSuperview() + make.top.equalToSuperview().inset(kNavBtm) + make.height.equalTo(110) + } + toolBar.snp.makeConstraints { make in + make.width.centerX.equalToSuperview() + make.height.equalTo(120) + make.bottom.equalToSuperview().inset(20) + } + textView.snp.makeConstraints { make in + make.top.equalTo(rhythView.snp.bottom) + make.left.right.equalToSuperview() + make.bottom.equalTo(toolBar.snp.top) + } + } + + override func setupData() { + if bgEnter { + textView.text = DcXcly.shared.recordText + } + } + + func setupSdk() { + DcXcly.shared.recordFinish = {[weak self] path in + guard let self = self else { return } + if !needSave { + return + } + let v = PopTextField() + v.show() + v.cancelTap = { + App.shared.popPage() + } + v.okTap = { [weak self] text in + guard let self = self else{return} + Logger.debug("111111 ===",textView.text ?? "") + let model = RecordDetailModel() + model.type = .present + model.from = toolBar.leftItem.language + model.to = .english + model.name = text + model.path = path + model.duration = YLToos.getDuration(model.path) + model.origin = textView.text + model.translate = "" + App.shared.popPage(animated: false) + App.shared.goToPage(RecordDetailVC(record: model)) + } + } + DcXcly.shared.recordNeedReloadData = {[weak self] text in + guard let self = self else { return } + textView.text = text + textView.scrollToBottom() + } +// DcXcly.shared.codeCallback = {[weak self] code in +// guard let code = code else{return} +// if code == 20001 { +// self.recordBtn.isSelected = true +// } +// else if code == 20002 || code == 20003{ +// self.recordBtn.isSelected = false +// } +// } + if autoStart{ + startRecord() + } + } + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + navigationController?.fd_fullscreenPopGestureRecognizer?.isEnabled = false + } + override func viewDidDisappear(_ animated: Bool) { + super.viewDidDisappear(animated) + navigationController?.fd_fullscreenPopGestureRecognizer?.isEnabled = true + } + override func navleftViewTapped() { + if toolBar.recordBtn.isSelected { + CustomAlert.showWith(title: "提示", messgage: "当前正在录音,确定放弃当前录音吗?".l10n, cancelCallback: {}) {[unowned self] in + self.needSave = false + self.rhythView.stopTimer() + DcXcly.shared.recordStop() + self.navigationController?.popViewController(animated: true) + } + } + else{ + super.navleftViewTapped() + } + } + func startRecord(){ + toolBar.recordBtnAction() + rhythView.startTimer() + } + func stopRecord(){ + toolBar.recordBtnAction() + rhythView.stopTimer() + } +} + +extension DcXclyVC:FieldRecordBarDelegate{ + func fieldRecordBar(_ bar: FieldRecordBar, languageChange language: CCAiLanguage) { + DcXcly.shared.setRecoordLanage(language) + AiLanguageCache.save(type: .xcly, lan: language) + } + + func fieldRecordBar(_ bar: FieldRecordBar, stateChange state: RecordSate) { + if state == .start { + DcXcly.shared.recordStart() + rhythView.startTimer() + } + if state == .resume{ + rhythView.startTimer() + DcXcly.shared.recordResume() + } + else if state == .pause{ + DcXcly.shared.recordPause() + rhythView.stopTimer() + } + else if state == .stop{ + rhythView.stopTimer() + DcXcly.shared.recordStop() + bar.startAnimating() + } + } +} diff --git a/ShenYin/ShenYin/Sdk/JoySdk/JoyChat.swift b/ShenYin/ShenYin/Sdk/JoySdk/JoyChat.swift index 2dbac9c..08479bd 100644 --- a/ShenYin/ShenYin/Sdk/JoySdk/JoyChat.swift +++ b/ShenYin/ShenYin/Sdk/JoySdk/JoyChat.swift @@ -34,7 +34,7 @@ extension JoyChat : JoyGemChatManagerDelegate { isTtsPlaying = true Logger.debug("开始播放") JoyGemChatManager.shared().playAudioStart() - App.shared.preparePlay() + CCAiManager.preparePlayBlueTooth() } Logger.debug("收到音频数据", data?.count ?? 0) PcmSound.sharedInstance().feed(data) @@ -47,7 +47,7 @@ extension JoyChat : JoyGemChatManagerDelegate { case .startRecord: if (isTtsPlaying) { isTtsPlaying = false - App.shared.preparePlay() + CCAiManager.preparePlayBlueTooth() } break case .stopRecord: diff --git a/ShenYin/ShenYin/Sdk/JoySdk/JoyChinse2English.swift b/ShenYin/ShenYin/Sdk/JoySdk/JoyChinse2English.swift new file mode 100644 index 0000000..f8a93f1 --- /dev/null +++ b/ShenYin/ShenYin/Sdk/JoySdk/JoyChinse2English.swift @@ -0,0 +1,104 @@ +// +// JoyChinse2English.swift +// ShenYin +// +// Created by liangshi on 2025/12/26. +// +import JoyGemAI + +class JoyChinse2English:NSObject{ + var dataList:[(Chinese2EnglishCellModel,Int)] = [] + var startIndex = 0//记录开始录音的次数,用于区分返回的数据 + + var stopCallback:(()->Void)? + var refreshDataCallback:(()->Void)? + override init(){ + super.init() + JoyGemSIManager.shared().delegate = self + PcmSound.sharedInstance().delegate = self + } + + func start(){ + startIndex += 100000 + // JoyGemSIManager.shared().originalLanguage = bar.leftItem.language.value + // JoyGemSIManager.shared().targetLanguage = bar.rightItem.language.value + JoyGemSIManager.shared().start() + // do { + // try AVAudioSession.sharedInstance().setCategory(.playAndRecord, options: [.defaultToSpeaker]) + // try AVAudioSession.sharedInstance().overrideOutputAudioPort(.speaker) + // } catch { + // print(error) + // } + } + + func stop(){ + JoyGemSIManager.shared().stop() + } + +} +extension JoyChinse2English : JoyGemSIManagerDelegate { + func siManagerStopped(_ manager: JoyGemSIManager, reason code: JoyGemRecordStopCode) { + stopCallback?() + Logger.debug("停止",code.rawValue) +// if code == .passively { +// toolBar.recordBtn.isSelected = false +// } + } + + func siManager(_ manager: JoyGemSIManager, didUpdateRaw model: JoyGemAsrModel) { + Logger.debug("text: \(model.text),index: \(model.index)") + let msg = dataList.firstIndex (where: { (str,idx) in + return idx == Int(model.index) + startIndex + }) + if (msg == nil) { + dataList.append((Chinese2EnglishCellModel(send: model.text),Int(model.index) + startIndex)) + } else { + let (item,_) = dataList[msg!] + item.send = model.text + } + // + // ///监听到一句话结束,自动结束当前录音 + // if (model.definite == true) { + // stopEarRecord() + // } + refreshDataCallback?() + } + + func siManager(_ manager: JoyGemSIManager, didUpdateTranslate model: JoyGemAsrModel) { + let msg = dataList.firstIndex (where: { (str,idx) in + return idx == Int(model.index) + startIndex + }) + if (msg == nil) { + dataList.append((Chinese2EnglishCellModel(response: model.text),Int(model.index) + startIndex)) + } else { + let (item,_) = dataList[msg!] + item.response = model.text + } + refreshDataCallback?() + } + + func siManager(_ manager: JoyGemSIManager, saveFile path: String) { + Logger.debug("path: \(path)") + } + + + func siManager(_ manager: JoyGemSIManager, stateChange code: JoyGemStateCode) { + Logger.debug("code: \(code)") + } + + func siManagerStopped(_ manager: JoyGemSIManager) { + + } + func siManager(_ manager: JoyGemSIManager, didUpdateAudioData data: Data, sampleRate: UInt) { + PcmSound.sharedInstance().feed(data) + } + + func siManager(_ manager: JoyGemSIManager, onError code: JoyGemErrorCode) { + + } +} +extension JoyChinse2English:PcmSoundDelegate{ + func remaining(_ frames: UInt) { + + } +} diff --git a/ShenYin/ShenYin/Sdk/JoySdk/JoyDialog.swift b/ShenYin/ShenYin/Sdk/JoySdk/JoyDialog.swift new file mode 100644 index 0000000..c640165 --- /dev/null +++ b/ShenYin/ShenYin/Sdk/JoySdk/JoyDialog.swift @@ -0,0 +1,16 @@ +// +// JoyDialog.swift +// ShenYin +// +// Created by liangshi on 2025/12/29. +// + +class JoyDialog: NSObject { + + + + + + + +} diff --git a/ShenYin/ShenYin/Sdk/JoySdk/JoySdkManager.swift b/ShenYin/ShenYin/Sdk/JoySdk/JoySdkManager.swift index 4149aa3..4e9a702 100644 --- a/ShenYin/ShenYin/Sdk/JoySdk/JoySdkManager.swift +++ b/ShenYin/ShenYin/Sdk/JoySdk/JoySdkManager.swift @@ -116,7 +116,7 @@ extension JoySdkManager : JoyGemAIManagerDelegate { } func joyGemAuthSuccessCallback() { Logger.debug("鉴权成功") - App.shared.preparePlay() + CCAiManager.preparePlayBlueTooth() DeviceCache.saveLastConnected(mac: CCBleManager.shared.connectPeripheral!.mac) DeviceCache.shared.add(CCBleManager.shared.connectPeripheral!.device.identifier.uuidString, mac: CCBleManager.shared.connectPeripheral!.mac) @@ -134,40 +134,40 @@ extension JoySdkManager : JoyGemAIManagerDelegate { switch action { case .cancel: Logger.debug("请求关闭录音功能/通知关闭了对话") - if let topVC = App.shared.topVC(), let vc = topVC as? FieldRecordingVC { + if let topVC = App.shared.topVC(), let vc = topVC as? JoyXclyVC { vc.stopRecord() } - if let topVC = App.shared.topVC(), let vc = topVC as? CallingRecordVC { + if let topVC = App.shared.topVC(), let vc = topVC as? JoyCallingRecordVC { vc.stopRecord() } - if let topVC = App.shared.topVC(), let vc = topVC as? StereoCallVC { + if let topVC = App.shared.topVC(), let vc = topVC as? JoyStereoCallVC { vc.stopRecord() } JoySdkManager.shared.record.recordStop() case .recordLive: Logger.debug("请求开启现场录音") - if let vc = App.shared.topVC() as? FieldRecordingVC { + if let vc = App.shared.topVC() as? JoyXclyVC { vc.startRecord() } else{ - App.shared.goToPage(FieldRecordingVC(autoStart: true)) + App.shared.goToPage(JoyXclyVC(autoStart: true)) JoySdkManager.shared.record.recordStart() } case .recordCall: Logger.debug("请求开启通话录音") if JoySdkManager.shared.callRecord.supportStereo{ - if let vc = App.shared.topVC() as? StereoCallVC { + if let vc = App.shared.topVC() as? JoyStereoCallVC { vc.startRecord() }else{ - App.shared.goToPage(CallingRecordVC(autoStart: true)) + App.shared.goToPage(JoyCallingRecordVC(autoStart: true)) JoySdkManager.shared.callRecord.callRecordStart() } } else{ - if let vc = App.shared.topVC() as? CallingRecordVC { + if let vc = App.shared.topVC() as? JoyCallingRecordVC { vc.startRecord() }else{ - App.shared.goToPage(CallingRecordVC(autoStart: true)) + App.shared.goToPage(JoyCallingRecordVC(autoStart: true)) JoySdkManager.shared.callRecord.callRecordStart() } } @@ -176,8 +176,8 @@ extension JoySdkManager : JoyGemAIManagerDelegate { Logger.debug( "请求开启同传听译") case .chat: Logger.debug("通知开启AI对话") - if !App.shared.topVC().isKind(of: ChatVC.self) { - App.shared.goToPage(ChatVC()) + if !App.shared.topVC().isKind(of: JoyChatVC.self) { + App.shared.goToPage(JoyChatVC()) } @unknown default: break diff --git a/ShenYin/ShenYin/Sdk/JoySdk/JoyTcty.swift b/ShenYin/ShenYin/Sdk/JoySdk/JoyTcty.swift new file mode 100644 index 0000000..368ae4c --- /dev/null +++ b/ShenYin/ShenYin/Sdk/JoySdk/JoyTcty.swift @@ -0,0 +1,120 @@ +// +// JoyTcty.swift +// ShenYin +// +// Created by liangshi on 2025/12/29. +// + +import JoyGemAI + +class JoyTcty:NSObject{ + var messageList : [(String,UInt)] = [] + var translateList : [(String,UInt)] = [] + var recordFilePath : String? + + + var stopCallback:(()->Void)? + var refreshOriginCallback:((String)->Void)? + var refreshTranslateCallback:((String)->Void)? + var filePathCallback:((String)->Void)? + + override init(){ + super.init() + JoyGemSIManager.shared().delegate = self + PcmSound.sharedInstance().delegate = self + } + + func start(from:String,to:String){ + messageList.removeAll() + translateList.removeAll() + + if Thread.isMainThread { + do { + try AVAudioSession.sharedInstance().setCategory(.playAndRecord, mode: .default, options: []) + try AVAudioSession.sharedInstance().setActive(true) + } catch let error as NSError { + Logger.debug("An error occurred setting the audio session category: \(error.localizedDescription)") + } + } else { + DispatchQueue.main.sync { + do { + try AVAudioSession.sharedInstance().setCategory(.playAndRecord, mode: .default, options: []) + try AVAudioSession.sharedInstance().setActive(true) + } catch let error as NSError { + Logger.debug("An error occurred setting the audio session category: \(error.localizedDescription)") + } + } + } + JoyGemSIManager.shared().originalLanguage = from + JoyGemSIManager.shared().targetLanguage = to + JoyGemSIManager.shared().start() + } + + func stop(){ + JoyGemSIManager.shared().stop() + } + +} + +extension JoyTcty : JoyGemSIManagerDelegate { + func siManagerStopped(_ manager: JoyGemSIManager, reason code: JoyGemRecordStopCode) { + stopCallback?() + } + + func siManager(_ manager: JoyGemSIManager, didUpdateRaw model: JoyGemAsrModel) { + let msg = messageList.firstIndex (where: { (str,idx) in + return idx == model.index; + }) + if (msg == nil) { + messageList.append((model.text,model.index)) + } else { + messageList[msg!] = (model.text,model.index) + } + + let text = messageList.compactMap({ (str,idx) in + return str + }).joined(separator: "\n") + refreshOriginCallback?(text) + } + + func siManager(_ manager: JoyGemSIManager, didUpdateTranslate model: JoyGemAsrModel) { + Logger.debug("translate text: \(model.text),index: \(model.index)") + let msg = translateList.firstIndex (where: { (str,idx) in + return idx == model.index; + }) + if (msg == nil) { + translateList.append((model.text,model.index)) + } else { + translateList[msg!] = (model.text,model.index) + } + + let text = translateList.compactMap({ (str,idx) in + return str + }).joined(separator: "\n") + refreshTranslateCallback?(text) + } + + + func siManager(_ manager: JoyGemSIManager, saveFile path: String) { + Logger.debug("path: \(path)") + recordFilePath = path + filePathCallback?(path) + } + + func siManager(_ manager: JoyGemSIManager, stateChange code: JoyGemStateCode) { + Logger.debug("code: \(code)") + } + + func siManager(_ manager: JoyGemSIManager, didUpdateAudioData data: Data, sampleRate: UInt) { + PcmSound.sharedInstance().feed(data) + } + func siManager(_ manager: JoyGemSIManager, onError code: JoyGemErrorCode) { + + } +} + +extension JoyTcty : PcmSoundDelegate { + func remaining(_ frames: UInt) { + Logger.debug("[frame:\(frames)]"); + } +} diff --git a/ShenYin/ShenYin/UI/Home/CallingRecordVC.swift b/ShenYin/ShenYin/Sdk/JoySdk/VC/JoyCallingRecordVC.swift similarity index 98% rename from ShenYin/ShenYin/UI/Home/CallingRecordVC.swift rename to ShenYin/ShenYin/Sdk/JoySdk/VC/JoyCallingRecordVC.swift index 9f92411..0ede8b8 100644 --- a/ShenYin/ShenYin/UI/Home/CallingRecordVC.swift +++ b/ShenYin/ShenYin/Sdk/JoySdk/VC/JoyCallingRecordVC.swift @@ -1,5 +1,5 @@ // -// CallingRecordVC.swift +// JoyCallingRecordVC.swift // ShenYin // // Created by liangshi on 2025/8/6. @@ -7,7 +7,7 @@ import UIKit import AVFAudio -class CallingRecordVC:BaseVC{ +class JoyCallingRecordVC:BaseVC{ let toolBar = FieldRecordBar() let textView = UITextView() let rhythView = RhythView() @@ -151,7 +151,7 @@ class CallingRecordVC:BaseVC{ } } -extension CallingRecordVC:FieldRecordBarDelegate{ +extension JoyCallingRecordVC:FieldRecordBarDelegate{ func fieldRecordBar(_ bar: FieldRecordBar, languageChange language: CCAiLanguage) { JoySdkManager.shared.callRecord.setCallRecoordLanage(language) AiLanguageCache.save(type: .thly, lan: language) diff --git a/ShenYin/ShenYin/UI/Home/Chat/ChatVC.swift b/ShenYin/ShenYin/Sdk/JoySdk/VC/JoyChatVC.swift similarity index 88% rename from ShenYin/ShenYin/UI/Home/Chat/ChatVC.swift rename to ShenYin/ShenYin/Sdk/JoySdk/VC/JoyChatVC.swift index 0b63f91..ee53a3f 100644 --- a/ShenYin/ShenYin/UI/Home/Chat/ChatVC.swift +++ b/ShenYin/ShenYin/Sdk/JoySdk/VC/JoyChatVC.swift @@ -8,7 +8,7 @@ import UIKit import JoyGemAI import AVFAudio -class ChatVC:BaseVC{ +class JoyChatVC:BaseVC{ let rightItem = ChatNavigationBarItem(frame: CGRect(x: 0, y: 0, width: 80, height: 44)) let roleItem = ChatRoleItem(frame: CGRect(x: 0, y: 0, width: 100, height: 44)) @@ -17,11 +17,10 @@ class ChatVC:BaseVC{ let headerView = ChatHeaderView(frame: CGRect(x: 0, y: 0, width: kScreenWid, height: 260)) let tipsLabel = UILabel() - var roleList:[JoyGemSpeaker] = [] + var roleList:[CCRole] = [] deinit { - PcmSound.sharedInstance().stopAudioUnit() - PcmSound.sharedInstance().cleanup() + CCAiManager.stopPlay() JoyGemChatManager.shared().removeObserver(self, forKeyPath: "isActive") } @@ -142,7 +141,12 @@ class ChatVC:BaseVC{ guard let self = self else{return} roleItem.role = model headerView.update(name: model.name, role: model.role) - JoyGemChatManager.shared().setSpeaker(model) + let joyMode = JoyGemSpeaker() + joyMode.role = model.role + joyMode.sid = model.sid + joyMode.name = model.name + joyMode.type = model.type + JoyGemChatManager.shared().setSpeaker(joyMode) } App.shared.goToPage(vc) } @@ -155,8 +159,8 @@ class ChatVC:BaseVC{ func loadRoleList(){ JoyGemChatManager.shared().getSpeakerList {[weak self] speakers in guard let self = self,speakers.count > 0 else{return} - self.roleList = speakers - self.roleList.insert(xiaoYinRole(), at: 0) + self.roleList = speakers.map{CCRole(sid: $0.sid, name: $0.name, type: $0.type, role: $0.role)} + self.roleList.insert(CCRole.xiaoYin, at: 0) var currentRole = roleList.first if let cacheRole = RoleCache.get(){ @@ -169,20 +173,17 @@ class ChatVC:BaseVC{ } self.roleItem.role = currentRole self.headerView.update(name: self.roleItem.role.name, role: self.roleItem.role.role) - JoyGemChatManager.shared().setSpeaker(self.roleItem.role) + let joyMode = JoyGemSpeaker() + joyMode.role = self.roleItem.role.role + joyMode.sid = self.roleItem.role.sid + joyMode.name = self.roleItem.role.name + joyMode.type = self.roleItem.role.type + JoyGemChatManager.shared().setSpeaker(joyMode) } } - func xiaoYinRole()->JoyGemSpeaker{ - let model = JoyGemSpeaker() - model.sid = "zh_female_tianmeitaozi_mars_bigtts" - model.name = "小音" - model.type = "default" - model.role = "test_xiaoyin" - return model - } } -extension ChatVC:ChatRecordBarDelegate{ +extension JoyChatVC:ChatRecordBarDelegate{ func recordBarCancel(_ toolBar: ChatRecordBar) { // JoyGemChatManager.shared().stopChat() JoyGemChatManager.shared().stopSingleChat() @@ -209,7 +210,7 @@ extension ChatVC:ChatRecordBarDelegate{ } } -extension ChatVC:UITableViewDelegate,UITableViewDataSource { +extension JoyChatVC:UITableViewDelegate,UITableViewDataSource { func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return JoySdkManager.shared.chat.dataList.count } diff --git a/ShenYin/ShenYin/UI/Home/Chinese2English/Chinese2EnglishVC.swift b/ShenYin/ShenYin/Sdk/JoySdk/VC/JoyChinese2EnglishVC.swift similarity index 51% rename from ShenYin/ShenYin/UI/Home/Chinese2English/Chinese2EnglishVC.swift rename to ShenYin/ShenYin/Sdk/JoySdk/VC/JoyChinese2EnglishVC.swift index 4363db3..0fcfc68 100644 --- a/ShenYin/ShenYin/UI/Home/Chinese2English/Chinese2EnglishVC.swift +++ b/ShenYin/ShenYin/Sdk/JoySdk/VC/JoyChinese2EnglishVC.swift @@ -6,15 +6,13 @@ // import UIKit -import JoyGemAI import AVFAudio -class Chinese2EnglishVC:BaseVC{ +class JoyChinese2EnglishVC:BaseVC{ let toolBar = RecordBar() let tableView = UITableView(frame: CGRectZero, style: .plain) let bg = UIImageView() + let sdk = JoyChinse2English() var dataList:[(Chinese2EnglishCellModel,Int)] = [] - var recordFilePath : String? - var startIndex = 0//记录开始录音的次数,用于区分返回的数据 // deinit { // PcmSound.sharedInstance().stopAudioUnit() @@ -60,32 +58,40 @@ class Chinese2EnglishVC:BaseVC{ } } override func setupData() { - JoyGemSIManager.shared().delegate = self - if TipsCache.needShow(type: .chinese2english) { RecordTipsView(type: .chinese2english).show() } + sdk.stopCallback = {[weak self] in + guard let self = self else{return} + toolBar.recordBtn.isSelected = false + } + sdk.refreshDataCallback = {[weak self] in + guard let self = self else{return} + dataList = sdk.dataList + refreshData() + } } override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) navigationController?.fd_fullscreenPopGestureRecognizer?.isEnabled = false - PcmSound.sharedInstance().delegate = self; - App.shared.preparePlay() + PcmSound.sharedInstance().delegate = sdk + CCAiManager.preparePlayBlueTooth() } override func viewDidDisappear(_ animated: Bool) { super.viewDidDisappear(animated) navigationController?.fd_fullscreenPopGestureRecognizer?.isEnabled = true - App.shared.preparePlay() + CCAiManager.preparePlayBlueTooth() } override func navleftViewTapped() { if toolBar.recordBtn.isSelected { - CustomAlert.showWith(title: "提示".l10n, messgage: "当前正在录音,确定放弃当前录音吗".l10n, cancelCallback: {}) { -// JoyGemSIManager.shared().stop() -// PcmSound.sharedInstance().stopAudioUnit() -// PcmSound.sharedInstance().cleanup() - super.navleftViewTapped() - } + self.sdk.stop() +// CustomAlert.showWith(title: "提示".l10n, messgage: "当前正在录音,确定放弃当前录音吗".l10n, cancelCallback: {}) { +// self.sdk.stop() +//// PcmSound.sharedInstance().stopAudioUnit() +//// PcmSound.sharedInstance().cleanup() +// super.navleftViewTapped() +// } } else{ super.navleftViewTapped() @@ -106,36 +112,27 @@ class Chinese2EnglishVC:BaseVC{ } } } -extension Chinese2EnglishVC:RecordBarDelegate{ +extension JoyChinese2EnglishVC:RecordBarDelegate{ func recordBarLanguageChange(_ bar: RecordBar) { } func recordBar(_ bar: RecordBar, stateChange state: RecordBarSate) { if state == .start { - startIndex += 100000 -// JoyGemSIManager.shared().originalLanguage = bar.leftItem.language.value -// JoyGemSIManager.shared().targetLanguage = bar.rightItem.language.value - JoyGemSIManager.shared().start() -// do { -// try AVAudioSession.sharedInstance().setCategory(.playAndRecord, options: [.defaultToSpeaker]) -// try AVAudioSession.sharedInstance().overrideOutputAudioPort(.speaker) -// } catch { -// print(error) -// } + sdk.start() } else if state == .stop{ - JoyGemSIManager.shared().stop() + sdk.stop() } } } -extension Chinese2EnglishVC:UITableViewDelegate,UITableViewDataSource { +extension JoyChinese2EnglishVC:UITableViewDelegate,UITableViewDataSource { func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return dataList.count } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = Chinese2EnglishCell.cell(withTableView: tableView, indexPath: indexPath) - let (vallue,index) = dataList[indexPath.row] + let (vallue,_) = dataList[indexPath.row] cell.data = vallue return cell } @@ -143,70 +140,3 @@ extension Chinese2EnglishVC:UITableViewDelegate,UITableViewDataSource { } } - -extension Chinese2EnglishVC : JoyGemSIManagerDelegate { - func siManagerStopped(_ manager: JoyGemSIManager, reason code: JoyGemRecordStopCode) { - Logger.debug("停止",code.rawValue) - if code == .passively { - toolBar.recordBtn.isSelected = false - } - } - - func siManager(_ manager: JoyGemSIManager, didUpdateRaw model: JoyGemAsrModel) { - Logger.debug("text: \(model.text),index: \(model.index)") - let msg = dataList.firstIndex (where: { (str,idx) in - return idx == Int(model.index) + startIndex - }) - if (msg == nil) { - dataList.append((Chinese2EnglishCellModel(send: model.text),Int(model.index) + startIndex)) - } else { - let (item,_) = dataList[msg!] - item.send = model.text - } - // - // ///监听到一句话结束,自动结束当前录音 - // if (model.definite == true) { - // stopEarRecord() - // } - refreshData() - } - - func siManager(_ manager: JoyGemSIManager, didUpdateTranslate model: JoyGemAsrModel) { - let msg = dataList.firstIndex (where: { (str,idx) in - return idx == Int(model.index) + startIndex - }) - if (msg == nil) { - dataList.append((Chinese2EnglishCellModel(response: model.text),Int(model.index) + startIndex)) - } else { - let (item,_) = dataList[msg!] - item.response = model.text - } - refreshData() - } - - func siManager(_ manager: JoyGemSIManager, saveFile path: String) { - Logger.debug("path: \(path)") - recordFilePath = path - } - - - func siManager(_ manager: JoyGemSIManager, stateChange code: JoyGemStateCode) { - Logger.debug("code: \(code)") - } - - func siManagerStopped(_ manager: JoyGemSIManager) { - - } - func siManager(_ manager: JoyGemSIManager, didUpdateAudioData data: Data, sampleRate: UInt) { - PcmSound.sharedInstance().feed(data) - } - - func siManager(_ manager: JoyGemSIManager, onError code: JoyGemErrorCode) { - - } -} -extension Chinese2EnglishVC:PcmSoundDelegate{ - func remaining(_ frames: UInt) { - - } -} diff --git a/ShenYin/ShenYin/Sdk/JoySdk/VC/JoyDialogTranlateVC.swift b/ShenYin/ShenYin/Sdk/JoySdk/VC/JoyDialogTranlateVC.swift new file mode 100644 index 0000000..c81a8b2 --- /dev/null +++ b/ShenYin/ShenYin/Sdk/JoySdk/VC/JoyDialogTranlateVC.swift @@ -0,0 +1,357 @@ +// +// JoyDialogTranslateVC.swift +// ShenYin +// +// Created by liangshi on 2025/8/6. +// + +import UIKit +import JoyGemAI +import AVFoundation +class JoyDialogTranslateVC:BaseVC{ + let toolBar = DialogRecordBar() + + let splitView = DialogSplitView() + let tableView = UITableView(frame: CGRectZero, style: .plain) + var dataList:[DialogTranslateCellModel] = [] + + var tmpList : [(String,UInt)] = [] + var tmpOtherList : [(String,UInt)] = [] + deinit { + CCAiManager.stopPlay() + } + override func viewDidLoad() { + super.viewDidLoad() + } + override func setupViews() { + view.addSubviews([toolBar,tableView,splitView]) + + navBar.title = "对话翻译".l10n + navBar.rightView.isHidden = false + navBar.rightView.setImage(UIImage(named: "help_icon"), for: .normal) + + toolBar.do { v in +// v.backgroundColor = .clear + v.delegate = self + v.navBar = navBar + } + view.backgroundColor = .hexString("#F7F7F7") + + tableView.do { v in + v.backgroundColor = .hexString("#F7F7F7") + v.delegate = self + v.dataSource = self + v.estimatedRowHeight = 60 + v.separatorStyle = .none + v.register(DialogTranslateCell.self, forCellReuseIdentifier: DialogTranslateCell.reuseID) + } + splitView.isHidden = true + + } + override func setupLayout() { + toolBar.snp.makeConstraints { make in + make.width.centerX.equalToSuperview() + make.height.equalTo(120) + make.bottom.equalToSuperview() + } + tableView.snp.makeConstraints { make in + make.top.equalToSuperview().inset(kNavBtm) + make.left.equalToSuperview() + make.width.equalTo(kScreenWid) + make.bottom.equalTo(toolBar.snp.top) + } + splitView.snp.makeConstraints { make in + make.edges.equalTo(tableView) + } + } + override func setupData() { + JoyGemSIManager.shared().delegate = self + JoyGemSIDeviceManager.shared().delegate = self + + PcmSound.sharedInstance().delegate = self; + PcmSound.sharedInstance().setup(16000, withchannels: 1) + do { + try AVAudioSession.sharedInstance().setActive(true) + } catch { + print(error) + } + + if TipsCache.needShow(type: .dialog) { + RecordTipsView(type: .dialog).show() + } + } + override func navRightViewTapped() { + let tip = RecordTipsView(type: .dialog) + tip.show() + } + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + navigationController?.fd_fullscreenPopGestureRecognizer?.isEnabled = false + } + override func viewDidDisappear(_ animated: Bool) { + super.viewDidDisappear(animated) + navigationController?.fd_fullscreenPopGestureRecognizer?.isEnabled = true + } + override func navleftViewTapped() { + JoyGemSIManager.shared().stop() + JoyGemSIDeviceManager.shared().stop() + CCAiManager.stopPlay() + super.navleftViewTapped() + } + + func refreshData(){ + tableView.reloadData() + if dataList.count > 0 { + tableView.scrollToRow(at: IndexPath(row: dataList.count - 1, section: 0), at: .none, animated: true) + } + splitView.dataList = dataList + } +} + +extension JoyDialogTranslateVC:UITableViewDelegate,UITableViewDataSource { + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return dataList.count + } + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cell = DialogTranslateCell.cell(withTableView: tableView, indexPath: indexPath) + cell.data = dataList[indexPath.row] + return cell + } + func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + + } +} + +extension JoyDialogTranslateVC:DialogRecordBarDelegate{ + func recordBar(_ bar: DialogRecordBar, layoutChange: DialogRecordLayoutType) { + if layoutChange == .spolit{ + splitView.isHidden = false + splitView.dataList = dataList + } + else{ + splitView.isHidden = true + } + } + + func recordBar(_ bar: DialogRecordBar, device: DialogRecordDeviceType, stateChange state: RecordBarSate) { + if device == .earphone { + if state == .start { + startEarRecord() + } + else{ + stopEarRecord() + } + } + else if device == .phone{ + if state == .start { + startPhoneRecord() + } + + else{ + stopPhoneRecord() + } + } + } + @objc func startPhoneRecord() { + tmpList.removeAll() + tmpOtherList.removeAll() + let model = DialogTranslateCellModel() + model.isPhone = true + dataList.append(model) + refreshData() + + JoyGemSIManager.shared().originalLanguage = toolBar.languageView.rightItem.language.value + JoyGemSIManager.shared().targetLanguage = toolBar.languageView.leftItem.language.value + Logger.debug("源语言",JoyGemSIManager.shared().originalLanguage) + Logger.debug("目标语言",JoyGemSIManager.shared().targetLanguage) + JoyGemSIManager.shared().start() + } + + @objc func stopPhoneRecord() { + JoyGemSIManager.shared().stop() + } + + @objc func startEarRecord() { + tmpList.removeAll() + tmpOtherList.removeAll() + let model = DialogTranslateCellModel() + model.isPhone = false + dataList.append(model) + refreshData() + + JoyGemSIDeviceManager.shared().originalLanguage = toolBar.languageView.leftItem.language.value + JoyGemSIDeviceManager.shared().targetLanguage = toolBar.languageView.rightItem.language.value + JoyGemSIDeviceManager.shared().start() + do { + try AVAudioSession.sharedInstance().setCategory(.playAndRecord, options: [.defaultToSpeaker]) + try AVAudioSession.sharedInstance().overrideOutputAudioPort(.speaker) + } catch { + print(error) + } + } + + @objc func stopEarRecord() { + JoyGemSIDeviceManager.shared().stop() + } +} + +extension JoyDialogTranslateVC : JoyGemSIManagerDelegate { + + + func siManagerStopped(_ manager: JoyGemSIManager, reason code: JoyGemRecordStopCode) { + if let model = dataList.last,model.send.isEmpty{ + dataList.removeLast() + refreshData() + } + } + + func siManager(_ manager: JoyGemSIManager, didUpdateRaw model: JoyGemAsrModel) { + let msg = tmpList.firstIndex (where: { (str,idx) in + return idx == model.index; + }) + if (msg == nil) { + tmpList.append((model.text,model.index)) + } else { + tmpList[msg!] = (model.text,model.index) + } + + ///监听到一句话结束,自动结束当前录音 + if (model.definite == true) { + toolBar.stopRecord(.phone) + stopPhoneRecord() + } + + guard let model = dataList.last else { + return + } + + model.send = tmpList.compactMap({ (str,idx) in + return str + }).joined(separator: "\n") + refreshData() + } + + func siManager(_ manager: JoyGemSIManager, didUpdateTranslate model: JoyGemAsrModel) { + let msg = tmpOtherList.firstIndex (where: { (str,idx) in + return idx == model.index; + }) + if (msg == nil) { + tmpOtherList.append((model.text,model.index)) + } else { + tmpOtherList[msg!] = (model.text,model.index) + } + + guard let model = dataList.last else { + return + } + + model.response = tmpOtherList.compactMap({ (str,idx) in + return str + }).joined(separator: "\n") + refreshData() + } + + func siManager(_ manager: JoyGemSIManager, saveFile path: String) { + + } + + + func siManager(_ manager: JoyGemSIManager, stateChange code: JoyGemStateCode) { + + } + + func siManagerStopped(_ manager: JoyGemSIManager) { + + } + func siManager(_ manager: JoyGemSIManager, didUpdateAudioData data: Data, sampleRate: UInt) { + PcmSound.sharedInstance().feed(data) + } + func siManager(_ manager: JoyGemSIManager, onError code: JoyGemErrorCode) { + + } +} + +extension JoyDialogTranslateVC : JoyGemSIDeviceManagerDelegate { + + + + func siDeviceManagerStopped(_ manager: JoyGemSIDeviceManager, reason code: JoyGemRecordStopCode) { + if let model = dataList.last,model.send.isEmpty{ + dataList.removeLast() + refreshData() + } + } + + func siDeviceManager(_ manager: JoyGemSIDeviceManager, didUpdateRaw model: JoyGemAsrModel) { + Logger.debug("text: \(model.text),index: \(model.index)") + let msg = tmpList.firstIndex (where: { (str,idx) in + return idx == model.index; + }) + if (msg == nil) { + tmpList.append((model.text,model.index)) + } else { + tmpList[msg!] = (model.text,model.index) + } + + ///监听到一句话结束,自动结束当前录音 + if (model.definite == true) { + toolBar.stopRecord(.earphone) + stopEarRecord() + } + + guard let model = dataList.last else { + return + } + + model.send = tmpList.compactMap({ (str,idx) in + return str + }).joined(separator: "\n") + refreshData() + } + + func siDeviceManager(_ manager: JoyGemSIDeviceManager, didUpdateTranslate model: JoyGemAsrModel) { + let msg = tmpOtherList.firstIndex (where: { (str,idx) in + return idx == model.index; + }) + if (msg == nil) { + tmpOtherList.append((model.text,model.index)) + } else { + tmpOtherList[msg!] = (model.text,model.index) + } + + guard let model = dataList.last else { + return + } + + model.response = tmpOtherList.compactMap({ (str,idx) in + return str + }).joined(separator: "\n") + refreshData() + } + func siDeviceManager(_ manager: JoyGemSIDeviceManager, didUpdateAudioData data: Data, sampleRate: UInt) { + PcmSound.sharedInstance().feed(data) + } + func siDeviceManager(_ manager: JoyGemSIDeviceManager, update mode: JoyGemRecordMode) { + + } + + func siDeviceManager(_ manager: JoyGemSIDeviceManager, saveFile path: String) { + Logger.debug("path: \(path)") + } + + func siDeviceManager(_ manager: JoyGemSIDeviceManager, stateChange code: JoyGemStateCode) { + Logger.debug("code: \(code)") + } + + func siDeviceManager(_ manager: JoyGemSIDeviceManager, onError code: JoyGemErrorCode) { + + } +} + + +extension JoyDialogTranslateVC : PcmSoundDelegate { + func remaining(_ frames: UInt) { + Logger.debug("[frame:\(frames)]"); + } +} diff --git a/ShenYin/ShenYin/UI/Home/StereoCallVC.swift b/ShenYin/ShenYin/Sdk/JoySdk/VC/JoyStereoCallVC.swift similarity index 99% rename from ShenYin/ShenYin/UI/Home/StereoCallVC.swift rename to ShenYin/ShenYin/Sdk/JoySdk/VC/JoyStereoCallVC.swift index 146ff75..085d94e 100644 --- a/ShenYin/ShenYin/UI/Home/StereoCallVC.swift +++ b/ShenYin/ShenYin/Sdk/JoySdk/VC/JoyStereoCallVC.swift @@ -1,5 +1,5 @@ // -// StereoCallVC.swift +// JoyStereoCallVC.swift // ShenYin // // Created by liangshi on 2025/11/26. @@ -7,7 +7,7 @@ import UIKit import AVFAudio -class StereoCallVC:BaseVC{ +class JoyStereoCallVC:BaseVC{ let toolBar = FieldRecordBar() let line = UIView() let selfTextView = UITextView() @@ -196,7 +196,7 @@ class StereoCallVC:BaseVC{ } } -extension StereoCallVC:FieldRecordBarDelegate{ +extension JoyStereoCallVC:FieldRecordBarDelegate{ func fieldRecordBar(_ bar: FieldRecordBar, languageChange language: CCAiLanguage) { JoySdkManager.shared.callRecord.setCallRecoordLanage(language) AiLanguageCache.save(type: .thly, lan: language) diff --git a/ShenYin/ShenYin/Sdk/JoySdk/VC/JoyTctyVC.swift b/ShenYin/ShenYin/Sdk/JoySdk/VC/JoyTctyVC.swift new file mode 100644 index 0000000..5931db3 --- /dev/null +++ b/ShenYin/ShenYin/Sdk/JoySdk/VC/JoyTctyVC.swift @@ -0,0 +1,155 @@ +// +// JoyDctyVC.swift +// ShenYin +// +// Created by liangshi on 2025/8/6. +// + +import UIKit +import AVFoundation +class JoyTctyVC:BaseVC{ + let toolBar = RecordBar() + let textView = PlaceHolderTextView() + let translateTextView = PlaceHolderTextView() + let sdk = JoyTcty() + + deinit { + CCAiManager.stopPlay() + } + override func setupViews() { + view.addSubviews([toolBar,textView,translateTextView]) + navBar.title = "同传听译".l10n + navBar.rightView.isHidden = false + navBar.rightView.setImage(UIImage(named: "help_icon"), for: .normal) + + toolBar.do { v in + v.backgroundColor = .clear + v.delegate = self + v.recordBtn.activeView.backgroundColor = .hexString("#F7F7F7") + v.leftItem.valueLabel.text = "手机".l10n + v.leftItem.language = AiLanguageCache.get(type: .tscy_left) ?? .chinse + v.rightItem.language = AiLanguageCache.get(type: .tscy_right) ?? .english + } + view.backgroundColor = .hexString("#F7F7F7") + + textView.do { v in + v.placeHolde = "此处将显示原文".l10n + v.textView.textColor = .hexString("#666666") + } + translateTextView.do { v in + v.textView.textColor = .hexString("#121212") + v.placeHolde = "此处将显示译文".l10n + v.bgView.yc.addgradientLayer(colors: [.hexString("#0092E1"),.hexString("#F7F7F7")], direction: .top2down,rect: CGRect(x: 0, y: 0, width: kScreenWid, height: (kScreenHei - kNavBtm - 120 - 10)/2)) + v.bgView.alpha = 0.1 + v.yc.setCorners(withRoundedRect: CGRect(x: 0, y: 0, width: kScreenWid, height: (kScreenHei - kNavBtm - 120 - 10)/2), roundingCorners: [.topLeft,.topRight], cornerRadii: CGSize(width: 20, height: 20)) + } + } + override func setupLayout() { + toolBar.snp.makeConstraints { make in + make.width.centerX.equalToSuperview() + make.height.equalTo(120) + make.bottom.equalToSuperview().inset(20) + } + textView.snp.makeConstraints { make in + make.top.equalToSuperview().inset(80) + make.left.right.equalToSuperview() + make.height.equalTo((kScreenHei - kNavBtm - 120 - 10)/2) + } + translateTextView.snp.makeConstraints { make in + make.top.equalTo(textView.snp.bottom).offset(10) + make.left.right.equalToSuperview() + make.bottom.equalTo(toolBar.snp.top) + } + } + override func setupData() { + if TipsCache.needShow(type: .tongchuan) { + RecordTipsView(type: .tongchuan).show() + } + + sdk.stopCallback = {[weak self] in + guard let self = self else{return} + toolBar.recordBtn.isSelected = false + } + sdk.refreshOriginCallback = {[weak self] text in + guard let self = self else{return} + textView.text = text + textView.textView.scrollToBottom() + } + sdk.refreshTranslateCallback = {[weak self] text in + guard let self = self else{return} + translateTextView.text = text + translateTextView.textView.scrollToBottom() + } + sdk.filePathCallback = { [weak self] path in + guard let self = self else{return} + guard let wavPath = DCFolder.recordFileResave(srcPath: path) else{return} + let v = PopTextField() + v.show() + v.cancelTap = { + App.shared.popPage() + } + v.okTap = { [weak self] text in + guard let self = self else{return} + + let model = RecordDetailModel() + model.type = .translate + model.from = toolBar.leftItem.language + model.to = toolBar.rightItem.language + model.name = text + model.path = wavPath + model.duration = YLToos.getDuration(model.path) + model.origin = textView.text ?? "" + model.translate = translateTextView.text ?? "" + App.shared.popPage(animated: false) + App.shared.goToPage(RecordDetailVC(record: model)) + } + } + } + override func navRightViewTapped() { + let tip = RecordTipsView(type: .tongchuan) + tip.show() + } + + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + navigationController?.fd_fullscreenPopGestureRecognizer?.isEnabled = false + + PcmSound.sharedInstance().delegate = sdk + CCAiManager.preparePlayBlueTooth() + } + override func viewDidDisappear(_ animated: Bool) { + super.viewDidDisappear(animated) + navigationController?.fd_fullscreenPopGestureRecognizer?.isEnabled = true + CCAiManager.stopPlay() + } + override func navleftViewTapped() { + if toolBar.recordBtn.isSelected { + CustomAlert.showWith(title: "提示".l10n, messgage: "当前正在录音,确定放弃当前录音吗?".l10n, cancelCallback: {}) { + self.sdk.stop() + CCAiManager.stopPlay() + super.navleftViewTapped() + } + } + else{ + super.navleftViewTapped() + } + } +} + +extension JoyTctyVC:RecordBarDelegate{ + func recordBarLanguageChange(_ bar:RecordBar){ + AiLanguageCache.save(type: .tscy_left, lan: bar.leftItem.language) + AiLanguageCache.save(type: .tscy_right, lan: bar.rightItem.language) + } + func recordBar(_ bar: RecordBar, stateChange state: RecordBarSate) { + if state == .start { + textView.text = nil + translateTextView.text = nil + sdk.start(from: toolBar.leftItem.language.value, to: toolBar.rightItem.language.value) + } + else if state == .stop{ + sdk.stop() + bar.startAnimating() + } + } +} diff --git a/ShenYin/ShenYin/UI/Home/FieldRecording/FieldRecordingVC.swift b/ShenYin/ShenYin/Sdk/JoySdk/VC/JoyXclyVC.swift similarity index 97% rename from ShenYin/ShenYin/UI/Home/FieldRecording/FieldRecordingVC.swift rename to ShenYin/ShenYin/Sdk/JoySdk/VC/JoyXclyVC.swift index 2a41fb7..7d4fb97 100644 --- a/ShenYin/ShenYin/UI/Home/FieldRecording/FieldRecordingVC.swift +++ b/ShenYin/ShenYin/Sdk/JoySdk/VC/JoyXclyVC.swift @@ -7,7 +7,7 @@ import UIKit import AVFAudio -class FieldRecordingVC:BaseVC{ +class JoyXclyVC:BaseVC{ let toolBar = FieldRecordBar() let textView = UITextView() let rhythView = RhythView() @@ -68,7 +68,6 @@ class FieldRecordingVC:BaseVC{ func setupSdk() { JoySdkManager.shared.record.recordFinish = {[weak self] path in guard let self = self else { return } - Logger.debug("0000000 ===",textView.text ?? "") if !needSave { return } @@ -133,7 +132,7 @@ class FieldRecordingVC:BaseVC{ } } -extension FieldRecordingVC:FieldRecordBarDelegate{ +extension JoyXclyVC:FieldRecordBarDelegate{ func fieldRecordBar(_ bar: FieldRecordBar, languageChange language: CCAiLanguage) { JoySdkManager.shared.record.setRecoordLanage(language) AiLanguageCache.save(type: .xcly, lan: language) diff --git a/ShenYin/ShenYin/UI/Home/Chat/ChatNavigationBarItem.swift b/ShenYin/ShenYin/UI/Home/Chat/ChatNavigationBarItem.swift index a8766c9..3b0eeac 100644 --- a/ShenYin/ShenYin/UI/Home/Chat/ChatNavigationBarItem.swift +++ b/ShenYin/ShenYin/UI/Home/Chat/ChatNavigationBarItem.swift @@ -5,7 +5,6 @@ // Created by liangshi on 2025/9/3. // import UIKit -import JoyGemAI class ChatNavigationBarItem: BaseView { let icon = UIImageView() @@ -44,7 +43,7 @@ class ChatRoleItem: BaseView { let icon = UIImageView() let titleLabel = UILabel() let deslabel = UILabel() - var role:JoyGemSpeaker!{ + var role:CCRole!{ didSet{ titleLabel.text = role.name icon.image = UIImage(named: "role_" + role.role) diff --git a/ShenYin/ShenYin/UI/Home/Chat/RoleListVC.swift b/ShenYin/ShenYin/UI/Home/Chat/RoleListVC.swift index c791423..38600de 100644 --- a/ShenYin/ShenYin/UI/Home/Chat/RoleListVC.swift +++ b/ShenYin/ShenYin/UI/Home/Chat/RoleListVC.swift @@ -6,14 +6,13 @@ // import UIKit -import JoyGemAI class RoleListVC: BaseVC{ let tableView = UITableView(frame: .zero, style: .plain) - let list:[JoyGemSpeaker] - let selectedModel:JoyGemSpeaker - var didSelected:((JoyGemSpeaker)->Void)? + let list:[CCRole] + let selectedModel:CCRole + var didSelected:((CCRole)->Void)? - init(list: [JoyGemSpeaker], selectedModel: JoyGemSpeaker, didSelected: ((JoyGemSpeaker) -> Void)? = nil) { + init(list: [CCRole], selectedModel: CCRole, didSelected: ((CCRole) -> Void)? = nil) { self.list = list self.selectedModel = selectedModel self.didSelected = didSelected @@ -87,7 +86,7 @@ class RoleListCell: BaseTableViewCell { let titleLabel = UILabel() let selectedIcon = UIImageView() let line = UIView() - var data:JoyGemSpeaker!{ + var data:CCRole!{ didSet{ titleLabel.text = data.name icon.image = UIImage(named: "role_" + data.role) diff --git a/ShenYin/ShenYin/UI/Home/RecordTranslateVC.swift b/ShenYin/ShenYin/UI/Home/RecordTranslateVC.swift deleted file mode 100644 index 9f0127a..0000000 --- a/ShenYin/ShenYin/UI/Home/RecordTranslateVC.swift +++ /dev/null @@ -1,242 +0,0 @@ -// -// RecordTranslateVC.swift -// ShenYin -// -// Created by liangshi on 2025/8/6. -// - -import UIKit -import JoyGemAI -import AVFoundation -class RecordTranslateVC:BaseVC{ - let toolBar = RecordBar() - let textView = PlaceHolderTextView() - let translateTextView = PlaceHolderTextView() - var messageList : [(String,UInt)] = [] - var translateList : [(String,UInt)] = [] - var recordFilePath : String? - - deinit { - PcmSound.sharedInstance().stopAudioUnit() - PcmSound.sharedInstance().cleanup() - } - override func setupViews() { - view.addSubviews([toolBar,textView,translateTextView]) - - navBar.title = "同传听译".l10n - navBar.rightView.isHidden = false - navBar.rightView.setImage(UIImage(named: "help_icon"), for: .normal) - - toolBar.do { v in - v.backgroundColor = .clear - v.delegate = self - v.recordBtn.activeView.backgroundColor = .hexString("#F7F7F7") - v.leftItem.valueLabel.text = "手机".l10n - v.leftItem.language = AiLanguageCache.get(type: .tscy_left) ?? .chinse - v.rightItem.language = AiLanguageCache.get(type: .tscy_right) ?? .english - } - view.backgroundColor = .hexString("#F7F7F7") - - textView.do { v in - v.placeHolde = "此处将显示原文".l10n - v.textView.textColor = .hexString("#666666") - } - translateTextView.do { v in - v.textView.textColor = .hexString("#121212") - v.placeHolde = "此处将显示译文".l10n - v.bgView.yc.addgradientLayer(colors: [.hexString("#0092E1"),.hexString("#F7F7F7")], direction: .top2down,rect: CGRect(x: 0, y: 0, width: kScreenWid, height: (kScreenHei - kNavBtm - 120 - 10)/2)) - v.bgView.alpha = 0.1 - v.yc.setCorners(withRoundedRect: CGRect(x: 0, y: 0, width: kScreenWid, height: (kScreenHei - kNavBtm - 120 - 10)/2), roundingCorners: [.topLeft,.topRight], cornerRadii: CGSize(width: 20, height: 20)) - } - } - override func setupLayout() { - toolBar.snp.makeConstraints { make in - make.width.centerX.equalToSuperview() - make.height.equalTo(120) - make.bottom.equalToSuperview().inset(20) - } - textView.snp.makeConstraints { make in - make.top.equalToSuperview().inset(80) - make.left.right.equalToSuperview() - make.height.equalTo((kScreenHei - kNavBtm - 120 - 10)/2) - } - translateTextView.snp.makeConstraints { make in - make.top.equalTo(textView.snp.bottom).offset(10) - make.left.right.equalToSuperview() - make.bottom.equalTo(toolBar.snp.top) - } - } - override func setupData() { - JoyGemSIManager.shared().delegate = self - - PcmSound.sharedInstance().delegate = self; - do { - try AVAudioSession.sharedInstance().setCategory(.playback) - try AVAudioSession.sharedInstance().setActive(true) - } catch { - Logger.debug(error) - } - PcmSound.sharedInstance().setup(16000, withchannels: 1) - - if TipsCache.needShow(type: .tongchuan) { - RecordTipsView(type: .tongchuan).show() - } - } - override func navRightViewTapped() { - let tip = RecordTipsView(type: .tongchuan) - tip.show() - } - - override func viewDidAppear(_ animated: Bool) { - super.viewDidAppear(animated) - navigationController?.fd_fullscreenPopGestureRecognizer?.isEnabled = false - } - override func viewDidDisappear(_ animated: Bool) { - super.viewDidDisappear(animated) - navigationController?.fd_fullscreenPopGestureRecognizer?.isEnabled = true - - PcmSound.sharedInstance().stopAudioUnit() - PcmSound.sharedInstance().cleanup() - } - override func navleftViewTapped() { - if toolBar.recordBtn.isSelected { - CustomAlert.showWith(title: "提示".l10n, messgage: "当前正在录音,确定放弃当前录音吗?".l10n, cancelCallback: {}) { - JoyGemSIManager.shared().stop() - PcmSound.sharedInstance().stopAudioUnit() - PcmSound.sharedInstance().cleanup() - super.navleftViewTapped() - } - } - else{ - super.navleftViewTapped() - } - } -} - -extension RecordTranslateVC:RecordBarDelegate{ - func recordBarLanguageChange(_ bar:RecordBar){ - AiLanguageCache.save(type: .tscy_left, lan: bar.leftItem.language) - AiLanguageCache.save(type: .tscy_right, lan: bar.rightItem.language) - } - func recordBar(_ bar: RecordBar, stateChange state: RecordBarSate) { - if state == .start { - messageList.removeAll() - translateList.removeAll() - textView.text = nil - translateTextView.text = nil - - if Thread.isMainThread { - do { - try AVAudioSession.sharedInstance().setCategory(.playAndRecord, mode: .default, options: []) - try AVAudioSession.sharedInstance().setActive(true) - } catch let error as NSError { - Logger.debug("An error occurred setting the audio session category: \(error.localizedDescription)") - } - } else { - DispatchQueue.main.sync { - do { - try AVAudioSession.sharedInstance().setCategory(.playAndRecord, mode: .default, options: []) - try AVAudioSession.sharedInstance().setActive(true) - } catch let error as NSError { - Logger.debug("An error occurred setting the audio session category: \(error.localizedDescription)") - } - } - } - JoyGemSIManager.shared().originalLanguage = toolBar.leftItem.language.value - JoyGemSIManager.shared().targetLanguage = toolBar.rightItem.language.value - JoyGemSIManager.shared().start() - } - else if state == .stop{ - JoyGemSIManager.shared().stop() - bar.startAnimating() - } - } -} - -extension RecordTranslateVC : JoyGemSIManagerDelegate { - func siManagerStopped(_ manager: JoyGemSIManager, reason code: JoyGemRecordStopCode) { - if (code == .actively) { -// YLprogressHUD.showAutoHiden("用户主动结束") - } else { - toolBar.recordBtn.isSelected = false - } - } - - func siManager(_ manager: JoyGemSIManager, didUpdateRaw model: JoyGemAsrModel) { - let msg = messageList.firstIndex (where: { (str,idx) in - return idx == model.index; - }) - if (msg == nil) { - messageList.append((model.text,model.index)) - } else { - messageList[msg!] = (model.text,model.index) - } - - textView.text = messageList.compactMap({ (str,idx) in - return str - }).joined(separator: "\n") - textView.textView.scrollToBottom() - } - - func siManager(_ manager: JoyGemSIManager, didUpdateTranslate model: JoyGemAsrModel) { - Logger.debug("translate text: \(model.text),index: \(model.index)") - let msg = translateList.firstIndex (where: { (str,idx) in - return idx == model.index; - }) - if (msg == nil) { - translateList.append((model.text,model.index)) - } else { - translateList[msg!] = (model.text,model.index) - } - - translateTextView.text = translateList.compactMap({ (str,idx) in - return str - }).joined(separator: "\n") - translateTextView.textView.scrollToBottom() - } - - - func siManager(_ manager: JoyGemSIManager, saveFile path: String) { - Logger.debug("path: \(path)") - recordFilePath = path - guard let path = recordFilePath,let wavPath = DCFolder.recordFileResave(srcPath: path) else{return} - let v = PopTextField() - v.show() - v.cancelTap = { - App.shared.popPage() - } - v.okTap = { [weak self] text in - guard let self = self else{return} - - let model = RecordDetailModel() - model.type = .translate - model.from = toolBar.leftItem.language - model.to = toolBar.rightItem.language - model.name = text - model.path = wavPath - model.duration = YLToos.getDuration(model.path) - model.origin = textView.text ?? "" - model.translate = translateTextView.text ?? "" - App.shared.popPage(animated: false) - App.shared.goToPage(RecordDetailVC(record: model)) - } - } - - func siManager(_ manager: JoyGemSIManager, stateChange code: JoyGemStateCode) { - Logger.debug("code: \(code)") - } - - func siManager(_ manager: JoyGemSIManager, didUpdateAudioData data: Data, sampleRate: UInt) { - PcmSound.sharedInstance().feed(data) - } - func siManager(_ manager: JoyGemSIManager, onError code: JoyGemErrorCode) { - - } -} - -extension RecordTranslateVC : PcmSoundDelegate { - - func remaining(_ frames: UInt) { - Logger.debug("[frame:\(frames)]"); - } -} diff --git a/ShenYin/ShenYin/UI/Home/Root/HomeFunctionView.swift b/ShenYin/ShenYin/UI/Home/Root/HomeFunctionView.swift index 6a0367d..0ded13c 100644 --- a/ShenYin/ShenYin/UI/Home/Root/HomeFunctionView.swift +++ b/ShenYin/ShenYin/UI/Home/Root/HomeFunctionView.swift @@ -185,20 +185,15 @@ class HomeCell: BaseView { } switch type { case .fileRecord: - App.shared.goToPage(FieldRecordingVC()) + CCPage.gotoFieldRecord() case .callRecord: - if JoySdkManager.shared.callRecord.supportStereo{ - App.shared.goToPage(StereoCallVC()) - } - else{ - App.shared.goToPage(CallingRecordVC()) - } + CCPage.gotoCallRecord() case .chinese2English: - App.shared.goToPage(Chinese2EnglishVC()) + CCPage.gotoChinese2Enlgish() case .recordTranslate: - App.shared.goToPage(RecordTranslateVC()) + CCPage.gotoTcty() case .dialogTranslate: - App.shared.goToPage(DialogTranslateVC()) + CCPage.gotoDialogTranslate() case .textTranslate: App.shared.goToPage(TextTranlateVC()) } diff --git a/ShenYin/ShenYin/UI/Home/Root/HomeHeadView.swift b/ShenYin/ShenYin/UI/Home/Root/HomeHeadView.swift index fcc0708..95a57fa 100644 --- a/ShenYin/ShenYin/UI/Home/Root/HomeHeadView.swift +++ b/ShenYin/ShenYin/UI/Home/Root/HomeHeadView.swift @@ -88,7 +88,7 @@ class HomeHeadView: BaseView { } @objc func chatAction(){ if CCBleManager.shared.isConnetd { - App.shared.goToPage(ChatVC()) + CCPage.gotoChat() } } @objc func deviceChange(_ noti:Notification){ diff --git a/ShenYin/ShenYin/UI/Home/TextTranslate/TextTranslateVC.swift b/ShenYin/ShenYin/UI/Home/TextTranslate/TextTranslateVC.swift index e58eefe..1af0ed4 100644 --- a/ShenYin/ShenYin/UI/Home/TextTranslate/TextTranslateVC.swift +++ b/ShenYin/ShenYin/UI/Home/TextTranslate/TextTranslateVC.swift @@ -6,7 +6,7 @@ // import UIKit -import JoyGemAI + class TextTranlateVC:BaseVC{ let scrollView = UIScrollView() let languageItem = LanguageSelectItem() @@ -95,16 +95,14 @@ extension TextTranlateVC:SendToolBarDelegate{ let from = languageItem.leftLabel.language.value let to = languageItem.rightLabel.language.value - JoyGemAIManager.shared().translate(text, from: from, to: to) {[weak self] result, finish in + CCAiManager.translate(text:text, from: from, to: to) { [weak self] response in guard let self = self else{return} + guard let (result, finish) = response else{return} if !finish{ dataList.last!.response = dataList.last!.response + result refreshData() } - } error: { error in - } - } }