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
}
}
}
-
- public func start(){
+ /// 0x00实时录音,0x0c通话录音
+ public func start(type:Int){
asrList = []
asrText = ""
setupRecord()
let config = StarburstBleAsrConfig()
- config.type = 0x0c/// 0x00实时录音,0x0c通话录音
- //开始录音
+ config.type = type
StarburstSdk.shared().startDeviceRecord(config)
}
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{
--- /dev/null
+//
+// 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)
+ }
+
+ }
+}
/// 是否进⾏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)?
/// 打断
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)
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) {
}
}
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
}
}
- DCChatManager.shared.finishCallback = { [weak self] path in
+ DcChatManager.shared.finishCallback = { [weak self] path in
guard let self = self else{return}
// self.recordBtn.isSelected = false
}
@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)
}
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) {
DCBleRecord.shared.fromLanguage = fromLanguage
DCBleRecord.shared.toLanguage = toLanguage
DCBleRecord.shared.isTranslate = true
- DCBleRecord.shared.start()
+ DCBleRecord.shared.start(type: 0x00)
}
func stopAction(){
//
-// DCChatManager.swift
+// DcChatManager.swift
// Example
//
// Created by liangshi on 2025/12/15.
}
-class DCChatManager:NSObject{
- static let shared = DCChatManager()
+class DcChatManager:NSObject{
+ static let shared = DcChatManager()
private var isTtsPlaying = false
private var audioData: Data?
// }
// }
}
-extension DCChatManager : PcmSoundDelegate {
+extension DcChatManager : PcmSoundDelegate {
func remaining(_ frames: UInt) {
DCLogger.debug("[frame:\(frames)]");
if (frames == 0) {
//
-// 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 {
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;
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";
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;
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";
<string>bluetooth-central</string>
<string>audio</string>
</array>
- <key>NSLocationWhenInUseUsageDescription</key>
- <string>允许APP使用定位方便查找耳机</string>
- <key>ITSAppUsesNonExemptEncryption</key>
- <false/>
- <key>UIUserInterfaceStyle</key>
- <string>Light</string>
</dict>
</plist>
IASignWithWeChat.shared.register()
-
+ CCCall.shared.start()
setupKeyboard()
loadCache()
LoginVM.fetchCountryList {result in
else{
App.shared.goToLogin()
}
-// App.shared.window?.rootViewController = ChatVC()
}
}
static let shared = App()
var window:UIWindow?
var isInBackground = false
- var isJoy = true
-
func topVC()->UIViewController!{
guard let vc = window?.rootViewController else{
}
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}
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(){
--- /dev/null
+//
+// 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()
+ }
+}
+
+
+
+
}
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)
}
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{
}
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)
}
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)
}
}
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()
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)
// 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{
--- /dev/null
+//
+// 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
+ }
+ }
+ }
+ }
+}
--- /dev/null
+//
+// 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())
+ }
+ }
+}
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
+ }
}
+++ /dev/null
-//
-// CCSdkManager.swift
-// ShenYin
-//
-// Created by liangshi on 2025/12/25.
-//
-
-class CCSdkManager {
-
-
-
-}
+++ /dev/null
-////
-//// 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) {
-//
-// }
-//}
////
-//// BleManager.swift
+//// DcBleManager.swift
//// Example
////
//// Created by liangshi on 2025/11/24.
//import CoreBluetooth
//import DeepChordsSdk
//
-//class BleManager: NSObject {
+//class DcBleManager: NSObject {
// var discoverDeviceCallback: (([CBPeripheral]) -> Void)?
// var didConnectDeviceCallback: ((CBPeripheral) -> Void)?
// var disConnectDeviceCallback: ((CBPeripheral) -> Void)?
// centralManager = CBCentralManager(delegate: self, queue: nil)
// }
//
-// static let shared = BleManager()
+// static let shared = DcBleManager()
//
// func stopScanDevice() {
// centralManager.stopScan()
//// }
// }
//}
-//extension BleManager:CBCentralManagerDelegate{
+//extension DcBleManager:CBCentralManagerDelegate{
// func centralManagerDidUpdateState(_ central: CBCentralManager) {
// DCLogger.debug("蓝牙状态",central.state.rawValue)
// switch central.state {
//
// 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)?) {
// }
//
// 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)
//
// }
//
//
-// DCChatManager.swift
+// DcChatManager.swift
// Example
//
// Created by liangshi on 2025/12/15.
//};
-class DCChatManager:NSObject{
- static let shared = DCChatManager()
+class DcChatManager:NSObject{
+ static let shared = DcChatManager()
private var isTtsPlaying = false
private var audioData: Data?
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}
}
}
DCChat.shared.finishCallback = {[weak self] path in
- self?.preparePlay()
+ CCAiManager.preparePlayBlueTooth()
self?.finishCallback?(path)
}
// }
// }
}
-extension DCChatManager : PcmSoundDelegate {
+extension DcChatManager : PcmSoundDelegate {
func remaining(_ frames: UInt) {
DCLogger.debug("[frame:\(frames)]");
if (frames == 0) {
--- /dev/null
+//
+// 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) {
+
+ }
+}
//
-// 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)?
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)
DeviceVM.updateOtaVersion()
}
}
-extension DCSdkManager{
+extension DcSdkManager{
static func getMac(_ dict:[String:Any])->String?{
if let advMsd = dict[CBAdvertisementDataManufacturerDataKey] as? Data {
--- /dev/null
+//
+// 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)]");
+ }
+}
--- /dev/null
+//
+// 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()
+ }
+}
--- /dev/null
+//
+// 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()
+ }
+}
--- /dev/null
+
+//
+// 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()
+ }
+}
--- /dev/null
+////
+//// 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) {
+
+ }
+}
--- /dev/null
+//
+// 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) {
+
+ }
+}
//
-// 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?
var tmpOtherList : [(String,UInt)] = []
var otherPcm = Data()
deinit {
- PcmSound.sharedInstance().stopAudioUnit()
- PcmSound.sharedInstance().cleanup()
+ CCAiManager.stopPlay()
}
override func viewDidLoad() {
super.viewDidLoad()
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
}
override func navleftViewTapped() {
JoyGemSIManager.shared().stop()
JoyGemSIDeviceManager.shared().stop()
- PcmSound.sharedInstance().stopAudioUnit()
- PcmSound.sharedInstance().cleanup()
+ CCAiManager.stopPlay()
super.navleftViewTapped()
}
}
}
-extension DialogTranslateVC:UITableViewDelegate,UITableViewDataSource {
+extension DcDialogTranslateVC:UITableViewDelegate,UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dataList.count
}
}
}
-extension DialogTranslateVC:DialogRecordBarDelegate{
+extension DcDialogTranslateVC:DialogRecordBarDelegate{
func recordBar(_ bar: DialogRecordBar, layoutChange: DialogRecordLayoutType) {
if layoutChange == .spolit{
splitView.isHidden = false
}
}
-extension DialogTranslateVC : JoyGemSIManagerDelegate {
-
+extension DcDialogTranslateVC : JoyGemSIManagerDelegate {
+
func siManagerStopped(_ manager: JoyGemSIManager, reason code: JoyGemRecordStopCode) {
if let model = dataList.last,model.send.isEmpty{
}
}
-extension DialogTranslateVC : JoyGemSIDeviceManagerDelegate {
-
+extension DcDialogTranslateVC : JoyGemSIDeviceManagerDelegate {
+
func siDeviceManagerStopped(_ manager: JoyGemSIDeviceManager, reason code: JoyGemRecordStopCode) {
}
-extension DialogTranslateVC : PcmSoundDelegate {
+extension DcDialogTranslateVC : PcmSoundDelegate {
func remaining(_ frames: UInt) {
Logger.debug("[frame:\(frames)]");
}
--- /dev/null
+//
+// 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)
+ }
+ }
+}
--- /dev/null
+//
+// 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()
+ }
+ }
+}
--- /dev/null
+//
+// 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()
+ }
+ }
+}
isTtsPlaying = true
Logger.debug("开始播放")
JoyGemChatManager.shared().playAudioStart()
- App.shared.preparePlay()
+ CCAiManager.preparePlayBlueTooth()
}
Logger.debug("收到音频数据", data?.count ?? 0)
PcmSound.sharedInstance().feed(data)
case .startRecord:
if (isTtsPlaying) {
isTtsPlaying = false
- App.shared.preparePlay()
+ CCAiManager.preparePlayBlueTooth()
}
break
case .stopRecord:
--- /dev/null
+//
+// 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) {
+
+ }
+}
--- /dev/null
+//
+// JoyDialog.swift
+// ShenYin
+//
+// Created by liangshi on 2025/12/29.
+//
+
+class JoyDialog: NSObject {
+
+
+
+
+
+
+
+}
}
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)
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()
}
}
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
--- /dev/null
+//
+// 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)]");
+ }
+}
//
-// CallingRecordVC.swift
+// JoyCallingRecordVC.swift
// ShenYin
//
// Created by liangshi on 2025/8/6.
import UIKit
import AVFAudio
-class CallingRecordVC:BaseVC{
+class JoyCallingRecordVC:BaseVC{
let toolBar = FieldRecordBar()
let textView = UITextView()
let rhythView = RhythView()
}
}
-extension CallingRecordVC:FieldRecordBarDelegate{
+extension JoyCallingRecordVC:FieldRecordBarDelegate{
func fieldRecordBar(_ bar: FieldRecordBar, languageChange language: CCAiLanguage) {
JoySdkManager.shared.callRecord.setCallRecoordLanage(language)
AiLanguageCache.save(type: .thly, lan: language)
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))
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")
}
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)
}
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(){
}
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()
}
}
-extension ChatVC:UITableViewDelegate,UITableViewDataSource {
+extension JoyChatVC:UITableViewDelegate,UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return JoySdkManager.shared.chat.dataList.count
}
//
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()
}
}
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()
}
}
}
-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
}
}
}
-
-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) {
-
- }
-}
--- /dev/null
+//
+// 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)]");
+ }
+}
//
-// StereoCallVC.swift
+// JoyStereoCallVC.swift
// ShenYin
//
// Created by liangshi on 2025/11/26.
import UIKit
import AVFAudio
-class StereoCallVC:BaseVC{
+class JoyStereoCallVC:BaseVC{
let toolBar = FieldRecordBar()
let line = UIView()
let selfTextView = UITextView()
}
}
-extension StereoCallVC:FieldRecordBarDelegate{
+extension JoyStereoCallVC:FieldRecordBarDelegate{
func fieldRecordBar(_ bar: FieldRecordBar, languageChange language: CCAiLanguage) {
JoySdkManager.shared.callRecord.setCallRecoordLanage(language)
AiLanguageCache.save(type: .thly, lan: language)
--- /dev/null
+//
+// 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()
+ }
+ }
+}
import UIKit
import AVFAudio
-class FieldRecordingVC:BaseVC{
+class JoyXclyVC:BaseVC{
let toolBar = FieldRecordBar()
let textView = UITextView()
let rhythView = RhythView()
func setupSdk() {
JoySdkManager.shared.record.recordFinish = {[weak self] path in
guard let self = self else { return }
- Logger.debug("0000000 ===",textView.text ?? "")
if !needSave {
return
}
}
}
-extension FieldRecordingVC:FieldRecordBarDelegate{
+extension JoyXclyVC:FieldRecordBarDelegate{
func fieldRecordBar(_ bar: FieldRecordBar, languageChange language: CCAiLanguage) {
JoySdkManager.shared.record.setRecoordLanage(language)
AiLanguageCache.save(type: .xcly, lan: language)
// Created by liangshi on 2025/9/3.
//
import UIKit
-import JoyGemAI
class ChatNavigationBarItem: BaseView {
let icon = UIImageView()
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)
//
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
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)
+++ /dev/null
-//
-// 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)]");
- }
-}
}
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())
}
}
@objc func chatAction(){
if CCBleManager.shared.isConnetd {
- App.shared.goToPage(ChatVC())
+ CCPage.gotoChat()
}
}
@objc func deviceChange(_ noti:Notification){
//
import UIKit
-import JoyGemAI
+
class TextTranlateVC:BaseVC{
let scrollView = UIScrollView()
let languageItem = LanguageSelectItem()
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
-
}
-
}
}