/* Begin PBXBuildFile section */
3F79C6142E6055E500EC38B9 /* JoyGemAI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3F79C4902E603B4700EC38B9 /* JoyGemAI.framework */; };
3F79C6152E6055E500EC38B9 /* JoyGemAI.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3F79C4902E603B4700EC38B9 /* JoyGemAI.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
- 3FF8A4622E72CBBA00271F9E /* DSWaveformImage in Frameworks */ = {isa = PBXBuildFile; productRef = 3FF8A4612E72CBBA00271F9E /* DSWaveformImage */; };
- 3FF8A4642E72CBBA00271F9E /* DSWaveformImageViews in Frameworks */ = {isa = PBXBuildFile; productRef = 3FF8A4632E72CBBA00271F9E /* DSWaveformImageViews */; };
F2E449CAD5874BE652A69B50 /* Pods_ShenYin.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3862D5561144E20721225B96 /* Pods_ShenYin.framework */; };
/* End PBXBuildFile section */
buildActionMask = 2147483647;
files = (
F2E449CAD5874BE652A69B50 /* Pods_ShenYin.framework in Frameworks */,
- 3FF8A4622E72CBBA00271F9E /* DSWaveformImage in Frameworks */,
3F79C6142E6055E500EC38B9 /* JoyGemAI.framework in Frameworks */,
- 3FF8A4642E72CBBA00271F9E /* DSWaveformImageViews in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
mainGroup = 3F887B932E38D8DC00C98658;
minimizedProjectReferenceProxies = 1;
packageReferences = (
- 3FF8A4602E72CBBA00271F9E /* XCRemoteSwiftPackageReference "DSWaveformImage" */,
);
preferredProjectObjectVersion = 77;
productRefGroup = 3F887B9D2E38D8DC00C98658 /* Products */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
+ CODE_SIGN_ENTITLEMENTS = ShenYin/ShenYin.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = M33X987285;
);
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = ShenYin/Info.plist;
+ INFOPLIST_KEY_CFBundleDisplayName = "深音";
INFOPLIST_KEY_NSBluetoothAlwaysUsageDescription = "扫描附近蓝牙设备需要获得权限";
INFOPLIST_KEY_NSCameraUsageDescription = "修改用户头像、提交意见反馈需要访问相册使用摄像头";
INFOPLIST_KEY_NSMicrophoneUsageDescription = "录音需要使用麦克风";
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
+ CODE_SIGN_ENTITLEMENTS = ShenYin/ShenYin.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = M33X987285;
);
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = ShenYin/Info.plist;
+ INFOPLIST_KEY_CFBundleDisplayName = "深音";
INFOPLIST_KEY_NSBluetoothAlwaysUsageDescription = "扫描附近蓝牙设备需要获得权限";
INFOPLIST_KEY_NSCameraUsageDescription = "修改用户头像、提交意见反馈需要访问相册使用摄像头";
INFOPLIST_KEY_NSMicrophoneUsageDescription = "录音需要使用麦克风";
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
-
-/* Begin XCRemoteSwiftPackageReference section */
- 3FF8A4602E72CBBA00271F9E /* XCRemoteSwiftPackageReference "DSWaveformImage" */ = {
- isa = XCRemoteSwiftPackageReference;
- repositoryURL = "https://github.com/dmrschmidt/DSWaveformImage";
- requirement = {
- kind = upToNextMajorVersion;
- minimumVersion = 14.2.2;
- };
- };
-/* End XCRemoteSwiftPackageReference section */
-
-/* Begin XCSwiftPackageProductDependency section */
- 3FF8A4612E72CBBA00271F9E /* DSWaveformImage */ = {
- isa = XCSwiftPackageProductDependency;
- package = 3FF8A4602E72CBBA00271F9E /* XCRemoteSwiftPackageReference "DSWaveformImage" */;
- productName = DSWaveformImage;
- };
- 3FF8A4632E72CBBA00271F9E /* DSWaveformImageViews */ = {
- isa = XCSwiftPackageProductDependency;
- package = 3FF8A4602E72CBBA00271F9E /* XCRemoteSwiftPackageReference "DSWaveformImage" */;
- productName = DSWaveformImageViews;
- };
-/* End XCSwiftPackageProductDependency section */
};
rootObject = 3F887B942E38D8DC00C98658 /* Project object */;
}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+ LastUpgradeVersion = "1640"
+ version = "1.7">
+ <BuildAction
+ parallelizeBuildables = "YES"
+ buildImplicitDependencies = "YES"
+ buildArchitectures = "Automatic">
+ <BuildActionEntries>
+ <BuildActionEntry
+ buildForTesting = "YES"
+ buildForRunning = "YES"
+ buildForProfiling = "YES"
+ buildForArchiving = "YES"
+ buildForAnalyzing = "YES">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "3F887B9B2E38D8DC00C98658"
+ BuildableName = "ShenYin.app"
+ BlueprintName = "ShenYin"
+ ReferencedContainer = "container:ShenYin.xcodeproj">
+ </BuildableReference>
+ </BuildActionEntry>
+ </BuildActionEntries>
+ </BuildAction>
+ <TestAction
+ buildConfiguration = "Debug"
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ shouldUseLaunchSchemeArgsEnv = "YES"
+ shouldAutocreateTestPlan = "YES">
+ </TestAction>
+ <LaunchAction
+ buildConfiguration = "Debug"
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ launchStyle = "0"
+ useCustomWorkingDirectory = "NO"
+ ignoresPersistentStateOnLaunch = "NO"
+ debugDocumentVersioning = "YES"
+ debugServiceExtension = "internal"
+ allowLocationSimulation = "YES">
+ <BuildableProductRunnable
+ runnableDebuggingMode = "0">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "3F887B9B2E38D8DC00C98658"
+ BuildableName = "ShenYin.app"
+ BlueprintName = "ShenYin"
+ ReferencedContainer = "container:ShenYin.xcodeproj">
+ </BuildableReference>
+ </BuildableProductRunnable>
+ </LaunchAction>
+ <ProfileAction
+ buildConfiguration = "Release"
+ shouldUseLaunchSchemeArgsEnv = "YES"
+ savedToolIdentifier = ""
+ useCustomWorkingDirectory = "NO"
+ debugDocumentVersioning = "YES">
+ <BuildableProductRunnable
+ runnableDebuggingMode = "0">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "3F887B9B2E38D8DC00C98658"
+ BuildableName = "ShenYin.app"
+ BlueprintName = "ShenYin"
+ ReferencedContainer = "container:ShenYin.xcodeproj">
+ </BuildableReference>
+ </BuildableProductRunnable>
+ </ProfileAction>
+ <AnalyzeAction
+ buildConfiguration = "Debug">
+ </AnalyzeAction>
+ <ArchiveAction
+ buildConfiguration = "Release"
+ revealArchiveInOrganizer = "YES">
+ </ArchiveAction>
+</Scheme>
+++ /dev/null
-{
- "originHash" : "eb3e6467aa3b79663b371a20cbe7f1b7c7a6a3c9f98e6945bc9704361b5ae42d",
- "pins" : [
- {
- "identity" : "dswaveformimage",
- "kind" : "remoteSourceControl",
- "location" : "https://github.com/dmrschmidt/DSWaveformImage",
- "state" : {
- "revision" : "4c56578ee10128ee2b2c04c9c5aa73812de722db",
- "version" : "14.2.2"
- }
- }
- ],
- "version" : 3
-}
static func loadCache(){
let list = DeviceCache.shared.getDeviceList()
Logger.debug("缓存设备",list)
- if list.count > 0 {
- CacheManager.shared.currentDevice = list.first
- BleManager.shared.startScanDevice()
- App.shared.enter()
- }
+// if list.count > 0 {
+// CacheManager.shared.currentDevice = list.first
+// BleManager.shared.startScanDevice()
+// App.shared.enter()
+// }
}
}
+++ /dev/null
-{
- "info" : {
- "author" : "xcode",
- "version" : 1
- }
-}
--- /dev/null
+{
+ "images" : [
+ {
+ "filename" : "Group 2094.png",
+ "idiom" : "universal",
+ "scale" : "1x"
+ },
+ {
+ "filename" : "Group 2094@2x.png",
+ "idiom" : "universal",
+ "scale" : "2x"
+ },
+ {
+ "filename" : "Group 2094@3x.png",
+ "idiom" : "universal",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
--- /dev/null
+{
+ "images" : [
+ {
+ "filename" : "Group 2288.png",
+ "idiom" : "universal",
+ "scale" : "1x"
+ },
+ {
+ "filename" : "Group 2288@2x.png",
+ "idiom" : "universal",
+ "scale" : "2x"
+ },
+ {
+ "filename" : "Group 2288@3x.png",
+ "idiom" : "universal",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
--- /dev/null
+{
+ "images" : [
+ {
+ "filename" : "Group 2289.png",
+ "idiom" : "universal",
+ "scale" : "1x"
+ },
+ {
+ "filename" : "Group 2289@2x.png",
+ "idiom" : "universal",
+ "scale" : "2x"
+ },
+ {
+ "filename" : "Group 2289@3x.png",
+ "idiom" : "universal",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
--- /dev/null
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "scale" : "1x"
+ },
+ {
+ "filename" : "Polygon_2@2x.png",
+ "idiom" : "universal",
+ "scale" : "2x"
+ },
+ {
+ "filename" : "Polygon_2@3x.png",
+ "idiom" : "universal",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
<string>bluetooth-central</string>
<string>audio</string>
</array>
- <key>CFBundleDisplayName</key>
- <string>深音</string>
</dict>
</plist>
//MARK: 初始化SDK
JoyGemAIManager.shared().initSdk()
//MARK: 配置日志类型
- JoyGemAIManager.shared().setLogMode(JoyGemLogMode.close)
+ JoyGemAIManager.shared().setLogMode(JoyGemLogMode.all)
let fileManager = FileManager.default
do {
// 创建 log 文件夹
try fileManager.createDirectory(at: logDirectory, withIntermediateDirectories: true, attributes: nil)
- print("log 文件夹已创建: \(logDirectory.path)")
+ Logger.debug("log 文件夹已创建: \(logDirectory.path)")
} catch {
- print("创建 log 文件夹时出错: \(error.localizedDescription)")
+ Logger.debug("创建 log 文件夹时出错: \(error.localizedDescription)")
}
} else {
- print("log 文件夹已存在: \(logDirectory.path)")
+ Logger.debug("log 文件夹已存在: \(logDirectory.path)")
}
// 获取当前日期并格式化
if !fileManager.fileExists(atPath: filePath.path) {
// 创建空文件
fileManager.createFile(atPath: filePath.path, contents: nil, attributes: nil)
- print("日志文件已创建: \(filePath.path)")
+ Logger.debug("日志文件已创建: \(filePath.path)")
} else {
- print("日志文件已存在: \(filePath.path)")
+ Logger.debug("日志文件已存在: \(filePath.path)")
}
//MARK: 配置日志路径
JoyGemAIManager.shared().setLogPath(filePath.path)
} else {
- print("无法找到应用支持目录")
+ Logger.debug("无法找到应用支持目录")
}
//MARK: 配置缓存路径
App.shared.window = window
window?.rootViewController = BaseTC()
-// window?.rootViewController = UINavigationController(rootViewController: TextTranlateVC())
+ window?.rootViewController = UINavigationController(rootViewController: RecordTranslateVC())
AppLaunch.start()
}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict/>
+</plist>
//
import UIKit
-
+import JoyGemAI
+import AVFAudio
class CallingRecordVC:BaseVC{
let toolBar = FieldRecordBar()
let textView = UITextView()
let rhythView = RhythView()
+ var messageList : [(String,UInt)] = []
+ var recordFilePath : String?
+
+
override func viewDidLoad() {
super.viewDidLoad()
}
override func setupViews() {
navBar.title = "通话录音"
+ toolBar.delegate = self
+ toolBar.hasRightItem = false
+
textView.do { v in
v.isEditable = false
v.font = .regular(14)
+ v.text = ""
v.textColor = .hexString("#121212")
v.contentInset = UIEdgeInsets(top: 0, left: 18, bottom: 0, right: 18)
}
make.bottom.equalTo(toolBar.snp.top)
}
}
+ override func setupData() {
+ JoyGemCallManager.shared().delegate = self
+// JoyGemCallManager.shared().stereoDelegate = self//双声道
+ }
func scrollToBottom(){
+ }
+ ///播放最后一次录音文件
+ @objc func playRecord() {
+ guard let file = recordFilePath else {return;}
+
+// do {
+// // 创建 AVAudioPlayer 实例
+// audioPlayer = try AVAudioPlayer(contentsOf: URL(fileURLWithPath: file))
+// audioPlayer?.prepareToPlay() // 准备播放
+// audioPlayer?.play() // 播放音频
+// } catch {
+// debugPrint("播放音频时发生错误: \(error.localizedDescription)")
+// }
+ }
+}
+
+extension CallingRecordVC:FieldRecordBarDelegate{
+ func fieldRecordBar(_ bar: FieldRecordBar, languageChange language: AiSupportLanguage) {
+ JoyGemCallManager.shared().originalLanguage = language.value
+ }
+
+ func fieldRecordBar(_ bar: FieldRecordBar, stateChange state: RecordSate) {
+ if state == .start {
+ textView.text = ""
+ messageList.removeAll()
+ JoyGemCallManager.shared().start()
+ }
+ else if state == .stop{
+ JoyGemCallManager.shared().stop()
+ bar.recordBtn.startAnimating {[weak self] in
+ guard let self = self else{return}
+ // toolBar.recordBtn.stopCli()
+ let v = PopTextField()
+ v.show()
+ v.cancelTap = {
+
+ }
+ v.okTap = {
+
+ }
+ }
+ }
+ }
+}
+extension CallingRecordVC : JoyGemCallManagerDelegate {
+
+ func callManagerStopped(_ manager: JoyGemCallManager, reason code: JoyGemRecordStopCode) {
+ if (code == .actively) {
+ YLprogressHUD.showAutoHiden( "用户主动结束")
+ } else {
+ YLprogressHUD.showAutoHiden( "自动结束")
+ ///判断当前蓝牙是否连接从而确定是否因为蓝牙断开造成的录音断开
+ }
+ }
+
+ func callManagerNotonCall(_ manager: JoyGemCallManager) {
+ YLprogressHUD.showAutoHiden("未处于通话状态")
+ }
+
+ func callManager(_ manager: JoyGemCallManager, saveFile path: String) {
+ debugPrint("path: \(path)")
+ recordFilePath = path
+ }
+
+ func callManager(_ manager: JoyGemCallManager, stateChange code: JoyGemStateCode) {
+ debugPrint("code: \(code)")
+ }
+
+ func callManager(_ manager: JoyGemCallManager, onError code: JoyGemErrorCode) {
+
+ }
+
+ func callManager(_ manager: JoyGemCallManager, didUpdateAsr model: JoyGemAsrModel) {
+ debugPrint("text: \(model.text),index: \(model.index)")
+ 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")
+ }
+
+}
+
+extension CallingRecordVC:JoyGemCallStereoDelegate{
+ func callManager(_ manager: JoyGemCallManager, didUpdateStereoAsr model: JoyGemAsrModel, channel index: UInt) {
+
}
}
try AVAudioSession.sharedInstance().setCategory(.playback, options: [options])
try AVAudioSession.sharedInstance().setActive(true)
} catch {
- print(error)
+ Logger.debug(error)
}
}
// stateLabel.text = "正在录音"
--- /dev/null
+//
+// Chinese2EnglishCell.swift
+// ShenYin
+//
+// Created by liangshi on 2025/9/15.
+//
+
+class Chinese2EnglishCellModel{
+ var send = ""
+ var response = ""
+
+ init(send: String = "", response: String = "") {
+ self.send = send
+ self.response = response
+ }
+}
+
+
+
+
+class Chinese2EnglishCell:BaseTableViewCell{
+ let bgView = UIView()
+ let line = BrokenLineView()
+ let originalLabel = UILabel()
+ let translationLabel = UILabel()
+ let copyIcon = UIImageView()
+
+ override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
+ super.init(style: style, reuseIdentifier: reuseIdentifier)
+
+ }
+
+ required init?(coder: NSCoder) {
+ fatalError("init(coder:) has not been implemented")
+ }
+
+ override func setupViews() {
+ contentView.backgroundColor = .rgb(0xF6F6F6)
+
+ bgView.do { v in
+ v.yc.setCornerRadius(12)
+ v.backgroundColor = .white
+ }
+ originalLabel.do { v in
+ v.font = .regular(14)
+ v.textColor = .hexString("#606D80")
+ v.numberOfLines = 0
+ v.textAlignment = .left
+ }
+ translationLabel.do { v in
+ v.font = .regular(14)
+ v.textColor = .hexString("#121212")
+ v.numberOfLines = 0
+ v.textAlignment = .left
+ }
+ line.do { v in
+ // v.backgroundColor = .hexString("#93C6EC")
+ }
+ copyIcon.do { v in
+ v.image = UIImage(named: "play_animate_3")
+ v.animationImages = [UIImage(named: "play_animate_1")!,UIImage(named: "play_animate_2")!,UIImage(named: "play_animate_3")!]
+ v.animationDuration = 0.6
+ v.animationRepeatCount = 0
+ v.yc.addTap(target: self, action: #selector(copyAction))
+ }
+ contentView.addSubview(bgView)
+ bgView.addSubviews([originalLabel,translationLabel,line,copyIcon])
+ }
+
+ override func setupLayout() {
+ bgView.snp.makeConstraints { make in
+ make.top.bottom.equalToSuperview().inset(10)
+ make.width.lessThanOrEqualTo(kScreenWid - 58)
+ make.left.equalToSuperview().inset(18)
+ }
+
+ originalLabel.snp.makeConstraints { make in
+ make.top.equalToSuperview().inset(14)
+ make.left.equalToSuperview().inset(12)
+ make.width.lessThanOrEqualTo(kScreenWid - 82)
+ }
+ line.snp.makeConstraints { make in
+ make.left.right.equalToSuperview().inset(12)
+ make.height.equalTo(1)
+ make.top.equalTo(originalLabel.snp.bottom).offset(15)
+ }
+
+ translationLabel.snp.makeConstraints { make in
+ make.top.equalTo(line.snp.bottom).offset(15)
+ make.left.equalToSuperview().inset(12)
+ make.width.lessThanOrEqualTo(kScreenWid - 82)
+ make.bottom.equalToSuperview().inset(47)
+ }
+ copyIcon.snp.makeConstraints { make in
+ make.right.equalToSuperview().inset(12)
+ make.size.equalTo(24)
+ make.bottom.equalToSuperview().inset(10)
+ }
+ }
+
+ var data:Chinese2EnglishCellModel!{
+ didSet{
+ originalLabel.text = data.send
+ translationLabel.text = data.response
+ }
+ }
+ @objc func copyAction(){
+// UIPasteboard.general.string = data.send + "\n" + data.response
+// YLprogressHUD.showAutoHiden("已拷贝")
+ if copyIcon.isAnimating {
+ copyIcon.stopAnimating()
+ }
+ else{
+ copyIcon.startAnimating()
+ }
+ }
+}
+
+
--- /dev/null
+//
+// Chinese2EnglishVC.swift
+// ShenYin
+//
+// Created by liangshi on 2025/8/6.
+//
+
+import UIKit
+import JoyGemAI
+import AVFAudio
+class Chinese2EnglishVC:BaseVC{
+ let toolBar = RecordBar()
+ let tableView = UITableView(frame: CGRectZero, style: .plain)
+ var dataList:[(Chinese2EnglishCellModel,UInt)] = []
+ var pcmData = Data()
+ override func viewDidLoad() {
+ super.viewDidLoad()
+ }
+ override func setupViews() {
+ navBar.title = "中英互译"
+ navBar.rightView.setImage(UIImage(named: "help_icon"), for: .normal)
+ toolBar.delegate = self
+
+ tableView.backgroundColor = .clear
+ tableView.delegate = self
+ tableView.dataSource = self
+ tableView.estimatedRowHeight = 60
+ tableView.separatorStyle = .none
+ tableView.register(Chinese2EnglishCell.self, forCellReuseIdentifier: Chinese2EnglishCell.reuseID)
+ view.addSubview(tableView)
+ view.addSubview(toolBar)
+
+ }
+ 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)
+ }
+ }
+ override func setupData() {
+ JoyGemSIDeviceManager.shared().delegate = self
+
+ PcmSound.sharedInstance().delegate = self;
+ PcmSound.sharedInstance().setup(16000, withchannels: 1)
+// do {
+// try AVAudioSession.sharedInstance().setActive(true)
+// } catch {
+// print(error)
+// }
+ }
+ 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)
+ }
+ }
+}
+extension Chinese2EnglishVC:RecordBarDelegate{
+
+ func recordBar(_ bar: RecordBar, stateChange state: RecordBarSate) {
+ if state == .start {
+ pcmData = Data()
+ dataList.removeAll()
+ refreshData()
+
+ JoyGemSIDeviceManager.shared().originalLanguage = bar.leftItem.language.value
+ JoyGemSIDeviceManager.shared().targetLanguage = bar.rightItem.language.value
+ JoyGemSIDeviceManager.shared().start()
+// do {
+// try AVAudioSession.sharedInstance().setCategory(.playAndRecord, options: [.defaultToSpeaker])
+// try AVAudioSession.sharedInstance().overrideOutputAudioPort(.speaker)
+// } catch {
+// print(error)
+// }
+ }
+ else if state == .stop{
+ JoyGemSIDeviceManager.shared().stop()
+ bar.recordBtn.startAnimating {[weak self] in
+ guard let self = self else{return}
+ // toolBar.recordBtn.stopCli()
+ let v = PopTextField()
+ v.show()
+ v.cancelTap = {
+
+ }
+ v.okTap = {
+
+ }
+ }
+ }
+ }
+}
+extension Chinese2EnglishVC: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]
+ cell.data = vallue
+ return cell
+ }
+ func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
+
+ }
+}
+
+extension Chinese2EnglishVC:JoyGemSIDeviceManagerDelegate{
+ func siDeviceManagerStopped(_ manager: JoyGemSIDeviceManager, reason code: JoyGemRecordStopCode) {
+ Logger.debug("停止",code.rawValue)
+ if code == .passively {
+ toolBar.recordBtn.isSelected = false
+ }
+ }
+
+ func siDeviceManager(_ manager: JoyGemSIDeviceManager, didUpdateRaw model: JoyGemAsrModel) {
+ debugPrint("text: \(model.text),index: \(model.index)")
+ let msg = dataList.firstIndex (where: { (str,idx) in
+ return idx == model.index;
+ })
+ if (msg == nil) {
+ dataList.append((Chinese2EnglishCellModel(send: model.text),model.index))
+ } else {
+ let (item,_) = dataList[msg!]
+ item.send = model.text
+ }
+//
+// ///监听到一句话结束,自动结束当前录音
+// if (model.definite == true) {
+// stopEarRecord()
+// }
+ refreshData()
+ }
+
+ func siDeviceManager(_ manager: JoyGemSIDeviceManager, didUpdateTranslate model: JoyGemAsrModel) {
+ let msg = dataList.firstIndex (where: { (str,idx) in
+ return idx == model.index;
+ })
+ if (msg == nil) {
+ dataList.append((Chinese2EnglishCellModel(response: model.text),model.index))
+ } else {
+ let (item,_) = dataList[msg!]
+ item.response = model.text
+ }
+ refreshData()
+ }
+
+ func siDeviceManager(_ manager: JoyGemSIDeviceManager, didUpdateAudioData data: Data) {
+ pcmData.append(data)
+ PcmSound.sharedInstance().feed(data)
+ }
+
+ func siDeviceManager(_ manager: JoyGemSIDeviceManager, update mode: JoyGemRecordMode) {
+
+ }
+
+ func siDeviceManager(_ manager: JoyGemSIDeviceManager, saveFile path: String) {
+ debugPrint("path: \(path)")
+ }
+
+ func siDeviceManager(_ manager: JoyGemSIDeviceManager, stateChange code: JoyGemStateCode) {
+ debugPrint("code: \(code)")
+ }
+
+ func siDeviceManager(_ manager: JoyGemSIDeviceManager, onError code: JoyGemErrorCode) {
+
+ }
+}
+extension Chinese2EnglishVC:PcmSoundDelegate{
+ func remaining(_ frames: UInt) {
+
+ }
+
+
+}
+++ /dev/null
-//
-// Chinese2EnglishVC.swift
-// ShenYin
-//
-// Created by liangshi on 2025/8/6.
-//
-
-import UIKit
-
-class Chinese2EnglishVC:BaseVC{
- let toolBar = RecordBar()
- let tableView = UITableView(frame: CGRectZero, style: .plain)
- var dataList = [1,2,3]
- override func viewDidLoad() {
- super.viewDidLoad()
- }
- override func setupViews() {
- navBar.title = "中英互译"
- navBar.rightView.setImage(UIImage(named: "help_icon"), for: .normal)
-
-
- tableView.backgroundColor = .clear
- tableView.delegate = self
- tableView.dataSource = self
- tableView.estimatedRowHeight = 60
- tableView.separatorStyle = .none
- tableView.register(TranslateCell.self, forCellReuseIdentifier: TranslateCell.reuseID)
- view.addSubview(tableView)
- view.addSubview(toolBar)
-
- }
- 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)
- }
- }
- override func navRightViewTapped() {
- let tip = RecordTipsView(type: .chinese2english)
- tip.show()
- }
- func scrollToBottom(){
- if dataList.count > 0 {
- tableView.scrollToRow(at: IndexPath(row: dataList.count - 1, section: 0), at: .none, animated: true)
- }
- }
-}
-extension Chinese2EnglishVC:UITableViewDelegate,UITableViewDataSource {
- func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
- return dataList.count
- }
-
- func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
- let cell = TranslateCell.cell(withTableView: tableView, indexPath: indexPath)
- // cell.data = dataList[indexPath.row]
- return cell
- }
- func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
-
- }
-}
let rightItem = RecordBarButton()
var state:RecordSate = .stop
var delegate:FieldRecordBarDelegate?
+ var hasRightItem:Bool = true
override func setupViews() {
backgroundColor = .white
rightItem.state = .start
rightItem.isHidden = !recordBtn.isSelected
+ if hasRightItem == false{
+ rightItem.isHidden = true
+ }
+
if recordBtn.isSelected {
- leftItem.alpha = 0.5
+ leftItem.alpha = 0.3
}
else{
leftItem.alpha = 1
try AVAudioSession.sharedInstance().setCategory(.playback, options: [options])
try AVAudioSession.sharedInstance().setActive(true)
} catch {
- print(error)
+ Logger.debug(error)
}
}
func scrollToBottom(){
// Created by liangshi on 2025/8/27.
//
+enum RecordBarSate:Int{
+ case start
+ case stop
+}
+
+protocol RecordBarDelegate {
+// func recordBar(_ bar:RecordBar,languageChange language:AiSupportLanguage)
+ func recordBar(_ bar:RecordBar,stateChange state:RecordBarSate)
+}
+
class RecordBar: BaseView {
- let recordBtn = UIButton()
+ let recordBtn = LoadingButton()
let leftItem = RecordBarItem()
let rightItem = RecordBarItem()
-
+ var delegate:RecordBarDelegate?
override func setupViews() {
backgroundColor = .white
v.setImage(UIImage(named: "btn_record_stop"), for: .selected)
v.addTarget(self, action: #selector(recordBtnAction), for: .touchUpInside)
}
- leftItem.do {v in
-// v.backgroundColor = .green
+ leftItem.do { (v) in
+ v.language = AiSupportLanguage.chinse
+ v.yc.addTap(target: self, action: #selector(leftAction))
}
- rightItem.do {v in
-// v.backgroundColor = .yellow
+ rightItem.do { (v) in
+
+ v.language = AiSupportLanguage.english
+ v.yc.addTap(target: self, action: #selector(rightAction))
}
}
make.center.equalToSuperview()
}
leftItem.snp.makeConstraints { make in
- make.size.equalTo(56)
+ make.height.equalTo(56)
make.centerY.equalTo(recordBtn)
make.right.equalToSuperview().offset(-(kScreenWid/2 + 60))
}
rightItem.snp.makeConstraints { make in
- make.size.equalTo(56)
+ make.height.equalTo(56)
make.centerY.equalTo(recordBtn)
make.left.equalToSuperview().offset(kScreenWid/2 + 60)
}
@objc func recordBtnAction(){
recordBtn.isSelected = !recordBtn.isSelected
+ if recordBtn.isSelected {
+ delegate?.recordBar(self, stateChange: .start)
+ }
+ else{
+ delegate?.recordBar(self, stateChange: .stop)
+ }
+
+ if recordBtn.isSelected {
+ leftItem.alpha = 0.3
+ rightItem.alpha = 0.3
+ }
+ else{
+ leftItem.alpha = 1
+ rightItem.alpha = 1
+ }
+ }
+
+ @objc func leftAction(){
+ if recordBtn.isSelected {
+ return
+ }
+ let v = LanguagePickerView(languages: AiSupportLanguage.translate,isLeft: true,left: leftItem.language,right: rightItem.language)
+ v.selectedIndex = v.list.firstIndex(where: {$0.title == leftItem.language.title}) ?? 0
+ v.show()
+ v.didSelected = {[weak self] (index,value) in
+ guard let self = self else { return }
+ if index == 0 {
+ leftItem.language = value
+ }
+ else{
+ rightItem.language = value
+ }
+ }
+ }
+ @objc func rightAction(){
+ if recordBtn.isSelected {
+ return
+ }
+ let v = LanguagePickerView(languages: AiSupportLanguage.translate,isLeft: false,left: leftItem.language,right: rightItem.language)
+ v.selectedIndex = v.list.firstIndex(where: {$0.title == rightItem.language.title}) ?? 0
+ v.show()
+ v.didSelected = {[weak self] (index,value) in
+ guard let self = self else { return }
+ if index == 0 {
+ leftItem.language = value
+ }
+ else{
+ rightItem.language = value
+ }
+ }
}
class RecordBarItem: BaseView {
let titleLabel = UILabel()
let valueLabel = UILabel()
+ var language:AiSupportLanguage = AiSupportLanguage.chinse{
+ didSet{
+ titleLabel.text = language.title
+ }
+ }
+
override func setupViews() {
// backgroundColor = .red
addSubviews([titleLabel,valueLabel,icon])
icon.do { v in
- v.image = UIImage(named: "")
- v.backgroundColor = .black
+ v.image = UIImage(named: "polygon")
}
titleLabel.do { v in
v.textColor = .rgb(0x121212)
v.font = .bold(16)
- v.text = "中文"
}
valueLabel.do { v in
v.textColor = .rgb(0x666666)
titleLabel.snp.makeConstraints { make in
make.right.equalTo(icon.snp.left).offset(-5)
make.centerY.equalTo(icon)
+ make.left.equalToSuperview()
}
valueLabel.snp.makeConstraints { make in
make.right.equalTo(titleLabel)
try AVAudioSession.sharedInstance().setCategory(.playback, options: [options])
try AVAudioSession.sharedInstance().setActive(true)
} catch {
- print(error)
+ Logger.debug(error)
}
}
override func navRightViewTapped() {
import UIKit
-
+import JoyGemAI
+import AVFoundation
class RecordTranslateVC:BaseVC{
let toolBar = RecordBar()
- let tableView = UITableView(frame: CGRectZero, style: .plain)
- var dataList = [1,2,3]
+ let textView = PlaceHolderTextView()
+ let translateTextView = PlaceHolderTextView()
+ var messageList : [(String,UInt)] = []
+ var translateList : [(String,UInt)] = []
+ var recordFilePath : String?
+
override func viewDidLoad() {
super.viewDidLoad()
}
override func setupViews() {
navBar.title = "同传听译"
navBar.rightView.setImage(UIImage(named: "help_icon"), for: .normal)
+ view.addSubviews([toolBar,textView,translateTextView])
+ toolBar.delegate = self
- tableView.backgroundColor = .clear
- tableView.delegate = self
- tableView.dataSource = self
- tableView.estimatedRowHeight = 60
- tableView.separatorStyle = .none
- tableView.register(TranslateCell.self, forCellReuseIdentifier: TranslateCell.reuseID)
- view.addSubview(tableView)
- view.addSubview(toolBar)
-
+ textView.do { v in
+ v.isEditable = false
+ v.font = .regular(14)
+ v.text = ""
+ v.textColor = .hexString("#121212")
+ v.contentInset = UIEdgeInsets(top: 0, left: 18, bottom: 0, right: 18)
+ }
+ textView.do { v in
+ v.isEditable = false
+ v.font = .regular(14)
+ v.text = ""
+ v.textColor = .hexString("#121212")
+ v.contentInset = UIEdgeInsets(top: 0, left: 18, bottom: 0, right: 18)
+ v.yc.setBorder(width: 1, color: .black)
+ }
+ translateTextView.do { v in
+ v.isEditable = false
+ v.font = .regular(14)
+ v.text = ""
+ v.textColor = .hexString("#121212")
+// v.yc.addgradientLayer(colors: [.hexString("#0092E1").withAlphaComponent(0.1),.white], direction: .top2down,rect: CGRect(x: 0, y: 0, width: kScreenWid, height: (kScreenHei - kNavBtm - 120 - 10)/2))
+// v.contentInset = UIEdgeInsets(top: 0, left: 18, bottom: 0, right: 18)
+ v.yc.setBorder(width: 1, color: .black)
+// 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.height.equalTo(120)
make.bottom.equalToSuperview().inset(20)
}
- tableView.snp.makeConstraints { make in
+ 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)
+ }
override func navRightViewTapped() {
let tip = RecordTipsView(type: .tongchuan)
tip.show()
}
func scrollToBottom(){
- if dataList.count > 0 {
- tableView.scrollToRow(at: IndexPath(row: dataList.count - 1, section: 0), at: .none, animated: true)
- }
+ let range = NSMakeRange(textView.text.count - 1, 1)
+ textView.scrollRangeToVisible(range)
}
}
-extension RecordTranslateVC:UITableViewDelegate,UITableViewDataSource {
- func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
- return dataList.count
+
+extension RecordTranslateVC:RecordBarDelegate{
+
+ func recordBar(_ bar: RecordBar, stateChange state: RecordBarSate) {
+ textView.text = "123"
+
+// 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.recordBtn.startAnimating {[weak self] in
+// guard let self = self else{return}
+// // toolBar.recordBtn.stopCli()
+// let v = PopTextField()
+// v.show()
+// v.cancelTap = {
+//
+// }
+// v.okTap = {
+//
+// }
+// }
+// }
+ }
+}
+
+extension RecordTranslateVC : JoyGemSIManagerDelegate {
+ func siManagerStopped(_ manager: JoyGemSIManager, reason code: JoyGemRecordStopCode) {
+ if (code == .actively) {
+ YLprogressHUD.showAutoHiden("用户主动结束")
+ } else {
+ YLprogressHUD.showAutoHiden("用户被动结束")
+ }
}
- func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
- let cell = TranslateCell.cell(withTableView: tableView, indexPath: indexPath)
- // cell.data = dataList[indexPath.row]
- return cell
+ func siManager(_ manager: JoyGemSIManager, didUpdateRaw model: JoyGemAsrModel) {
+ debugPrint("text: \(model.text),index: \(model.index)")
+ 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")
}
- func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
+
+ func siManager(_ manager: JoyGemSIManager, didUpdateTranslate model: JoyGemAsrModel) {
+ debugPrint("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")
+ }
+
+
+ func siManager(_ manager: JoyGemSIManager, saveFile path: String) {
+ debugPrint("path: \(path)")
+ recordFilePath = path
+ }
+
+ func siManager(_ manager: JoyGemSIManager, stateChange code: JoyGemStateCode) {
+ debugPrint("code: \(code)")
+ }
+
+ func siManager(_ manager: JoyGemSIManager, didUpdateAudioData data: Data) {
+ PcmSound.sharedInstance().feed(data)
+ }
+
+ func siManager(_ manager: JoyGemSIManager, onError code: JoyGemErrorCode) {
+
+ }
+}
+
+extension RecordTranslateVC : PcmSoundDelegate {
+
+ func remaining(_ frames: UInt) {
+ debugPrint("[frame:\(frames)]");
}
}
for result: LBXScanResult in arrayResult {
if let str = result.strScanned {
- print(str)
+ Logger.debug(str)
}
}
func topVC()->UIViewController!{
guard let vc = window?.rootViewController else{
- print("页面异常!!!")
+ Logger.debug("页面异常!!!")
return UIViewController()
}
return getTopViewController(from: vc)
let data = self.textView.text.data(using:.utf8)
do {
try data?.write(to: url, options:.atomic)
- print(filePath)
+ Logger.debug(filePath)
self.saveFile(withFilePath: filePath)
} catch {
- print(error)
+ Logger.debug(error)
}
}
--- /dev/null
+//
+// PlaceHolderTextView.swift
+// ShenYin
+//
+// Created by liangshi on 2025/9/13.
+//
+
+import UIKit
+
+extension UITextView {
+ @objc dynamic var observedText: String {
+ return text
+ }
+}
+
+class PlaceHolderTextView: UITextView {
+ let label = UILabel()
+
+ var placeHolde:String = ""{
+ didSet{
+ label.text = placeHolde
+ }
+ }
+
+
+ override init(frame: CGRect, textContainer: NSTextContainer?) {
+ super.init(frame: frame, textContainer: textContainer)
+
+ self.addObserver(self, forKeyPath: "observedText", options: [.new,.old], context: nil)
+
+
+ label.do { v in
+ v.text = "此处将显示原文"
+ v.textAlignment = .center
+ v.textColor = .hexString("#666666")
+ v.font = .regular(16)
+ }
+ addSubview(label)
+ label.snp.makeConstraints { make in
+ make.center.equalToSuperview()
+ }
+ }
+
+ required init?(coder: NSCoder) {
+ fatalError("init(coder:) has not been implemented")
+ }
+
+ override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
+ if keyPath == "observedText", let textView = object as? UITextView {
+ if let newText = change?[.newKey] as? String {
+ Logger.debug("Text changed to: \(newText)")
+ if newText.isNotEmpty {
+ label.isHidden = true
+ }
+ else{
+ label.isHidden = false
+ }
+ }
+ else{
+ label.isHidden = false
+ }
+ }
+ }
+
+
+}
let language = segView.currentLanguage
selectedIndex = list.firstIndex(where: {$0 == language})!
tableView.reloadData()
+ didSelected?(0 ,segView.leftLabel.language)
+ didSelected?(1 ,segView.rightLabel.language)
}
}
extension LanguagePickerView:UITableViewDelegate,UITableViewDataSource {
var okTap:(()->Void)?
init() {
- super.init()
+ super.init(backgroundEnabled: false)
}
required init?(coder: NSCoder) {
m.left.right.equalToSuperview().inset(30)
}
actionbtn.snp.makeConstraints { (m) in
- m.top.equalTo(desLabel2.snp.bottom).offset(40)
+ m.bottom.equalToSuperview().inset(30)
m.left.right.equalToSuperview().inset(52)
m.height.equalTo(46)
}
hide()
}
@objc func btnAction(){
-
+ hide()
}
}