format.dataSize = 80
self.opusDecoder = JLOpusDecoder(decoder: format, delegate: self)
-// opusDecoder.resetOpusFramet(format)
+ // opusDecoder.resetOpusFramet(format)
}
func inputData(_ data:Data){
}
-//extension OpusTools{
-// static func pcmTowav(_ data:Data)->String?{
-// do{
-// let path = DCFolder.cacheDirectory() + "/\(Date().timeIntervalSince1970).wav"
-// try JLPcmToWav.convertPCMData(data, toWAVFile: path, sampleRate: 16000, numChannels: 2, bitsPerSample: 16)
-// return path
-// }
-// catch{
-// return nil
-// }
-// }
-//
-// static func opusSteroTowav(_ opusPath:String,finished:@escaping ((String?)->Void)){
-// let format = JLOpusFormat.defaultFormats()
-// format.sampleRate = 16000
-// format.hasDataHeader = false
-// format.channels = 2
-// format.dataSize = 80
-// let pcmPath = DCFolder.cacheDirectory() + "/\(Int(Date().timeIntervalSince1970*10000)).pcm"
-// DCLogger.debug(pcmPath)
-//
-// let opusDecoder = JLOpusDecoder(decoder: format, delegate: OpusTools())
-// opusDecoder.opusDecodeFile(opusPath, outPut: pcmPath) { pcmPath,err in
-// if err == nil,let path = pcmPath{
-// let wav = pcmTowav(path,sampleRate: 16000,channels: 2)
-// finished(wav)
-// }
-// else{
-// finished(nil)
-// }
-// }
-// return finished(nil)
-// }
-// static func opusTowav(_ opusPath:String,finished:@escaping ((String?)->Void)){
-// let format = JLOpusFormat.defaultFormats()
-// format.sampleRate = 16000
-// format.hasDataHeader = false
-// format.channels = 1
-// format.dataSize = 40
-// let pcmPath = DCFolder.cacheDirectory() + "/\(Int(Date().timeIntervalSince1970*10000)).pcm"
-// DCLogger.debug(pcmPath)
-//
-// let opusDecoder = JLOpusDecoder(decoder: format, delegate: OpusTools())
-// opusDecoder.opusDecodeFile(opusPath, outPut: pcmPath) { pcmPath,err in
-// if err == nil,let path = pcmPath{
-// let wav = pcmTowav(path,sampleRate: 16000,channels: 1)
-// finished(wav)
-// }
-// else{
-// finished(nil)
-// }
-// }
-// return finished(nil)
-// }
-//
-// static func pcmTowav(_ pcmPath:String,sampleRate:UInt32,channels:UInt16)->String?{
-// if let data = NSData(contentsOfFile: pcmPath) as? Data {
-// do{
-// let path = DCFolder.record + "\(Int(Date().timeIntervalSince1970*10000)).wav"
-// DCLogger.debug(path)
-// try JLPcmToWav.convertPCMData(data, toWAVFile: path, sampleRate: sampleRate, numChannels: channels, bitsPerSample: 16)
-// return path
-// }
-// catch{
-// return nil
-// }
-// }
-// return nil
-// }
-//}
+extension OpusTools{
+ static func pcmTowav(_ data:Data)->String?{
+ do{
+ let path = OpusTools.cacheDirectory() + "/\(Date().timeIntervalSince1970).wav"
+ try JLPcmToWav.convertPCMData(data, toWAVFile: path, sampleRate: 16000, numChannels: 2, bitsPerSample: 16)
+ return path
+ }
+ catch{
+ return nil
+ }
+ }
+
+ static func opusSteroTowav(_ opusPath:String,finished:@escaping ((String?)->Void)){
+ let format = JLOpusFormat.defaultFormats()
+ format.sampleRate = 16000
+ format.hasDataHeader = false
+ format.channels = 2
+ format.dataSize = 80
+ let pcmPath = OpusTools.cacheDirectory() + "/\(Int(Date().timeIntervalSince1970*10000)).pcm"
+
+ let opusDecoder = JLOpusDecoder(decoder: format, delegate: OpusTools())
+ opusDecoder.opusDecodeFile(opusPath, outPut: pcmPath) { pcmPath,err in
+ if err == nil,let path = pcmPath{
+ let wav = pcmTowav(path,sampleRate: 16000,channels: 2)
+ finished(wav)
+ }
+ else{
+ finished(nil)
+ }
+ }
+ return finished(nil)
+ }
+ static func opusTowav(_ opusPath:String,finished:@escaping ((String?)->Void)){
+ let format = JLOpusFormat.defaultFormats()
+ format.sampleRate = 16000
+ format.hasDataHeader = false
+ format.channels = 1
+ format.dataSize = 40
+ let pcmPath = OpusTools.cacheDirectory() + "/\(Int(Date().timeIntervalSince1970*10000)).pcm"
+
+ let opusDecoder = JLOpusDecoder(decoder: format, delegate: OpusTools())
+ opusDecoder.opusDecodeFile(opusPath, outPut: pcmPath) { pcmPath,err in
+ if err == nil,let path = pcmPath{
+ let wav = pcmTowav(path,sampleRate: 16000,channels: 1)
+ finished(wav)
+ }
+ else{
+ finished(nil)
+ }
+ }
+ return finished(nil)
+ }
+
+ static func pcmTowav(_ pcmPath:String,sampleRate:UInt32,channels:UInt16)->String?{
+ if let data = NSData(contentsOfFile: pcmPath) as? Data {
+ do{
+ let path = OpusTools.cacheDirectory() + "/\(Int(Date().timeIntervalSince1970*10000)).wav"
+ try JLPcmToWav.convertPCMData(data, toWAVFile: path, sampleRate: sampleRate, numChannels: channels, bitsPerSample: 16)
+ return path
+ }
+ catch{
+ return nil
+ }
+ }
+ return nil
+ }
+
+ // 获取应用沙盒中的缓存目录路径
+ static func cacheDirectory() -> String! {
+ // 获取缓存目录的URL
+ if let cacheURL = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first {
+ // 转换为路径字符串
+ return cacheURL.path
+ }
+ return ""
+ }
+}
UI/Raisound/RSChatVC.swift,
UI/Raisound/RSManager.swift,
UI/Raisound/RSRecordBar.swift,
+ UI/Raisound/RSRoleVC.swift,
+ UI/Raisound/RSSpeakerVC.swift,
+ UI/Raisound/RSVoiceModel.swift,
UI/Raisound/SwiftWebSocket.swift,
UI/Sleep/AudioPlayerManager.swift,
UI/Sleep/AudioPlayerVC.swift,
guard let _ = (scene as? UIWindowScene) else { return }
App.shared.window = window
-// AppLaunch.start()
- MapManager.shared.setupSdk()
- window?.rootViewController = UINavigationController(rootViewController: SportSetVC())
+ AppLaunch.start()
+// MapManager.shared.setupSdk()
+// window?.rootViewController = UINavigationController(rootViewController: SportSetVC())
}
func sceneDidDisconnect(_ scene: UIScene) {
extension MSVoiceChat{
private func openWebSocket(){
- let url = RSApi.wsBaseURL + "dt=\(RSAuth.shared.deviceToken)"
+ let url = RSApi.wsBaseUrl + "dt=\(RSAuth.shared.deviceToken)"
Logger.debug(url)
socket = WebSocket(url)
socket?.delegate = self
import SwiftHTTP
class RSApi {
- static let baseUrl = "https://app-api.raisound.com"
- static let wsBaseURL = "wss://amp.raisound.com/vc/agent?"
+// static let baseUrl = "https://app-api.raisound.com"
+// static let wsBaseUrl = "wss://amp.raisound.com/vc/agent?"
+
+ static let baseUrl = "https://dmp-dev.raisound.com/dmp-api"
+ static let wsBaseUrl = "ws://118.145.158.207:22080/vc/agent?"
+
static func request<T: HandyJSON>(api: String,params:[String:Any],method:RequestMethod,completionHandler:@escaping ((T, String) -> Void)){
let url = baseUrl + api
}
}
}
+ static func request1<T: HandyJSON>(api: String,params:[String:Any],method:RequestMethod,completionHandler:@escaping ((T, String) -> Void)){
+ let url = "https://dmp-dev.raisound.com" + api
+ Logger.debug("request => ",url )
+ Logger.debug(YLToos.jsonToString(params))
+
+ if method == .get {
+ HTTP.GET(url,parameters: params,requestSerializer: JSONParameterSerializer()){ response in
+ handleResponse(response, completionHandler: completionHandler)
+ }
+ }
+ else if method == .post{
+ HTTP.POST(url,parameters: params,requestSerializer: JSONParameterSerializer()){ response in
+ handleResponse(response, completionHandler: completionHandler)
+ }
+ }
+ else if method == .put{
+ HTTP.PUT(url,parameters: params,headers: ["bz-dt":RSAuth.shared.cookie],requestSerializer: JSONParameterSerializer()){ response in
+ handleResponse(response, completionHandler: completionHandler)
+ }
+ }
+ else if method == .delete{
+ HTTP.DELETE(url,parameters: params,requestSerializer: JSONParameterSerializer()){ response in
+ handleResponse(response, completionHandler: completionHandler)
+ }
+ }
+ }
}
static let productKey = "gSUeLgTFnQZMcySLy2TCrEfyPbQgarxa"
var deviceToken = ""
var deviceID = ""
+ var cookie = ""
public func register(deviceId:String,completeHandler: @escaping MSRegisterCallBack){
let timeStamp = "\(Int(Date().timeIntervalSince1970)*1000)"
HTTP.POST(url,parameters: params,requestSerializer: JSONParameterSerializer()){ response in
DispatchQueue.main.sync {
+ if let headers = response.headers {
+ if let setCookieHeader = headers["Set-Cookie"] {
+ print("登录响应中的Set-Cookie头: \(setCookieHeader)")
+ }
+
+ // 打印所有响应头
+ print("完整响应头信息:")
+ for (key, value) in headers {
+ print(" \(key): \(value)")
+ if key == "bz-dt"{
+ self.cookie = value
+ }
+ }
+ }
let (dict,error) = MSHttpManager.receiveData(response)
if let error = error {
completeHandler(nil,error)
return
}
+
guard let dict = dict else {return completeHandler(nil,error)}
if let result = dict["success"] as? Bool,result == true,let dataDict = dict["data"] as? [String:Any]{
}
}
}
+
+ public func requestRoleList(completeHandler: @escaping (_ result:RSRoleModelRoot)->Void){
+ let api = "/bot/bot/list/1?deviceId=\(deviceID)"
+ RSApi.request(api: api, params: [:], method: .get) { (model:RSRoleModelRoot,res) in
+ completeHandler(model)
+ }
+ }
+ public func requestVoiceList(completeHandler: @escaping (_ result:RSVoiceModelRoot)->Void){
+ let api = "/bot/bot-voice/list?category=&platform=volcengine"
+ RSApi.request(api: api, params: [:], method: .get) { (model:RSVoiceModelRoot,res) in
+ completeHandler(model)
+ }
+ }
+ public func updateRole(role:RSRoleModel,completeHandler: @escaping (_ result:ResponseBaseModel)->Void){
+
+ // 获取当前存储的所有cookies
+ let cookies = HTTPCookieStorage.shared.cookies
+ for cookie in cookies ?? [] {
+ print("Stored Cookie - Name: \(cookie.name), Value: \(cookie.value)")
+ }
+
+
+ let api = "/dev/device/dev-bot?deviceId=\(deviceID)&botId=\(role.id)&speakerId=\(role.speakerId)"
+ RSApi.request(api: api, params: [:], method: .put) { (model:ResponseBaseModel,res) in
+ completeHandler(model)
+ }
+ }
}
class MSHttpManager{
let recordMamager = AiMouseRecordManager()
var dataList:[RSChatCellModel] = []
+ var selectedModel:RSRoleModel?
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
override func setupViews() {
navBar.leftView.isHidden = true
navBar.title = "AI"
+ navBar.rightView.setTitle("切换角色", for: .normal)
+ navBar.rightView.isHidden = false
+// navBar.rightView.setImage(UIImage(named: "feedback_history"), for: .normal)
tableView.backgroundColor = .clear
tableView.delegate = self
tableView.scrollToRow(at: IndexPath(row: dataList.count - 1, section: 0), at: .none, animated: false)
}
}
+ override func navRightViewTapped() {
+ let vc = RSRoleVC()
+ vc.selectedModel = selectedModel
+ vc.didSelected = {[weak self] model in
+ self?.selectedModel = model
+ }
+ App.shared.goToPage(vc)
+ }
}
extension RSChatVC:RSRecordBarDelegate{
var chat:MSVoiceChat = MSVoiceChat()
func start(){
+// RSAuth.shared.requestRoleList { token, error in
+//
+// }
if let password = UserDefaults.standard.string(forKey: deviceId){
startLogin(password: password)
}
//50021是账号或密码错误.可以在这里调用注册,重新注册后,之前的密码会被覆盖,APP更新保存的新密码
self?.registerAction()
}
+// self?.registerAction()
}
else{
Logger.debug(token ?? "")
--- /dev/null
+//
+// RSRoleVC.swift
+// ShenYin
+//
+// Created by liangshi on 2026/1/30.
+//
+
+import UIKit
+class RSRoleVC: BaseVC{
+ let tableView = UITableView(frame: .zero, style: .plain)
+ var list:[RSRoleModel] = []
+ var didSelected:((RSRoleModel)->Void)?
+ var selectedModel:RSRoleModel?
+
+ override func viewDidLoad() {
+ super.viewDidLoad()
+ // Do any additional setup after loading the view.
+ }
+ override func setupViews() {
+ self.navBar.title = "音色".l10n
+
+ tableView.do { v in
+ v.backgroundColor = .color(.bgColor)
+ v.delegate = self
+ v.dataSource = self
+ v.rowHeight = 52
+ v.separatorStyle = .none
+ v.register(SelectCell.self, forCellReuseIdentifier: SelectCell.reuseID)
+ }
+ view.addSubviews([tableView])
+
+ }
+ override func setupLayout() {
+ tableView.snp.makeConstraints { make in
+ make.left.right.bottom.equalToSuperview()
+ make.top.equalTo(kNavBtm)
+ }
+ }
+ override func setupData() {
+ RSAuth.shared.requestRoleList {[weak self] model in
+ guard let self = self else{return}
+ if model.isSuccess{
+ list = model.data.list
+ tableView.reloadData()
+ }
+ }
+ }
+}
+
+extension RSRoleVC:UITableViewDelegate,UITableViewDataSource {
+ func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
+ return list.count
+ }
+
+ func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
+ let cell = SelectCell.cell(withTableView: tableView, indexPath: indexPath)
+ cell.titleLabel.text = list[indexPath.row].name
+ let rect = CGRect(x: 0, y: 0, width: kScreenWid - 36, height: 52)
+ if indexPath.row == 0 {
+ cell.bgView.yc.setCorners(withRoundedRect: rect, roundingCorners: [.topLeft,.topRight], cornerRadii: CGSize(width: 8, height: 8))
+ }
+ else if indexPath.row == list.count - 1{
+ cell.bgView.yc.setCorners(withRoundedRect: rect, roundingCorners: [.bottomLeft,.bottomRight], cornerRadii: CGSize(width: 8, height: 8))
+ }
+ else{
+ cell.bgView.yc.setCorners(withRoundedRect: rect, roundingCorners: [.allCorners], cornerRadii: CGSize(width: 0, height: 0))
+ }
+ if let model = selectedModel{
+ cell.selectedIcon.isHidden = model.id != list[indexPath.row].id
+ }
+ else{
+ cell.selectedIcon.isHidden = true
+ }
+ return cell
+ }
+ func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
+ let data = list[indexPath.row]
+ let vc = RSSpeakerVC()
+ vc.role = data
+ App.shared.goToPage(vc)
+
+// let data = list[indexPath.row]
+// YLprogressHUD.showLoading()
+// RSAuth.shared.updateRole(role: data) {[weak self] result in
+// if result.isSuccess{
+// YLprogressHUD.showSuccess { [weak self] in
+// guard let self = self else {return}
+// selectedModel = data
+// didSelected?(data)
+// tableView.reloadData()
+// navigationController?.popViewController(animated: true)
+// }
+// }
+// else{
+// YLprogressHUD.showAutoHiden(result.msg)
+// }
+// }
+ }
+}
--- /dev/null
+//
+// RSSpeakerVC.swift
+// ShenYin
+//
+// Created by liangshi on 2026/1/31.
+//
+
+import UIKit
+class RSSpeakerVC: BaseVC{
+ let tableView = UITableView(frame: .zero, style: .plain)
+ var list:[RSVoiceModel] = []
+ var didSelected:((RSVoiceModel)->Void)?
+ var selectedModel:RSVoiceModel?
+ var role:RSRoleModel!
+
+ override func viewDidLoad() {
+ super.viewDidLoad()
+ // Do any additional setup after loading the view.
+ }
+ override func setupViews() {
+ self.navBar.title = "音色".l10n
+
+ tableView.do { v in
+ v.backgroundColor = .color(.bgColor)
+ v.delegate = self
+ v.dataSource = self
+ v.rowHeight = 52
+ v.separatorStyle = .none
+ v.register(SelectCell.self, forCellReuseIdentifier: SelectCell.reuseID)
+ }
+ view.addSubviews([tableView])
+
+ }
+ override func setupLayout() {
+ tableView.snp.makeConstraints { make in
+ make.left.right.bottom.equalToSuperview()
+ make.top.equalTo(kNavBtm)
+ }
+ }
+ override func setupData() {
+ RSAuth.shared.requestVoiceList {[weak self] model in
+ guard let self = self else{return}
+ if model.isSuccess{
+ list = model.data
+ tableView.reloadData()
+ }
+ }
+ }
+}
+
+extension RSSpeakerVC:UITableViewDelegate,UITableViewDataSource {
+ func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
+ return list.count
+ }
+
+ func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
+ let cell = SelectCell.cell(withTableView: tableView, indexPath: indexPath)
+ cell.titleLabel.text = list[indexPath.row].name
+ let rect = CGRect(x: 0, y: 0, width: kScreenWid - 36, height: 52)
+ if indexPath.row == 0 {
+ cell.bgView.yc.setCorners(withRoundedRect: rect, roundingCorners: [.topLeft,.topRight], cornerRadii: CGSize(width: 8, height: 8))
+ }
+ else if indexPath.row == list.count - 1{
+ cell.bgView.yc.setCorners(withRoundedRect: rect, roundingCorners: [.bottomLeft,.bottomRight], cornerRadii: CGSize(width: 8, height: 8))
+ }
+ else{
+ cell.bgView.yc.setCorners(withRoundedRect: rect, roundingCorners: [.allCorners], cornerRadii: CGSize(width: 0, height: 0))
+ }
+ if let model = selectedModel{
+ cell.selectedIcon.isHidden = model.id != list[indexPath.row].id
+ }
+ else{
+ cell.selectedIcon.isHidden = true
+ }
+ return cell
+ }
+ func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
+ let data = list[indexPath.row]
+ role.speakerId = data.voiceId
+
+ YLprogressHUD.showLoading()
+ RSAuth.shared.updateRole(role: role) {[weak self] result in
+ if result.isSuccess{
+ YLprogressHUD.showSuccess { [weak self] in
+ guard let self = self else {return}
+ selectedModel = data
+ didSelected?(data)
+ tableView.reloadData()
+ navigationController?.popViewController(animated: true)
+ }
+ }
+ else{
+ YLprogressHUD.showAutoHiden(result.msg)
+ }
+ }
+ }
+}
--- /dev/null
+//
+// RSVoiceModel.swift
+// ShenYin
+//
+// Created by liangshi on 2026/1/30.
+//
+
+import HandyJSON
+
+class RSVoiceModelRoot:ResponseBaseModel {
+ var data = [RSVoiceModel]()
+}
+
+class RSVoiceModel:HandyJSON {
+ var id = ""
+ var name = ""
+ var voiceId = ""
+ var gender = ""
+ var platform = ""
+ var category = ""
+ var vip = ""
+ var language = ""
+ var avatar = ""
+ var trialUrl = ""
+
+ required public init() { }
+}
+
+class RSRoleModelRoot:ResponseBaseModel {
+ var data = RSRoleModelData()
+}
+class RSRoleModelData:HandyJSON {
+ var list = [RSRoleModel]()
+
+ required public init() { }
+}
+class RSRoleModel:HandyJSON {
+ var id = ""
+ var name = ""
+ var speakerId = ""
+ var voiceName = ""
+ var cover = ""
+ var description = ""
+
+ required public init() { }
+}