full.append(data)
full.addCrc()
if let connectPeripheral = connectPeripheral, let writeCha = writeCha {
-// DCLogger.debug("发",writeCha.uuid.uuidString,full.joined())
+ DCLogger.debug("发",writeCha.uuid.uuidString,full.joined())
connectPeripheral.writeValue(full, for: writeCha, type:.withoutResponse)
}
}
}
else if data[1] == 0x0D{
let subData = data.subdata(in: 2..<data.count - 3)
- //DCLogger.debug("传给字节sdk",subData.joined())
+ DCLogger.debug("传给字节sdk",subData.joined())
StarburstSdk.shared().receiveBleData(subData)
}else{
receiveNoticeData(data)
config.ttsFormat = ttsFormat
StarburstSdk.shared().setupDeviceRecord(with: nil, asrConfig: config) { config in
- DCLogger.debug("录音类型 \(config!.type == 0x00 ? "实时录音" : "通话录音")")
+// DCLogger.debug("录音类型 \(config!.type == 0x00 ? "实时录音" : "通话录音")")
} 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)")
+// DCLogger.debug("asr = \(text)")
asrSplitCallback?(DCAsrModel(text: text, index: model.definiteIndex, definite: model.definite))
let msg = asrList.firstIndex (where: { (str,idx) in
return idx == model.definiteIndex;
}
}
- /// 开始
- public func start(){
+
+ /// 开始录音
+ /// - Parameter type: /0x00实时录音,0x0c通话录音,0x30字幕翻译
+ public func start(type:Int = 0x00){
asrList = []
asrText = ""
setupRecord()
let config = StarburstBleAsrConfig()
- config.type = 0x00 // 0x00实时录音,0x0c通话录音
+ config.type = type // 0x00实时录音,0x0c通话录音
StarburstSdk.shared().startDeviceRecord(config)
}
Sdk/DcSdk/tool/DcChinese2Enligsh.swift,
Sdk/DcSdk/tool/DcSdkManager.swift,
Sdk/DcSdk/tool/DcStereo.swift,
+ Sdk/DcSdk/tool/DcSubTitle.swift,
Sdk/DcSdk/tool/DcTcty.swift,
Sdk/DcSdk/tool/DcXcly.swift,
Sdk/DcSdk/tool/JLAudioUnitKit.xcframework,
Sdk/DcSdk/VC/DcChinese2EnglishVC.swift,
Sdk/DcSdk/VC/DcDialogTranslateVC.swift,
Sdk/DcSdk/VC/DcStereoCallVC.swift,
+ Sdk/DcSdk/VC/DcSubTitleVC.swift,
Sdk/DcSdk/VC/DcTctyVC.swift,
Sdk/DcSdk/VC/DcXclyVC.swift,
Sdk/DcSdk/VC/HomeDeviceVC.swift,
Sdk/JoySdk/VC/JoyChinese2EnglishVC.swift,
Sdk/JoySdk/VC/JoyDialogTranlateVC.swift,
Sdk/JoySdk/VC/JoyStereoCallVC.swift,
+ Sdk/JoySdk/VC/JoySubTitleVC.swift,
Sdk/JoySdk/VC/JoyTctyVC.swift,
Sdk/JoySdk/VC/JoyXclyVC.swift,
Sdk/JoySdk/VC/ModeVC.swift,
- UI/Home/SubTitle/PiPManager.swift,
- UI/Home/SubTitle/SubTitleSetCell.swift,
- UI/Home/SubTitle/SubTitleSetVC.swift,
- UI/Home/SubTitle/SubTitleVC.swift,
);
target = 3FD1DFE32F0E00BF00A62733 /* playboy */;
};
CODE_SIGN_ENTITLEMENTS = ShenYin/PLAYBOY.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
- CURRENT_PROJECT_VERSION = 202603091640;
+ CURRENT_PROJECT_VERSION = 202603121640;
DEVELOPMENT_TEAM = JKLJ58J33Q;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
LIBRARY_SEARCH_PATHS = "$(inherited)";
- MARKETING_VERSION = 0.01;
+ MARKETING_VERSION = 0.02;
PRODUCT_BUNDLE_IDENTIFIER = com.ios.playboy;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
CODE_SIGN_ENTITLEMENTS = ShenYin/PLAYBOY.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
- CURRENT_PROJECT_VERSION = 202603091640;
+ CURRENT_PROJECT_VERSION = 202603121640;
DEVELOPMENT_TEAM = JKLJ58J33Q;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
LIBRARY_SEARCH_PATHS = "$(inherited)";
- MARKETING_VERSION = 0.01;
+ MARKETING_VERSION = 0.02;
PRODUCT_BUNDLE_IDENTIFIER = com.ios.playboy;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
}
}
- static func uploadVoiceFile1<T: HandyJSON>(_ target: CommomApi,completionHandler:@escaping ((T, String) -> Void)) {
- Logger.debug("request => ",target.url)
- guard let fileUrl = target.param["fileUrl"] as? URL else {
- Logger.debug("后",target.param)
- completionHandler(T(),"")
- return
- }
- if FileManager.default.fileExists(atPath: fileUrl.path){
- Logger.debug("文件存在")
- }
- else{
-
- Logger.debug("文件不存在")
- completionHandler(T(),"")
- return
- }
- Logger.debug(fileUrl)
- let request = HTTP.POST(target.url, parameters: ["form-data": Upload(fileUrl: fileUrl)]) { response in
- handleResponse(response, completionHandler: completionHandler)
- }
- request?.progress = { progress in
- Logger.debug("上传进度: \(progress)%")
- }
- }
+// static func uploadVoiceFile1<T: HandyJSON>(_ target: CommomApi,completionHandler:@escaping ((T, String) -> Void)) {
+// Logger.debug("request => ",target.url)
+// guard let fileUrl = target.param["fileUrl"] as? URL else {
+// Logger.debug("后",target.param)
+// completionHandler(T(),"")
+// return
+// }
+// if FileManager.default.fileExists(atPath: fileUrl.path){
+// Logger.debug("文件存在")
+// }
+// else{
+//
+// Logger.debug("文件不存在")
+// completionHandler(T(),"")
+// return
+// }
+// Logger.debug(fileUrl)
+// let request = HTTP.POST(target.url, parameters: ["form-data": Upload(fileUrl: fileUrl)]) { response in
+// handleResponse(response, completionHandler: completionHandler)
+// }
+// request?.progress = { progress in
+// Logger.debug("上传进度: \(progress)%")
+// }
+// }
static func uploadVoiceFile<T: HandyJSON>(_ target: CommomApi,completionHandler:@escaping ((T, String) -> Void)) {
Logger.debug("request => ",target.url)
guard let fileUrl = target.param["fileUrl"] as? URL else {
}
}
}
-enum SubTitleSFontType:Int,HandyJSONEnum{
+enum SubTitleFontType:Int,HandyJSONEnum{
case min = 12
case stand = 14
case big = 18
return .regular(CGFloat(self.rawValue))
}
}
+enum SubTitleSoureType:Int,HandyJSONEnum{
+ case mic = 0
+ case phone = 1
+ var title:String{
+ switch self {
+ case .mic:
+ return "麦克风".l10n
+ case .phone:
+ return "紧手机内部".l10n
+ }
+ }
+ var font:UIFont{
+ return .regular(CGFloat(self.rawValue))
+ }
+}
class SubTitleCacheModel:NSObject,HandyJSON,NSCoding {
var lan_form:CCAiLanguage = .english
var lan_to:CCAiLanguage = .chinse
- var fontType:SubTitleSFontType = .stand
+ var soure:CCAiLanguage = .chinse
+ var fontType:SubTitleFontType = .stand
var showType:SubTitleShowType = .all
+ var sourceType:SubTitleSoureType = .mic
required public override init() { }
func encode(with aCoder: NSCoder) {
aCoder.encode(lan_to.rawValue, forKey: "lan_to")
aCoder.encode(fontType.rawValue, forKey: "fontType")
aCoder.encode(showType.rawValue, forKey: "showType")
+ aCoder.encode(sourceType.rawValue, forKey: "sourceType")
}
required init?(coder aDecoder: NSCoder) {
let lanToString = aDecoder.decodeObject(forKey: "lan_to") as? String ?? "chinse"
let fontTypeValue = aDecoder.decodeInteger(forKey: "fontType")
let showTypeValue = aDecoder.decodeInteger(forKey: "showType")
+ let sourceTypeValue = aDecoder.decodeInteger(forKey: "sourceType")
lan_form = CCAiLanguage(rawValue: lanFormString) ?? .english
lan_to = CCAiLanguage(rawValue: lanToString) ?? .chinse
- fontType = SubTitleSFontType(rawValue: fontTypeValue) ?? .stand
+ fontType = SubTitleFontType(rawValue: fontTypeValue) ?? .stand
showType = SubTitleShowType(rawValue: showTypeValue) ?? .all
+ sourceType = SubTitleSoureType(rawValue: sourceTypeValue) ?? .mic
}
}
override func setupViews() {
navBar.title = device.productName
navBar.backgroundColor = .color(.bgColor)
+// navBar.rightView.isHidden = false
+// navBar.rightView.setTitle("字幕翻译", for: .normal)
view.backgroundColor = .color(.bgColor)
// navBar.rightView.setImage(UIImage(named: "add_icon"), for: .normal)
view.addSubview(bgView)
make.width.equalTo(kScreenWid)
}
}
+ override func navRightViewTapped() {
+ CCPage.gotoSubTitle()
+ }
func updateUI(){
if CCBleManager.shared.isConected(mac: device.mac){
headerView.enable = true
CCPage.gotoFieldRecord()
case .callRecord:
CCPage.gotoCallRecord()
+// CCPage.gotoSubTitle()
case .chinese2English:
CCPage.gotoChinese2Enlgish()
case .recordTranslate:
--- /dev/null
+//
+// Untitled.swift
+// ShenYin
+//
+// Created by liangshi on 2026/3/5.
+//
+
+
+import UIKit
+import AVKit
+
+@available(iOS 15.0, *)
+class DcSubTitleVC: BaseVC {
+ let languageItem = LanguageSelectItem()
+ let toolBar = RecordBar()
+ let tableView = UITableView(frame: CGRectZero, style: .plain)
+
+
+ let pipManager = PipManager()
+ let sdk = DcSubTitle()
+
+ override func viewDidLoad() {
+ super.viewDidLoad()
+ }
+
+ override func viewWillAppear(_ animated: Bool) {
+ super.viewWillAppear(animated)
+ let cache = SubTitleCache.shared.get()
+ languageItem.leftLabel.language = cache.lan_form
+ languageItem.rightLabel.language = cache.lan_to
+ }
+ override func viewDidAppear(_ animated: Bool) {
+ super.viewDidAppear(animated)
+
+ }
+ override func viewDidDisappear(_ animated: Bool) {
+ super.viewDidDisappear(animated)
+ sdk.stopEarRecord()
+ }
+ override func setupViews() {
+ view.backgroundColor = .color(.bgColor)
+ navBar.do { v in
+ v.addSubview(languageItem)
+ v.rightView.isHidden = false
+// v.rightView.setImage(UIImage(named: "set_right"), for: .normal)
+ navBar.rightView.setImage(UIImage(named: "help_icon"), for: .normal)
+ }
+
+ view.addSubviews([toolBar,tableView])
+
+ toolBar.do { v in
+ v.delegate = self
+ v.leftItem.isHidden = true
+ v.rightItem.isHidden = true
+ }
+ languageItem.do { v in
+ v.languageChange = { (from,to) in
+ let cache = SubTitleCache.shared.get()
+ cache.lan_form = from
+ cache.lan_to = to
+ SubTitleCache.shared.save(cache)
+ }
+ }
+ tableView.do { v in
+ v.backgroundColor = .color(.bgColor)
+ v.delegate = self
+ v.dataSource = self
+ v.estimatedRowHeight = 60
+ v.separatorStyle = .none
+ v.register(SubTitleCell.self, forCellReuseIdentifier: SubTitleCell.reuseID)
+ }
+// let soureView = UIView()
+// soureView.backgroundColor = .red
+// view.addSubviews([soureView])
+// soureView.snp.makeConstraints { make in
+// make.size.equalTo(10)
+// make.center.equalToSuperview()
+// }
+ pipManager.setupPictureInPicture(soureceView: self.view)
+ }
+ override func setupLayout() {
+ languageItem.snp.makeConstraints { make in
+ make.left.right.equalToSuperview().inset(80)
+ make.centerX.equalToSuperview()
+ make.bottom.equalToSuperview().offset(5)
+ }
+ 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)
+ }
+ }
+
+ override func setupData() {
+ setupSdk()
+ NotificationCenter.default.addObserver(
+ self,
+ selector: #selector(appDidEnterBackground(_:)),
+ name: UIApplication.willResignActiveNotification,
+ object: nil
+ )
+
+ NotificationCenter.default.addObserver(
+ self,
+ selector: #selector(appWillEnterForeground(_:)),
+ name: UIApplication.didBecomeActiveNotification,
+ object: nil
+ )
+
+ }
+ override func navRightViewTapped() {
+ if !languageItem.enable {
+ return
+ }
+ let vc = SubTitleSetVC()
+ App.shared.goToPage(vc)
+ }
+
+ @objc private func appDidEnterBackground(_ notification: Notification) {
+ if sdk.recording {
+ pipManager.startPictureInPicture()
+ }
+ }
+
+ @objc private func appWillEnterForeground(_ notification: Notification) {
+ pipManager.stopPictureInPicture()
+ }
+ func setupSdk(){
+ sdk.recordNeedReloadData = {[weak self] in
+ guard let self = self else{return}
+ if let model = sdk.dataList.last{
+ pipManager.contentVC.updateRaw(model.send)
+ pipManager.contentVC.updateTrans(model.response)
+ tableView.reloadData()
+ if sdk.dataList.count > 0 {
+ tableView.scrollToRow(at: IndexPath(row: sdk.dataList.count - 1, section: 0), at: .bottom, animated: true)
+ }
+ }
+ }
+ }
+}
+
+
+@available(iOS 15.0, *)
+extension DcSubTitleVC:RecordBarDelegate{
+ func recordBarLanguageChange(_ bar: RecordBar) {
+
+ sdk.stopEarRecord()
+ sdk.startEarRecord(from: languageItem.leftLabel.language, to: languageItem.rightLabel.language)
+ }
+ func recordBar(_ bar: RecordBar, stateChange state: RecordBarSate) {
+ if state == .start {
+ sdk.startEarRecord(from: languageItem.leftLabel.language, to: languageItem.rightLabel.language)
+ languageItem.enable = false
+ navBar.rightView.alpha = 0.5
+ }
+ else if state == .stop{
+ sdk.stopEarRecord()
+ languageItem.enable = true
+ navBar.rightView.alpha = 1
+ }
+ }
+}
+@available(iOS 15.0, *)
+extension DcSubTitleVC:UITableViewDelegate,UITableViewDataSource {
+ func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
+ return sdk.dataList.count
+ }
+
+ func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
+ let cell = SubTitleCell.cell(withTableView: tableView, indexPath: indexPath)
+ cell.data = sdk.dataList[indexPath.row]
+ return cell
+ }
+ func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
+
+ }
+}
//
-//class CCConfig{
-// static var appLanguage = AppSupportLanguage.english
-//
-// static let wxAppID = "wxeb3291e720293abc"
-// static let wxUniversalLink = "https://www.playboykq.com/app/"
-// static let appStoreLink = "https://itunes.apple.com/cn/app/6757697090?mt=8"
-// /// 版本检测
-// static let appStoreVersionURL = "https://itunes.apple.com/cn/lookup?id=6757697090"
-//
-// /// 服务器地址
-// static let baseURL = "https://app-api.deepchords.com"
-//
-// /// 用户隐私协议地址
-// static let yonghuBaseURL = "https://pub-cdn.playboykq.com"
-//
-//}
-
class CCConfig{
static var appLanguage = AppSupportLanguage.english
-
+
static let wxAppID = "wxeb3291e720293abc"
static let wxUniversalLink = "https://www.playboykq.com/app/"
static let appStoreLink = "https://itunes.apple.com/cn/app/6757697090?mt=8"
/// 版本检测
static let appStoreVersionURL = "https://itunes.apple.com/cn/lookup?id=6757697090"
-
+
/// 服务器地址
- static let baseURL = "https://app-api.playboykq.com"
-
+ static let baseURL = "https://app-api.deepchords.com"
+
/// 用户隐私协议地址
static let yonghuBaseURL = "https://pub-cdn.playboykq.com"
-
+
}
+
+//class CCConfig{
+// static var appLanguage = AppSupportLanguage.english
+//
+// static let wxAppID = "wxeb3291e720293abc"
+// static let wxUniversalLink = "https://www.playboykq.com/app/"
+// static let appStoreLink = "https://itunes.apple.com/cn/app/6757697090?mt=8"
+// /// 版本检测
+// static let appStoreVersionURL = "https://itunes.apple.com/cn/lookup?id=6757697090"
+//
+// /// 服务器地址
+// static let baseURL = "https://app-api.playboykq.com"
+//
+// /// 用户隐私协议地址
+// static let yonghuBaseURL = "https://pub-cdn.playboykq.com"
+//
+//}
App.shared.goToPage(DcDialogTranslateVC())
}
}
+ static func gotoSubTitle(){
+ if CCDevice.isJoy {
+ // App.shared.goToPage(JoyDialogTranslateVC())
+ }
+ else{
+ if #available(iOS 15.0, *) {
+ App.shared.goToPage(DcSubTitleVC())
+ } else {
+ // Fallback on earlier versions
+ }
+ }
+ }
}
--- /dev/null
+//
+// DcSubTitle.swift
+// ShenYin
+//
+// Created by liangshi on 2026/3/13.
+//
+
+
+import UIKit
+import DeepChordsSdk
+
+class DcSubTitle:NSObject{
+ var dataList:[SubTitleCellModel] = []
+ var recordNeedReloadData:(()->Void)?
+ var recording = false
+ override init() {
+ super.init()
+ setupSdk()
+ }
+ func setupSdk(){
+ DCBleRecord.shared.asrSplitCallback = { [weak self] model in
+ guard let self = self else{return}
+// Logger.debug("asr: \(model.text),index: \(model.index)")
+ let index = dataList.firstIndex (where: { item in
+ return item.index == model.index;
+ })
+ if (index == nil) {
+ let item0 = SubTitleCellModel(send: model.text,index: model.index)
+ dataList.append(item0)
+ } else {
+ dataList[index!].send = model.text
+ }
+ recordNeedReloadData?()
+ }
+ DCBleRecord.shared.translateSplitCallback = { [weak self] model in
+ guard let self = self else{return}
+// Logger.debug("tranlate: \(model.text),index: \(model.index)")
+ let index = dataList.firstIndex (where: { item in
+ return item.index == model.index;
+ })
+ if (index == nil) {
+ let item0 = SubTitleCellModel(response: model.text,index: model.index)
+ dataList.append(item0)
+ } else {
+ dataList[index!].response = model.text
+ }
+ recordNeedReloadData?()
+ }
+// DCBleRecord.shared.translateDataCallback = { data in
+// PcmSound.sharedInstance().feed(data)
+// }
+// DCBleRecord.shared.codeCallback = { [weak self] code in
+// guard let self = self else{return}
+// if code == 20002 || code == 20003{
+// if let model = dataList.last,model.send.isEmpty{
+// dataList.removeLast()
+// refreshData()
+// }
+// }
+// }
+ DCBleRecord.shared.filePathCallBack = { [weak self] path in
+ guard let self = self else{return}
+ // self.recordBtn.isSelected = false
+ recording = false
+ }
+ }
+
+ func startEarRecord(from:CCAiLanguage,to:CCAiLanguage) {
+ recording = true
+ dataList.removeAll()
+ recordNeedReloadData?()
+
+ DCBleRecord.shared.fromLanguage = from.value
+ DCBleRecord.shared.toLanguage = to.value
+ DCBleRecord.shared.isTranslate = true
+ DCBleRecord.shared.isTTs = false
+
+ if SubTitleCache.shared.get().sourceType == .mic{
+ DCBleRecord.shared.start()
+ }
+ else{
+ DCBleRecord.shared.start(type: 0x30)
+ }
+
+ do {
+ try AVAudioSession.sharedInstance().setCategory(.playAndRecord, options: [.defaultToSpeaker])
+ try AVAudioSession.sharedInstance().overrideOutputAudioPort(.speaker)
+ } catch {
+ print(error)
+ }
+ }
+
+ func stopEarRecord() {
+ DCBleRecord.shared.stop()
+ recording = false
+ }
+}
}
}
@objc func subTitleAction(){
- if #available(iOS 15.0, *) {
-// if CCBleManager.shared.isConected(mac: device.mac){
- App.shared.goToPage(SubTitleVC())
-// }
- } else {
- // Fallback on earlier versions
- }
+ CCPage.gotoSubTitle()
}
}
+////
+//// Untitled.swift
+//// ShenYin
+////
+//// Created by liangshi on 2026/3/5.
+////
//
-// Untitled.swift
-// ShenYin
//
-// Created by liangshi on 2026/3/5.
-//
-
-
import UIKit
import AVKit
import JoyGemAI
-import SVProgressHUD
@available(iOS 15.0, *)
-class SubTitleVC: BaseVC {
+class JoySubTitleVC: BaseVC {
let languageItem = LanguageSelectItem()
let toolBar = RecordBar()
- var startButton : UIButton!
-
- var stopButton : UIButton!
- let pipManager = PiPManager()
-
-
- //画中画载体
- internal var sourceView : UIView = UIView()
+ let pipManager = PipManager()
+ var messageList : [(String,UInt)] = []
+ var translateList : [(String,UInt)] = []
override func viewDidLoad() {
super.viewDidLoad()
}
+ override func viewDidAppear(_ animated: Bool) {
+ super.viewDidAppear(animated)
+
+ }
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
let cache = SubTitleCache.shared.get()
}
override func setupViews() {
view.backgroundColor = UIColor.white
- sourceView.backgroundColor = .clear
navBar.do { v in
v.backgroundColor = .white
v.addSubview(languageItem)
v.rightView.setImage(UIImage(named: "set_right"), for: .normal)
}
- view.addSubviews([sourceView,toolBar])
+ view.addSubviews([toolBar])
toolBar.do { v in
v.delegate = self
v.rightItem.isHidden = true
}
languageItem.do { v in
- v.languageChange = {[weak self] (from,to) in
- guard let self = self else { return }
+ v.languageChange = { (from,to) in
let cache = SubTitleCache.shared.get()
cache.lan_form = from
cache.lan_to = to
SubTitleCache.shared.save(cache)
}
}
+ pipManager.setupPictureInPicture(soureceView: self.navigationController!.view)
}
override func setupLayout() {
languageItem.snp.makeConstraints { make in
make.height.equalTo(120)
make.bottom.equalToSuperview()
}
- sourceView.snp.makeConstraints { make in
- make.top.equalTo(kNavBtm + 20)
- make.height.equalTo(200)
- make.centerX.equalToSuperview()
- make.width.equalTo(200)
- }
}
override func setupData() {
} catch {
print(error)
}
-
JoyGemSubtitleManager.shared().delegate = self
- // 初始化画中画管理器
- pipManager.setupPictureInPicture(sourceView: self.sourceView)
- // 监听画中画状态变化
NotificationCenter.default.addObserver(
self,
- selector: #selector(pipStateChanged(_:)),
- name: NSNotification.Name("PiPStateChanged"),
+ selector: #selector(appDidEnterBackground(_:)),
+ name: UIApplication.willResignActiveNotification,
object: nil
)
+
+ NotificationCenter.default.addObserver(
+ self,
+ selector: #selector(appWillEnterForeground(_:)),
+ name: UIApplication.didBecomeActiveNotification,
+ object: nil
+ )
+
}
override func navRightViewTapped() {
let vc = SubTitleSetVC()
App.shared.goToPage(vc)
}
-
- @objc private func pipStateChanged(_ notification: Notification) {
- if let userInfo = notification.userInfo,
- let isActive = userInfo["isActive"] as? Bool {
- print("画中画状态: \(isActive ? "活跃" : "非活跃")")
- }
+
+
+ @objc private func appDidEnterBackground(_ notification: Notification) {
+ pipManager.startPictureInPicture()
+ }
+
+ @objc private func appWillEnterForeground(_ notification: Notification) {
+ pipManager.stopPictureInPicture()
}
}
+
+
@available(iOS 15.0, *)
-extension SubTitleVC:RecordBarDelegate{
+extension JoySubTitleVC:RecordBarDelegate{
func recordBarLanguageChange(_ bar: RecordBar) {
-
+
}
func recordBar(_ bar: RecordBar, stateChange state: RecordBarSate) {
if state == .start {
JoyGemSubtitleManager.shared().originalLanguage = languageItem.leftLabel.language.value
JoyGemSubtitleManager.shared().targetLanguage = languageItem.rightLabel.language.value
JoyGemSubtitleManager.shared().start()
-
- guard AVPictureInPictureController.isPictureInPictureSupported() else {
- print("设备不支持画中画")
- return
- }
-
- pipManager.startPictureInPicture()
}
else if state == .stop{
JoyGemSubtitleManager.shared().stop()
-
- pipManager.stopPictureInPicture()
}
}
}
@available(iOS 15.0, *)
-extension SubTitleVC : JoyGemSubtitleManagerDelegate {
+extension JoySubTitleVC : JoyGemSubtitleManagerDelegate {
func subtitleManager(_ manager: JoyGemSubtitleManager, didUpdateRaw model: JoyGemAsrModel) {
debugPrint("text: \(model.text),index: \(model.index)")
- pipManager.updateRaw(model.text,model.index)
+ updateRaw(model.text,model.index)
}
func subtitleManager(_ manager: JoyGemSubtitleManager, didUpdateTranslate model: JoyGemAsrModel) {
debugPrint("translate text: \(model.text),index: \(model.index)")
- pipManager.updateTrans(model.text,model.index)
+ updateTrans(model.text,model.index)
}
func subtitleManagerStopped(_ manager: JoyGemSubtitleManager) {
}
+ func updateRaw(_ text: String,_ index: UInt) {
+ let msg = messageList.firstIndex (where: { (str,idx) in
+ return idx == index;
+ })
+ if (msg == nil) {
+ messageList.append((text,index))
+ } else {
+ messageList[msg!] = (text,index)
+ }
+
+ let res = messageList.compactMap({ (str,idx) in
+ return str
+ }).joined(separator: "\n")
+ pipManager.contentVC.updateRaw(res)
+ }
+
+ func updateTrans(_ text: String,_ index: UInt) {
+ let msg = translateList.firstIndex (where: { (str,idx) in
+ return idx == index;
+ })
+ if (msg == nil) {
+ translateList.append((text,index))
+ } else {
+ translateList[msg!] = (text,index)
+ }
+
+ let res = translateList.compactMap({ (str,idx) in
+ return str
+ }).joined(separator: "\n")
+ pipManager.contentVC.updateTrans(res)
+ }
}
// App.shared.goToPage(DcDialogTranslateVC())
}
}
+ static func gotoSubTitle(){
+ if CCDevice.isJoy {
+ if #available(iOS 15.0, *) {
+ App.shared.goToPage(JoySubTitleVC())
+ } else {
+ // Fallback on earlier versions
+ }
+ }
+ else{
+
+ }
+ }
}
+++ /dev/null
-//
-// Untitled.swift
-// ShenYin
-//
-// Created by liangshi on 2026/3/5.
-//
-
-import AVKit
-import UIKit
-
-@available(iOS 15.0, *)
-class PiPManager: NSObject, ObservableObject {
-
- var pipController: AVPictureInPictureController?
-
- var contentVC: DesktopLyricsContentVC!
-
- var messageList : [(String,UInt)] = []
-
- var translateList : [(String,UInt)] = []
-
- func setupPictureInPicture(sourceView : UIView) {
-
- contentVC = DesktopLyricsContentVC()
- contentVC.preferredContentSize = CGSize(width: UIScreen.main.bounds.width, height: 240) // 画中画窗口大小
-
- // 设置播放器
- pipController = AVPictureInPictureController(contentSource: AVPictureInPictureController.ContentSource(
- activeVideoCallSourceView: sourceView,
- contentViewController: contentVC
- ))
- pipController?.delegate = self
- }
-
-
- func startPictureInPicture() {
- // 1. 检查场景状态
- if let scene = UIApplication.shared.connectedScenes.first as? UIWindowScene {
- print("场景激活状态: \(scene.activationState.rawValue)")
- print("场景状态描述: \(describeActivationState(scene.activationState))")
- } else {
- print("❌ 无法获取场景")
- }
-
- pipController?.startPictureInPicture()
- }
-
- private func describeActivationState(_ state: UIScene.ActivationState) -> String {
- switch state {
- case .unattached: return "unattached - 场景未连接到会话"
- case .foregroundInactive: return "foregroundInactive - 前景但不活跃"
- case .foregroundActive: return "foregroundActive - 前景且活跃 ✅"
- case .background: return "background - 后台"
- @unknown default: return "unknown"
- }
- }
-
- func stopPictureInPicture() {
- pipController?.stopPictureInPicture()
- }
-
- func updateRaw(_ text: String,_ index: UInt) {
- let msg = messageList.firstIndex (where: { (str,idx) in
- return idx == index;
- })
- if (msg == nil) {
- messageList.append((text,index))
- } else {
- messageList[msg!] = (text,index)
- }
-
- let res = messageList.compactMap({ (str,idx) in
- return str
- }).joined(separator: "\n")
- contentVC?.updateRaw(res)
- }
-
- func updateTrans(_ text: String,_ index: UInt) {
- let msg = translateList.firstIndex (where: { (str,idx) in
- return idx == index;
- })
- if (msg == nil) {
- translateList.append((text,index))
- } else {
- translateList[msg!] = (text,index)
- }
-
- let res = translateList.compactMap({ (str,idx) in
- return str
- }).joined(separator: "\n")
- contentVC?.updateTrans(res)
- }
-}
-
-@available(iOS 15.0, *)
-extension PiPManager: AVPictureInPictureControllerDelegate {
- func pictureInPictureControllerWillStartPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) {
- print("画中画即将开始")
- }
-
- func pictureInPictureControllerDidStartPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) {
- print("画中画已开始")
- // 开始播放透明视频
- //player?.play()
- }
-
- func pictureInPictureController(_ pictureInPictureController: AVPictureInPictureController, failedToStartPictureInPictureWithError error: Error) {
- print("画中画启动失败: \(error)")
- }
-
-
- func pictureInPictureControllerDidStopPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) {
- print("画中画已结束")
-// messageList.removeAll()
-// translateList.removeAll()
- }
-}
-// 画中画内容视图控制器
-@available(iOS 15.0, *)
-class DesktopLyricsContentVC : AVPictureInPictureVideoCallViewController {
- var textView = UITextView()
- var translateTextView = UITextView()
-
- override func viewDidLoad() {
- super.viewDidLoad()
- setupViews()
- setupLayout()
- }
- func setupViews(){
- view.addSubviews([textView,translateTextView])
- view.backgroundColor = .clear
-
- textView.do { v in
- v.textColor = UIColor.blue
- v.text = "原文".l10n
- v.font = UIFont.boldSystemFont(ofSize: 14)
- v.backgroundColor = .clear
- }
-
- translateTextView.do { v in
- v.textColor = UIColor.orange
- v.text = "译文".l10n
- v.font = UIFont.boldSystemFont(ofSize: 14)
- v.backgroundColor = .clear
- }
- }
- func setupLayout(){
- let cache = SubTitleCache.shared.get()
-
- if cache.showType == .all {
- textView.snp.makeConstraints { make in
- make.top.equalToSuperview().inset(40)
- make.leading.trailing.equalToSuperview()
- make.bottom.equalTo(view.snp.centerY)
- }
- translateTextView.snp.makeConstraints { make in
- make.leading.trailing.equalToSuperview()
- make.top.equalTo(textView.snp.bottom)
- make.height.equalTo(textView.snp.height)
- }
- textView.isHidden = false
- translateTextView.isHidden = false
- }
- else if cache.showType == .from {
- textView.snp.makeConstraints { make in
- make.top.bottom.equalToSuperview().inset(40)
- make.leading.trailing.equalToSuperview()
- }
- textView.isHidden = false
- translateTextView.isHidden = true
- } else {
- translateTextView.snp.makeConstraints { make in
- make.top.bottom.equalToSuperview().inset(40)
- make.leading.trailing.equalToSuperview()
- }
- textView.isHidden = true
- translateTextView.isHidden = false
- }
-
- }
-
- func updateRaw(_ text: String) {
- textView.text = text
- textView.scrollRangeToVisible(NSRange(location: textView.text.count, length: 0))
- }
-
- func updateTrans(_ text : String) {
- translateTextView.text = text
- translateTextView.scrollRangeToVisible(NSRange(location: translateTextView.text.count, length: 0))
- }
-}
--- /dev/null
+//
+// Untitled.swift
+// ShenYin
+//
+// Created by liangshi on 2026/3/5.
+//
+
+import AVKit
+import UIKit
+
+@available(iOS 15.0, *)
+class PipManager:NSObject,AVPictureInPictureControllerDelegate{
+ var pipController: AVPictureInPictureController?
+ var contentVC: PipContentVC!
+
+
+ func setupPictureInPicture(soureceView:UIView) {
+ guard AVPictureInPictureController.isPictureInPictureSupported() else {
+ print("❌ 设备不支持画中画")
+ return
+ }
+
+ contentVC = PipContentVC()
+ contentVC.preferredContentSize = CGSizeMake(kScreenWid, 200)
+ let contentSource = AVPictureInPictureController.ContentSource(
+ activeVideoCallSourceView:soureceView ,
+ contentViewController: contentVC
+ )
+
+ pipController = AVPictureInPictureController(contentSource: contentSource)
+ pipController?.delegate = self
+ pipController?.canStartPictureInPictureAutomaticallyFromInline = false
+ }
+
+ @objc func startPictureInPicture(){
+ guard let pipController = pipController else {
+ print("❌ pipController 未初始化,无法启动画中画")
+ return
+ }
+
+ if pipController.isPictureInPicturePossible {
+ print("✅ 画中画可以启动,准备开始...")
+ pipController.startPictureInPicture()
+ contentVC?.reset()
+ } else {
+ print("❌ 画中画当前无法启动")
+ }
+ }
+ @objc func stopPictureInPicture() {
+ guard let pipController = pipController else {
+ print("❌ pipController 未初始化,无法停止画中画")
+ return
+ }
+
+ if pipController.isPictureInPictureActive {
+ print("✅ 画中画正在显示,准备停止...")
+ pipController.stopPictureInPicture()
+ print("✅ 画中画已停止")
+ } else {
+ print("⚠️ 画中画当前未显示")
+ }
+ }
+
+ func pictureInPictureControllerWillStartPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) {
+ print("画中画即将开始")
+ }
+
+ func pictureInPictureControllerDidStartPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) {
+ print("✅ 画中画已开始")
+ }
+
+ func pictureInPictureController(_ pictureInPictureController: AVPictureInPictureController, failedToStartPictureInPictureWithError error: Error) {
+ print("❌ 画中画启动失败: \(error)")
+ }
+
+ func pictureInPictureControllerWillStopPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) {
+ print("⏹️ 画中画即将结束")
+ }
+
+ func pictureInPictureControllerDidStopPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) {
+ print("🛑 画中画已结束")
+ }
+
+}
+
+
+
+
+
+
+
+// 画中画内容视图控制器
+@available(iOS 15.0, *)
+class PipContentVC : AVPictureInPictureVideoCallViewController {
+ var textView = UITextView()
+ var translateTextView = UITextView()
+
+ override func viewDidLoad() {
+ super.viewDidLoad()
+ view.addSubviews([textView,translateTextView])
+ view.backgroundColor = .rgb(0x717171)
+ reset()
+ }
+
+ func updateRaw(_ text: String) {
+ textView.text = text
+ textView.scrollRangeToVisible(NSRange(location: textView.text.count, length: 0))
+ }
+
+ func updateTrans(_ text : String) {
+ translateTextView.text = text
+ translateTextView.scrollRangeToVisible(NSRange(location: translateTextView.text.count, length: 0))
+ }
+ func reset(){
+ if (textView.superview == nil) {
+ return
+ }
+ let cache = SubTitleCache.shared.get()
+
+ textView.do { v in
+ v.textColor = .rgb(0xD8D8D8)
+ v.font = cache.fontType.font
+ v.backgroundColor = .clear
+ }
+
+ translateTextView.do { v in
+ v.textColor = .white
+ v.font = cache.fontType.font
+ v.backgroundColor = .clear
+ }
+
+ if cache.showType == .all {
+ textView.snp.remakeConstraints { make in
+ make.top.equalToSuperview().inset(50)
+ make.leading.trailing.equalToSuperview().inset(16)
+ make.bottom.equalTo(view.snp.centerY)
+ }
+ translateTextView.snp.remakeConstraints { make in
+ make.leading.trailing.equalToSuperview().inset(16)
+ make.top.equalTo(textView.snp.bottom).offset(2)
+ make.bottom.equalToSuperview().inset(16)
+ }
+ textView.isHidden = false
+ translateTextView.isHidden = false
+ }
+ else if cache.showType == .from {
+ textView.snp.remakeConstraints { make in
+ make.top.bottom.equalToSuperview().inset(50)
+ make.leading.trailing.equalToSuperview().inset(16)
+ }
+ textView.isHidden = false
+ translateTextView.isHidden = true
+ } else {
+ translateTextView.snp.remakeConstraints { make in
+ make.top.bottom.equalToSuperview().inset(50)
+ make.leading.trailing.equalToSuperview().inset(16)
+ }
+ textView.isHidden = true
+ translateTextView.isHidden = false
+ }
+ }
+}
--- /dev/null
+//
+// SubTitleCell.swift
+// ShenYin
+//
+// Created by liangshi on 2026/3/13.
+//
+
+class SubTitleCellModel{
+ var send = ""
+ var response = ""
+ var index : Int = 0
+
+ init(send: String = "", response: String = "",index:Int = 0) {
+ self.send = send
+ self.response = response
+ self.index = index
+ }
+}
+
+
+
+
+class SubTitleCell:BaseTableViewCell{
+ let bgView = UIView()
+ private let line = BrokenLineView()
+ let originalLabel = UILabel()
+ let translationLabel = UILabel()
+ let playIcon = 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 = .clear
+ backgroundView?.backgroundColor = .clear
+ backgroundColor = .clear
+
+ bgView.do { v in
+ v.yc.setCornerRadius(12)
+ v.backgroundColor = .color(.cellBg)
+ }
+ originalLabel.do { v in
+ v.font = .regular(14)
+ v.textColor = .color(.secondary)
+ v.numberOfLines = 0
+ v.textAlignment = .left
+ }
+ translationLabel.do { v in
+ v.font = .regular(14)
+ v.textColor = .color(.first)
+ v.numberOfLines = 0
+ v.textAlignment = .left
+ }
+ line.do { v in
+ }
+ playIcon.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(playAction))
+ v.isHidden = true
+ }
+ contentView.addSubview(bgView)
+ bgView.addSubviews([originalLabel,translationLabel,line,playIcon])
+ }
+
+ 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.height.greaterThanOrEqualTo(15)
+ make.left.equalToSuperview().inset(12)
+ make.right.equalToSuperview().inset(50)
+ make.width.lessThanOrEqualTo(kScreenWid - 120)
+ }
+ 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.right.equalToSuperview().inset(12)
+ make.height.greaterThanOrEqualTo(15)
+ make.bottom.equalToSuperview().inset(14)
+ }
+ playIcon.snp.makeConstraints { make in
+ make.right.equalToSuperview().inset(12)
+ make.size.equalTo(24)
+ make.top.equalToSuperview().inset(12)
+ }
+ }
+
+ var data:SubTitleCellModel!{
+ didSet{
+ originalLabel.text = data.send
+ translationLabel.text = data.response
+
+// if data.isPhone {
+// bgView.snp.remakeConstraints { make in
+// make.top.bottom.equalToSuperview().inset(10)
+// // make.width.equalTo(kScreenWid - 78)
+// make.right.equalToSuperview().inset(18)
+// }
+// bgView.backgroundColor = .color(.theme)
+// originalLabel.textColor = UIColor.white.withAlphaComponent(0.8)
+// translationLabel.textColor = .white
+// }
+// else{
+ bgView.snp.remakeConstraints { make in
+ make.top.bottom.equalToSuperview().inset(10)
+ make.left.equalToSuperview().inset(18)
+ }
+ bgView.backgroundColor = .color(.cellBg)
+ originalLabel.textColor = .color(.secondary)
+ translationLabel.textColor = .color(.first)
+// }
+ line.setNeedsLayout()
+ }
+ }
+ @objc func playAction(){
+ if playIcon.isAnimating {
+ playIcon.stopAnimating()
+ }
+ else{
+ playIcon.startAnimating()
+ }
+ }
+}
public enum SubTitleSetCellType: Int {
case language
+ case soure
case showType
case font
switch self {
case .language:
return "语言".l10n
+ case .soure:
+ return "声源".l10n
case .showType:
return "字幕展示内容".l10n
case .font:
if type == .language {
valueLabel.text = model.lan_form.title + "-" + model.lan_to.title
}
+ else if type == .soure {
+ valueLabel.text = model.sourceType.title
+ }
else if type == .showType {
valueLabel.text = model.showType.title
}
import UIKit
class SubTitleSetVC: BaseVC{
let tableView = UITableView(frame: .zero, style: .plain)
- var list:[SubTitleSetCellType] = [.language,.showType,.font]
+ var list:[SubTitleSetCellType] = [.language,.soure,.showType,.font]
let imgPicker = ImagePickerManager()
override func viewDidLoad() {
switch data {
case .language:
languageAction()
+ case .soure:
+ sourceTypeAction()
case .showType:
showTypeAction()
case .font:
tableView.reloadData()
}
}
+
+ @objc func sourceTypeAction(){
+ let cache = SubTitleCache.shared.get()
+ let list:[SubTitleSoureType] = [.mic,.phone]
+ let picker = CustomPicker(title: "声音源".l10n, list: list.map{$0.title})
+ picker.selectedIndex = list.firstIndex(where: {cache.sourceType.title == $0.title})!
+ picker.show()
+ picker.didSelected = {[weak self] (index,value) in
+ guard let self = self else { return }
+ cache.sourceType = list.first(where: {$0.title == value})!
+ SubTitleCache.shared.save(cache)
+ tableView.reloadData()
+ }
+ }
@objc func showTypeAction(){
let cache = SubTitleCache.shared.get()
let list:[SubTitleShowType] = [.all,.from,.to]
}
@objc func fontAction(){
let cache = SubTitleCache.shared.get()
- let list:[SubTitleSFontType] = [.min,.stand,.big]
+ let list:[SubTitleFontType] = [.min,.stand,.big]
let picker = CustomPicker(title: "字体大小".l10n, list: list.map{$0.title})
picker.selectedIndex = list.firstIndex(where: {cache.fontType.title == $0.title})!
picker.show()
//
import UIKit
+import DeepChordsSdk
class LogVC: BaseVC {
var textView = UITextView()
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
- textView.text = Logger.logs.joined()
+ if CCDevice.isJoy {
+
+ }
+ else{
+ textView.text = DCLogger.logs.joined()
+ }
let range = NSMakeRange(textView.text.count - 1, 1)
textView.scrollRangeToVisible(range)
}
let switcIcon = UIImageView()
var languageChange:((_ from:CCAiLanguage,_ to:CCAiLanguage)->Void)?
+ var enable:Bool = true{
+ didSet{
+ self.alpha = enable ? 1:0.5
+ }
+ }
+
override func setupViews() {
addSubviews([bottonView])
bottonView.addSubviews([leftLabel,rightLabel,switcIcon])
}
}
@objc func switchAction(){
+ if !enable {
+ return
+ }
let text1 = leftLabel.language
let text2 = rightLabel.language
leftLabel.language = text2
languageChange?(leftLabel.language,rightLabel.language)
}
@objc func leftAction(){
+ if !enable {
+ return
+ }
let v = LanguagePickerView(languages: CCAiLanguage.all,isLeft: true,left: leftLabel.language,right: rightLabel.language)
v.selectedIndex = v.list.firstIndex(where: {$0.title == leftLabel.language.title}) ?? 0
v.show()
}
}
@objc func rightAction(){
+ if !enable {
+ return
+ }
let v = LanguagePickerView(languages: CCAiLanguage.all,isLeft: false,left: leftLabel.language,right: rightLabel.language)
v.selectedIndex = v.list.firstIndex(where: {$0.title == rightLabel.language.title}) ?? 0
v.show()